Requests
requests 的底层实现其实就是 urllib
Requests 继承了urllib的所有特性。Requests支持HTTP连接保持和连接池,支持使用cookie保持会话,支持文件上传,支持自动确定响应内容的编码,支持国际化的 URL 和 POST 数据自动编码。
开源地址:https://github.com/kennethreitz/requests
中文文档 API: http://docs.python-requests.org/zh_CN/latest/index.html
安装方式
$ pip install requests
基本GET请求(headers参数 和 parmas参数)
import requests
kw = {'wd':'长城'}
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()
response = requests.get("http://www.baidu.com/s?", params = kw, headers = headers)
# 查看响应内容,response.text 返回的是Unicode格式的数据
print (response.text)
# 查看响应内容,response.content返回的字节流数据
print (respones.content)
# 查看完整url地址
print (response.url)
# 查看响应头部字符编码
print (response.encoding)
# 查看响应码
print (response.status_code)
-
使用response.text 时,Requests 会基于 HTTP 响应的文本编码自动解码响应内容,大多数 Unicode 字符集都能被无缝地解码。
-
使用response.content 时,返回的是服务器响应数据的原始二进制字节流,可以用来保存图片等二进制文件。
-
当收到一个响应时,Requests 会猜测响应的编码方式,用于在你调用response.text 方法时对响应进行解码。Requests 首先在 HTTP 头部检测是否存在指定的编码方式,如果不存在,则会使用 chardet.detect来尝试猜测编码方式(存在误差)
-
更推荐使用response.content.deocde()
基本POST请求(data参数)
1.传入data数据
import requests
formdata = {
"type":"AUTO",
"i":"i love python"
}
url = "****l"
headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"}
response = requests.post(url, data = formdata, headers = headers)
print (response.text)
# 如果是json文件可以直接显示
print (response.json())
json.loads();字符串转json对象。
json.dumps(),队长转json
代理(proxies参数)
import requests
# 根据协议类型,选择不同的代理
proxies = {
"http": "http://12.34.56.79:9527",
"https": "http://12.34.56.79:9527",
}
response = requests.get("http://www.baidu.com", proxies = proxies)
print response.text
Cookies 和 Sission
- Cookies
如果一个响应中包含了cookie,那么我们可以利用 cookies参数拿到:
cookiejar = response.cookies
//<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
# 8\. 将CookieJar转为字典:
cookiedict = requests.utils.dict_from_cookiejar(cookiejar)
//{'BDORZ': '27315'}
- Sission
ssion = requests.session()
ssion.post()
寻找登录的post地址
- 在form表单中寻找action对应的url地址
- post的数据是input标签中name的值作为键,真正的用户名密码作 为值的字典,post的url地址就是action对应的url地址
- 抓包,寻找登录的url地址
- 勾选perserve log按钮,防止页面跳转找不到url
- 寻找post数据,确定参数
- 参数不会变,直接用,比如密码不是动态加密的时候
- 参数会变
- 参数在当前的响应中
- 通过js生成
- 定位想要的js
- 选择会触发js时间的按钮,点击event listener,找到js的位置
- 通过chrome中的search all file来搜索url中关键字
- 添加断点的方式来查看js的操作,通过python来进行同样的操作
处理HTTPS请求 SSL证书验证
r = requests.get("https://www.12306.cn/mormhweb/", verify = False)
请求重试
from retrying import retry
@retry(stop_max_attempt_number=3)
def method():
数据提取
- json模块如何使用。
- 字符串和python类型
- json.loads(json_str)
- json.dumps(python类型,ensure_ascii=False,indent=2)
- 类文件对象中的数据和python类型的转化
- json.load(fp)[fp是类文件对象]
- json.dump(obj,fp,ensure_ascii=False,indent=2)
Python 的 re 模块(正则提取)
正则表达式:就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑。
(正则基础知识https://blog.csdn.net/yucaixiang/article/details/103434951)
- 常用正则表达式的方法:
re.compile(编译)
pattern.match(从头找一个)
pattern.search(找一个)
pattern.findall(找所有)
pattern.sub(替换)
split 方法 - 注意:贪婪模式与非贪婪模式
- 贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配 ( * );
- 非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配 ( ? );
- Python里数量词默认是贪婪的。
- python中原始字符串r的用法
- 原始字符串定义(raw string):所有的字符串都是直接按照字面的意思来使用,没有转义特殊或不能打印的字符,原始字符串往往针对特殊字符而言
- 原始字符串r,待匹配字符串中有反斜杠的时候,使用r能够忽视反斜杠带来的转义的效果
r'hello world'
- findall 和分组()
re.findall(r“a.bc”,”a\nbc”,re.DOTALL) 和
re.findall(r“a(.)bc”,”a\nbc”,re.DOTALL)的区别?
不分组时匹配的是全部,分组后匹配的是组内的内容
点号默认情况匹配不到\n
\s能够匹配空白字符,不仅仅包含空格,还有\t|\r\n - 返回html取值范例
re.findall(r"<h1 class=\"title\">.*?<p>(.*?)</p>",html_str,re.S)
这里需要注意一个是re.S是正则表达式中匹配的一个参数。
如果 没有re.S 则是 只匹配一行 有没有符合规则的字符串,如果没有则下一行重新匹配。
如果 加上re.S 则是将 所有的字符串 将一个整体进行匹配,findall 将所有匹配到的结果封装到一个list中。
XPATH和LXML类库提取数据
XPath
XPath (XML Path Language) 是一门在 HTML\XML 文档中查找信息的语言,可用来在 HTML\XML 文档中对元素和属性进行遍历
。
- XPath 开发工具
- 开源的XPath表达式编辑工具:XMLQuire(XML格式文件可用)
- Chrome插件 XPath Helper
- Firefox插件 XPath Checker
- 选取节点
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
… | 选取当前节点的父节点。 |
@ | 选取属性。 |
- 谓语(Predicates)
- 选取若干路径
- xpath学习重点
- 使用xpath helper或者是chrome中的copy xpath都是从element中提取的数据,但是爬虫获取的是url对应的响应,往往和elements不一样
- 获取文本
- a/text() 获取a下的文本
- a//text() 获取a下的所有标签的文本
- //a[text()=‘下一页’] 选择文本为下一页三个字的a标签
- @符号
- a/@href
- //ul[@id=“detail-list”]
- //
- 在xpath最前面表示从当前html中任意位置开始选择
- li//a 表示的是li下任何一个标签
lxml库
lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。
lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用之前学习的XPath语法,来快速的定位特定元素以及节点信息。
需要安装C语言库,可使用 pip 安装:pip install lxml (或通过wheel方式安装)
- 解析 HTML 代码
`from lxml import etree
#利用etree.HTML,将字符串解析为HTML文档
html = etree.HTML(text)`
# 按字符串序列化HTML文档
result = etree.tostring(html)
- lxml 可以自动修正 html 代码,例子里不仅补全了 li 标签,还添加了 body,html 标签
- lxml 能够接受bytes和str的字符串
- 文件读取
- etree.parse() 方法来读取文件。
html = etree.parse('./hello.html')
- 提取页面数据的思路
- 先分组,渠道一个包含分组标签的列表
- 遍历,取其中每一组进行数据的提取,不会造成数据的对应错乱
实现爬虫的套路
-
准备url
- 准备start_url
- url地址规律不明显,总数不确定
- 通过代码提取下一页的url
- xpath
- 寻找url地址,部分参数在当前的响应中(比如,当前页码数和总的页码数在当前的响应中)
- 准备url_list
- 页码总数明确
- url地址规律明显
- 准备start_url
-
发送请求,获取响应
- 添加随机的User-Agent,反反爬虫
- 添加随机的代理ip,反反爬虫
- 在对方判断出我们是爬虫之后,应该添加更多的headers字段,包括cookie
- cookie的处理可以使用session来解决
- 准备一堆能用的cookie,组成cookie池
- 如果不登录
- 准备刚开始能够成功请求对方网站的cookie,即接收对方网站设置在response的cookie
- 下一次请求的时候,使用之前的列表中的cookie来请求
- 如果登录
- 准备多个账号
- 使用程序获取每个账号的cookie
- 之后请求登录之后才能访问的网站随机的选择cookie
- 如果不登录
-
提取数据
- 确定数据的位置
- 如果数据在当前的url地址中
- 提取的是列表页的数据
- 直接请求列表页的url地址,不用进入详情页
- 提取的是详情页的数据
-
- 确定url
-
- 发送请求
-
- 提取数据
-
- 返回
-
- 提取的是列表页的数据
- 如果数据不在当前的url地址中
- 在其他的响应中,寻找数据的位置
-
- 从network中从上往下找
-
- 使用chrome中的过滤条件,选择出了js,css,img之外的按钮
-
- 使用chrome的search all file,搜索数字和英文
-
- 在其他的响应中,寻找数据的位置
- 如果数据在当前的url地址中
- 数据的提取
- xpath,从html中提取整块的数据,先分组,之后每一组再提取
- re,提取max_time,price,html中的json字符串
- json
- 确定数据的位置
-
保存
- 保存在本地,text,json,csv
- 保存在数据库
多线程爬虫
实现准备url,请求响应,解析数据,保存数据,这些步骤的解耦,多线程执行。
Queue(队列对象)
Queue是python中的标准库,可以直接import Queue引用;队列是线程间最常用的交换数据的形式
对于资源,加锁是个重要的环节。因为python原生的list,dict等,都是not thread safe的。而Queue,是线程安全的,因此在满足使用条件下,建议使用队列
-
初始化: class Queue.Queue(maxsize) FIFO 先进先出
-
包中的常用方法:
- Queue.qsize() 返回队列的大小
- Queue.empty() 如果队列为空,返回True,反之False
- Queue.full() 如果队列满了,返回True,反之False
- Queue.full 与 maxsize 大小对应
- Queue.get([block[, timeout]])获取队列,timeout等待时间
- 创建一个“队列”对象
- import Queue
myqueue = Queue.Queue(maxsize = 10)
- 将一个值放入队列中
myqueue.put(10)
- 将一个值从队列中取出
myqueue.get()
代码案例
# coding=utf-8
import requests
from lxml import etree
import threading
from queue import Queue
class QiubaiSpdier:
def __init__(self):
self.url_temp = "https://www.qiushibaike.com/8hr/page/{}/"
self.headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36"}
self.url_queue = Queue()
self.html_queue = Queue()
self.content_queue = Queue()
def get_url_list(self):
# return [self.url_temp.format(i) for i in range(1,14)]
for i in range(1,4):
self.url_queue.put(self.url_temp.format(i))
def parse_url(self):
while True:
url = self.url_queue.get()
print(url)
response = requests.get(url,headers=self.headers)
self.html_queue.put(response.content.decode())
self.url_queue.task_done()
def get_content_list(self): #提取数据
while True:
html_str = self.html_queue.get()
html = etree.HTML(html_str)
div_list = html.xpath("//div[@id='content-left']/div") #分组
content_list = []
for div in div_list:
item= {}
item["content"] = div.xpath(".//div[@class='content']/span/text()")
item["content"] = [i.replace("\n","") for i in item["content"]]
item["author_gender"] = div.xpath(".//div[contains(@class,'articleGender')]/@class")
item["author_gender"] = item["author_gender"][0].split(" ")[-1].replace("Icon","") if len(item["author_gender"])>0 else None
item["auhtor_age"] = div.xpath(".//div[contains(@class,'articleGender')]/text()")
item["auhtor_age"] = item["auhtor_age"][0] if len(item["auhtor_age"])>0 else None
item["content_img"] = div.xpath(".//div[@class='thumb']/a/img/@src")
item["content_img"] = "https:"+item["content_img"][0] if len(item["content_img"])>0 else None
item["author_img"] = div.xpath(".//div[@class='author clearfix']//img/@src")
item["author_img"] = "https:"+item["author_img"][0] if len(item["author_img"])>0 else None
item["stats_vote"] = div.xpath(".//span[@class='stats-vote']/i/text()")
item["stats_vote"] = item["stats_vote"][0] if len(item["stats_vote"])>0 else None
content_list.append(item)
self.content_queue.put(content_list)
self.html_queue.task_done()
def save_content_list(self): #保存
while True:
content_list = self.content_queue.get()
for i in content_list:
# print(i)
pass
self.content_queue.task_done()
def run(self): #实现主要逻辑
thread_list = []
#1.url_list
t_url = threading.Thread(target=self.get_url_list)
thread_list.append(t_url)
#2.遍历,发送请求,获取响应
for i in range(20):
t_parse = threading.Thread(target=self.parse_url)
thread_list.append(t_parse)
#3.提取数据
for i in range(2):
t_html = threading.Thread(target=self.get_content_list)
thread_list.append(t_html)
#4.保存
t_save = threading.Thread(target=self.save_content_list)
thread_list.append(t_save)
for t in thread_list:
t.setDaemon(True) #把子线程设置为守护线程,该线程不重要主线程结束,子线程结束
t.start()
for q in [self.url_queue,self.html_queue,self.content_queue]:
q.join() #让主线程等待阻塞,等待队列的任务完成之后再完成
print("主线程结束")
if __name__ == '__main__':
qiubai = QiubaiSpdier()
qiubai.run()
Selenium和PhantomJS
Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动操作,不同是Selenium 可以直接运行在浏览器上,它支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器)
基本使用
- 加载网页:
from selenium import webdriver
driver = webdriver.PhantomJS(“c:…/pantomjs.exe”)
driver.get(“http://www.baidu.com/”)
driver.save_screenshot(“长城.png”) - 定位和操作:
driver.find_element_by_id(“kw”).send_keys(“长城”)
driver.find_element_by_id(“su”).click() - 查看请求信息:
driver.page_source
driver.get_cookies()
driver.current_url - 退出
driver.close() #退出当前页面
driver.quit() #退出浏览器 - Cookie相关用法:
//获取每个cookie
{cookie[‘name’]: cookie[‘value’] for cookie in driver.get_cookies()}
driver.delete_cookie(“CookieName”)
driver.delete_all_cookies()
定位UI元素方法 (WebElements)
find_element_by_id
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
selenium使用的注意点
- 获取文本和获取属性
先定位到元素,然后调用.text或者get_attribute方法来去 - selenium获取的页面数据是浏览器中elements的内容
- find_element和find_elements的区别
find_element返回一个element,如果没有会报错
find_elements返回一个列表,没有就是空列表
在判断是否有下一页的时候,使用find_elements来根据结果的列表长度来判断 - 如果页面中含有iframe、frame,需要先调用driver.switch_to.frame的方法切换到frame中才能定位元素
- selenium请求第一页的时候回等待页面加载完了之后在获取数据,但是在点击翻页之后,hi直接获取数据,此时可能会报错,因为数据还没有加载出来,需要time.sleep(3)
- selenium中find_element_by_class_name智能接收一个class对应的一个值,不能传入多个
PhantomJS
PhantomJS 是一个基于Webkit的“无界面”(headless)浏览器,它会把网站加载到内存并执行页面上的 JavaScript,因为不会展示图形界面,所以运行起来比完整的浏览器要高效。
chromedirver下载地址:https://npm.taobao.org/mirrors/chromedriver
phantomjs下载地址:http://phantomjs.org/download.html
验证码的识别
-
url不变,验证码不变
- 请求验证码的地址,获得相应,识别 -
url不变,验证码会变
- 思路:对方服务器返回验证码的时候,会和每个用户的信息和验证码进行一个对应,之后,在用户发送post请求的时候,会对比post请求中法的验证码和当前用户真正的存储在服务器端的验证码是否相同
1.实例化session
2.使用seesion请求登录页面,获取验证码的地址
3.使用session请求验证码,识别
4.使用session发送post请求’ -
使用selenium登录,遇到验证码
- url不变,验证码不变,同上
- url不变,验证码会变
selenium请求登录页面,同时拿到验证码的地址
获取登录页面中driver中的cookie,交给requests模块发送验证码的请求,识别
输入验证码,点击登录
鼠标动作链
有些时候,我们需要再页面上模拟一些鼠标操作,比如双击、右击、拖拽甚至按住不动等,我们可以通过导入 ActionChains 类来做到:
#导入 ActionChains 类
from selenium.webdriver import ActionChains
# 鼠标移动到 ac 位置
ac = driver.find_element_by_xpath('element')
ActionChains(driver).move_to_element(ac).perform()
# 在 ac 位置单击
ac = driver.find_element_by_xpath("elementA")
ActionChains(driver).move_to_element(ac).click(ac).perform()
# 在 ac 位置双击
ac = driver.find_element_by_xpath("elementB")
ActionChains(driver).move_to_element(ac).double_click(ac).perform()
# 在 ac 位置右击
ac = driver.find_element_by_xpath("elementC")
ActionChains(driver).move_to_element(ac).context_click(ac).perform()
# 在 ac 位置左键单击hold住
ac = driver.find_element_by_xpath('elementF')
ActionChains(driver).move_to_element(ac).click_and_hold(ac).perform()
# 将 ac1 拖拽到 ac2 位置
ac1 = driver.find_element_by_xpath('elementD')
ac2 = driver.find_element_by_xpath('elementE')
ActionChains(driver).drag_and_drop(ac1, ac2).perform()
填充表单
我们已经知道了怎样向文本框中输入文字,但是有时候我们会碰到<select> </select>标签的下拉框。直接点击下拉框中的选项不一定可行。
<select id="status" class="form-control valid" onchange="" name="status">
<option value=""></option>
<option value="0">未审核</option>
<option value="1">初审通过</option>
<option value="2">复审通过</option>
<option value="3">审核不通过</option>
</select
Selenium专门提供了Select类来处理下拉框。 其实 WebDriver 中提供了一个叫 Select 的方法,可以帮助我们完成这些事情:
# 导入 Select 类
from selenium.webdriver.support.ui import Select
# 找到 name 的选项卡
select = Select(driver.find_element_by_name('status'))
#
select.select_by_index(1)
select.select_by_value("0")
select.select_by_visible_text(u"未审核")
页面切换
个浏览器肯定会有很多窗口,所以我们肯定要有方法来实现窗口的切换。切换窗口的方法如下:
driver.switch_to.window("this is window name")
也可以使用 window_handles 方法来获取每个窗口的操作对象。例如:
for handle in driver.window_handles:
driver.switch_to_window(handle)
页面前进和后退
driver.forward() #前进
driver.back() # 后退
Tesseract
Tesseract是一个将图像翻译成文字的OCR库(光学文字识别,Optical Character Recognition)
- 安装Tesseract
Windows 系统
下载可执行安装文件https://code.google.com/p/tesseract-ocr/downloads/list安装。
Linux 系统
可以通过 apt-get 安装: $sudo apt-get tesseract-ocr
Mac OS X系统
用 Homebrew(http://brew.sh/)等第三方库可以很方便地安装 brew install tesseract - 在python中调用Tesseract
pip install pytesseract
使用:
在python代码中
import pytesseract
from PIL import Image
image = Image.open(jpg)
pytesseract.image_to_string(image