目录
(2)发送请求:
前言
python爬虫(进阶):https://blog.csdn.net/qwerdftgu/article/details/120136508
提示:以下是本篇文章正文内容,下面案例可供参考
一、爬虫基础
1.爬虫流程:
- url -----> 发起请求 ------->获取响应 ------->解析响应 -------->保存数据(文件、数据库)
2.htttp/https协议复习:
- 区别:https比http更安全,但是性能更低!
- HTTP:超文本传输协议,默认端口号80
-
HTTPS:HTTP + SSL(安全套接字层),默认端口号443,SSL对传输的内容(也就是请求体或响应体)进行加密!!
3.爬虫特别关注的请求头和响应头:
(1)请求头:后3个最重要
- host : 域名
- Connection : keep-alive(长连接)--->每次TCP连接/释放都需要三次/四次握手,我们让它一直处于连接状态,就只需要一次连接和释放!!!
- Upgrade-Insecure-Requests: 升级为 https请求
- User-Agent : 用户代理,表明自己的身份(通常通过这个模仿浏览器访问服务器,羊皮)
- Referer: 页面跳转处(从那个页面跳转过来的),有些网站可能用这个来检测是不是爬虫;防盗链(图片/视频)----->你直接复制这个图片的链接到其他地方时,图片将显示不了!
- Cookie: 状态保持,通常用于判断用户和其权限!像有些推送就是通过识别Cookie来进行的。
(2)响应头:
- 只关注Set-Cookie : 对方服务器设置cookie到用户浏览器的缓存!!!
(3)常见的响应码:
- 200:成功 302:跳转 403:资源不可用 404:找不到该页面
- 503:服务器由于维护或者负载过重未能响应,有可能是因为爬虫频繁访问url,使服务器忽视爬虫请求,最终返回503响应码。
(4)浏览器请求过程与爬虫的区别:
-
浏览器:发送所有请求,进行渲染! 因为返回的HTML静态文件中会引用其他数据,这些数据又是一个个url!
-
爬虫:只发送指定请求,不会渲染!
-
三部分:骨骼(HTML静态文件) ------> 肌肉(js/ajax请求) -------> 皮肤(css/font/图片等)
4.requests模块
(1)基础:
- response.text 和 response.content的区别:
requests.text: 类型:str 解码类型:requests模块------>推测!
requests.content: 类型:bytes 解码类型:没有指定
2.response.content进行decode,来解决中文乱码:
response.content.decode() 默认utf-8
response.content.decode(‘GBK’)
3.response响应对象的其它常用属性或方法:
response.url # 响应的url,有时候响应的url和请求的url不相同
response.staus_code # 响应状态码
response.requests.header # 响应对应的请求头
response.header # 响应头
response.request._cookies # 响应对应请求的cookies;返回cookiesJar类型
response.cookies # 响应的cookies;经过了set-cookies动作;
response.json() # 自动将json字符串类型的响应内容转换为python对象(dict or list)
(2)发送请求:
- 发送带header的请求: 当我们以爬虫的身份去请求和加上header模拟浏览器去请求,获得的数据可能是不相同的!!!!
requests.get(url, header={})
- 通过params携带参数字典
requests.get(url, header=header, params=kw)
- 在header中携带cookie:cookies的目的是状态保持和用户鉴别!
- cookies参数的使用:
# 将cookies字符串转换为cookies参数所需的字典:
cookies_dict = { cookies.split('=')[0] : cookies.split('=')[-1] for cookie in cookies_str.split(';')}
requests.get(url, header=header, cookies=cookies_dict)
# cookieJar对象转换为cookies字典:
dict_cookies = requests.utils.dict_from_cookiejar(response.cookies)
- 注意:cookie一般是有过期时间的,一旦过期需要重新获取!!
(3)超时参数timeout的使用:
在爬虫中,一个请求很久没有结果,就会让整个项目的效率变得非常低,这个时候我们就需要对请求进行强制要求,让他必须在特定的时间内返回结果,否则就报错。、
# 超时参数timeout的使用
response = requests.get(url , timeout=3) # 单位是秒,发送请求后,3秒内返回响应,否则就抛出异常!
(4)代理proxies的使用:
- 含义:
代理ip是一个ip,指向的是一个代理服务器,代理服务器能够帮我们向目标服务器发送请求,起中转作用;
分为:正向代理和反向代理 - 分类:
透明
匿名
高匿 ---->让别根本无法发现你是代理ip,是我们使用爬虫最好的选择!!!
(5)使用verify参数忽略CA证书
因为一些网站上的CA证书是未认证的,而我们在发送请求时,requests会默认对其CA证书进行对比认证,此时可能会报错,请求不成功!只需要忽略其CA证书即可!
response = requests.get(url, verify=False) # 一般默认是True!
(6)发送POST请求:
- 实现方法:
requests.post(url, data)
# data是一个字典
- post数据来源!!!
1.固定值 抓包比较不变值
2.输入值 抓包比较根据自身变化的值
3.预设值 {
* 静态文件 需要提前从HTML中获取
* 发请求 需要提前对指定地址发送请求获取
}
4.在客户端生成的------> 最难!!!!
-
实例:实现爬虫模拟金山翻译:(未做完)
import requests class King(object): def __init__(self, word): self.url = 'https://ifanyi.iciba.com/index.php?' self.headers = { 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Mobile Safari/537.36 Edg/92.0.902.55' } self.word = word self.data = { 'from': 'en', 'to': 'zh', 'q': self.word } def get_data(self): response = requests.post(self.url, headers=self.headers, data=self.data) return response.content def run(self): response = self.get_data() print(response) if __name__ == '__main__': king = King('Chain') king.run()
(7)利用requests.session进行状态保持
requests模块中的Session类能够自动处理发送请求获取响应过程中产生的cookie,进而达到状态保持的目的。
- requests.session的作用:
自动处理cookie,即下一次会带上前一次的cookkie - 应用场景:
自动处理连续多次请求过程中产生的cookie
requests.session使用方法:
session = requests.session() # 实例化session对象
response = session.get(url, header, ..)
response = session.post(url, data, ...)
5.数据提取:
(1)概要:
- 结构化:信息能够用数据或统一的结构加以表示!
json数据(高频出现):
json模块
re模块 //处理
jsonpath模块(json模块的一些补充)
xml数据(低频出现):
re模块 --->正则表达式
lxml模块 --->xpath语法
- 非结构化:无法用数字或统一的结构表示,如文本、图像、声音、网页等信息
html:
re模块 **重点**
lxml模块 **重点**
beautifulsoup{
-正则表达式
-xpath
-css选择器
pyquery ---->css选择器
- xml与html
xml:一种可扩展标记语言,样子和html很像,功能更专注于对传输和存储数据,它与html一点不同是:xml的标签可以自己定义
区别:
数据格式 描述 设计目的 XML 可扩展标记语言 被设计为传输和存储数据,其焦点是数据内容 HTML 超文本标记语言 显示数据以及然后更好显示数据
(2)jsonpath模块:
1.场景:
多层次嵌套的复杂字典直接提取数据
2.安装:
pip install jsonpath
3.使用:
from jsonpath import jsonpath
ret = jsonpath(a, 'jsonpath语法规则字符串')
jsonpath和json一般一起使用!!!
4.语法:
$ 根节点(最外层的大括号)
. 子节点
.. 内部任意位置,子孙节点
其它语法及示例:链接from jsonpath import jsonpath data = {'key1':{'key2':{'key3':{'key4':'python'}}}} # 注意:jsonpath的结果为列表,获取数据需要索引!! print(jsonpath(data, '$..key4')[0]) # 与上面的无关:获取根节点下所有key为name的值: list = jsonpath(data, '$..name')
(3) lxml模块:
-
处理html/xml格式内容时需要用到lxml模块和xpath语法:
-
xpath helper的安装与使用:
-
1.安装:
是Google浏览器的插件,需要到Google应用商店下载,或者去网上找方法下载
2.作用:
对当前页面测试xpath语法规则,可以直接在网页上运行xpath语句,更直观快捷。
-
-
xpath语法:
下面所说的元素,标签和节点都是指同样的东西!!!
1.基础节点选择语法:
表达式 描述 nodename 选中该元素 / 从根节点选取、或者是元素和元素间的过滤,相对于整个文本 // 从匹配选择的当前节点选择文档中的节点,而不考虑他们的位置 . 选择当前节点 .. 选取当前节点的父节点 @ 选取属性 text() 选取文本 eg:
//title/text() 从开闭标签中选取文本内容
//link/@href 从选中的节点标签中获取指定属性的值2.节点修饰语法:选取特定节点
- 通过索引修饰节点:
/html/body/div[3]/div[last() - 1] 选择倒数第二个div
/html/body/div[position()>2] 范围选择- 通过属性值修饰节点:
//div[@id='content-let']/div/@id 出现在[]中的@是使用标签属性名和属性值修饰的节点,出现在结尾的/@是取属性值- 通过子节点的值修饰节点:
//span[i>2000] 选取span标签下的i标签的文本值大于2000的
//dic[span[2]>=9.4]- 通过包含修饰:
//div[contains(text(), '一页')] 选取所有div标签下文本中含有‘一页’字符的标签
//div[contains(@id, 'quisrfs')] 选取所有div标签下含有属性id且值为'quisrfs'的标签3.etree.tostring函数:
将elements对象转换为bytes类型的字符串
4.其它:
- 全部标签:
//*- 全部属性:
//@*- xpath复合语法: --> |
//h2/a|//td/a 或者的意思学习链接:xpath教程
- 实例:百度贴吧(获取每一页的每一个标题和文字内容)
import requests import re from lxml import etree class Tieba(object): def __init__(self, str): self.url = 'https://tieba.baidu.com/f?ie=utf-8&kw={}&fr=search'.format(str) self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36', 'Referer': 'https://tieba.baidu.com/f?ie=utf-8&kw=%E8%8B%B1%E9%9B%84%E8%81%94%E7%9B%9F&fr=search' } self.proxies = { "HTTP": "http://182.34.27.89:9999" } pass def get_data(self, url): response = requests.get(url, headers=self.headers, proxies=self.proxies) return response.content def parse_data(self, data): global count # 创建element对象 print(len(data)) # 一切以获取的源码为准,也就是data内容!!! data = data.decode().replace("<!--", "").replace("-->", "") with open('github1.html', 'w', encoding='utf8') as f: f.write(data) # etree.HTML()能够自动补全html缺失的标签 html = etree.HTML(data) # //li[@class='j_thread_list clearfix thread_item_box']/div/div[2]/div[1]/div[1]/a 标题 # //li[@class='j_thread_list clearfix thread_item_box']/div/div[2]/div[2]/div[1]/div/text() 内容 el_list = html.xpath('//li[@class=" j_thread_list clearfix thread_item_box"]/div/div[2]') with open('text.txt', 'a', encoding='utf8') as f: count += 1 f.write('\n\n' + ' papg-' + str(count) + '\n') for el in el_list: str1 = el.xpath("./div[1]/div[1]/a/text()")[0] str2 = el.xpath("./div[2]/div[1]/div/text()")[0] str3 = '标题: ' + str1.strip() + '\n' + '内容: ' + str2.strip() + '\n\n' # print(str3) f.write(str3) # 获取下一页连接: try: next_url = 'https:' + html.xpath("//a[@class='next pagination-item ']/@href")[0] except: next_url = None return next_url def run(self): # url # headers # 发送请求,获取响应 while(True): data = self.get_data(self.url) # 从响应中提取数据(数据和翻页的url) self.url = self.parse_data(data) if self.url == None: break # 判断是否终结 if __name__ == '__main__': global count count = 0 tieba = Tieba('英雄联盟') tieba.run()
6.Selenium:
下面主要介绍selenium自动化测试框架在爬虫中的应用,selenium能够大幅度降低爬虫的编写难度,但是也同样会降低爬虫的爬取速度,在逼不得已的情况下我们可以使用selenium进行爬虫的编写。
(1)selenium的介绍:
selenium是一个web的自动化测试工具,最初是为网站自动化测试而开发的,selenium可以直接调用浏览器,它支持所有的主流浏览器(包括PhantomJS等无界面浏览器),可以接受指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏等。
# 开发使用有头浏览器,部署使用无头浏览器
效果展示:先看后面,不然不会成功!!
1.有头浏览器:chome
from selenium import webdriver #如果driver没有添加到了环境变量,则需要将driver的绝对路径赋值给executable_path参数 # driver = webdriver.Chrome(executable_path='/home/worker/Desktop/driver/chromedriver') # 如果driver添加了环境变量则不需要设置executable_path driver = webdriver.Chrome() # 向一个url发起请求 driver.get("http://www .itcast.cn/") # 把网页保存为图片,69版本以上的谷歌浏览器将无法使用截图功能 # driver.save_screenshot('itcast.png') print(driver.title) # 打印页面的标题#退出模拟浏览器 driver.quit() #-定要退出!不退出会有残贸进程
2.无头浏览器:phantomJS
from selenium import webdriver # 指定driver的绝对路径 driver = webdriver.PhantomJS(executable_path='/home/worker/Desktop/driver/phantomjs') # driver = webdriver. Chrome(executable path=' /hame/worker/Desktop/driver/chromedriver.') # 向一个url发起请求 driver.get("http:/ /www.itcast.cn/") # 把网页保存为图片 driver.save_screenshot("itcast-png") # 退出模拟浏览器 driver.quit() # 一定要退出!不退出会有残贸进程!
# python代码能够自动的调用谷歌浏览或phantomjs无界面浏览器,控制其自动访问网站
(2)工作原理
(3)安装与配置:
1.安装:
- 安装模块:
pip/pip3 install selenium
- 安装driver:
获取浏览器版本:
根据版本下载驱动:
访问:https://npm.taobao.org/mirrors/chromedriver, 点击进入不同版本的Chromedriver下载页面
需要解压
配置:
windows环境下需要将chromedriver.exe所在的目录设置为path环境变量,注意:chromedriver.exe 要放在python安装路径下
linux/mac环境下,将chromedriver所在的目录设置到系统的PATH环境值中- 简单使用:
import time from selenium import webdriver # 通过指定chromedriver的路径来实例化driver对象,chromedriver放在当前目录。 # #driver = webdriver.Chrome(executable_path='./chromedriver ') # chromedriver已经添加环境变量 driver = webdriver.Chrome() #控制浏览器访问url地址 driver.get("https://www.baidu.com/") time.sleep(3) # 在百度搜索框中搜索" python' driver.find_element_by_id("kw").send_keys( 'python') #点击'百度搜索" driver.find_element_by_id('su').click() time.sleep(6) #退出浏览器 driver.quit()
(4)driver对象的常用属性和方法:
1. driver.page_source当前标签页浏览器渲染之后的网页源代码
2. driver .current_url当前标签页的url
3. driver.close()关闭当前标签页,如果只有一个标签页则关闭整个浏览器
4. driver.quit()关闭浏览器
5. driver.forward()页面前进
6. driver.back()页面后退
7.driver.save_screenshot('图片名') 页面截图
(5)driver对象定位标签元素获取标签对象的方法
元素定位:
- find_element_by_id(返回一个元素)
- find_element(s)_by_elass_nam(根据类名获取元素列表)
- find_element(s)_by_name(根据标签的name属性值返回包含标签对象元素的列表)
- find_element(s)_by_xpath(返回一个包含元素的列表)
- find_element(s)_by_link_text(根据连接文本获取元素列表)
- find_element(s)_by_partial_link_text(根据链接包含的文本获取元素列表)
- find_element(s)_by_tag_name(根据标签名获取元素列表)
目标元素在当前html中是唯一标签的时候或是众多定位出来的标签中的第一个的时候才能使用!!!- find_element(s)_by_css(根据css选择器来获取元素列表)
注意:
find_element_by_xxx
定位到则是一个对象
定位不到则报错
find_elements_by_xxx
定位到则是一个含有元素的列表
定位不到则是一个空列表
by_link_text和by_partial_link_text区别:
全部文本和保含某个文本代码:
import time from selenium import webdriver driver = webdriver.Chrome() url = 'http://www.baidu.com' driver.get(url) # 通过xpath进行元素定位 # driver.find_element_by_xpath('//*[@id="kw"]').send_keys ('python3') # 通过cs5选择器进行元素定位 # driver.find _element_by_css_selector('#kw').send_keys('python3') # 通过name属性值进行元素定位 # driver.find_element_by_name('wd').send_keys('python3') # 通过class属性值进行元素定位 driver.find_element_by_class_name('s_ipt').send_keys('python3') driver.find_element_by_id('su').click() # 通过链接文本进行元素定位: # driver.find_element_by_link_text('hao123').click() # driver.find_element_by_partial_link_text('hao').click() time.sleep(3) driver.quit()
元素操作:
- 获取文本 element.text
通过定位获取的标签对象的text属性,获取文本内容- 获取属性值 element.get_attribute('属性名')
通过定位获取的标签对象的get_attribute 函数,传入属性名来获取属性的值- 代码:
import time from selenium import webdriver driver = webdriver.Chrome() url = 'https://gy.58.com/chuzu/?PGTID=0d100000-007d-f3cf-c228-362d6bf643c5&ClickID=2' driver.get(url) el_list = driver.find_elements_by_xpath('/html/body/div[6]/div[2]/ul/li/div[2]/h2/a') for el in el_list: print(el.text, el.get_attribute('href')) # el.click()只支持可以点击操作的标签 # el.send_keys(data) el text input # el.clear() 对输入框做清空操作!! time.sleep(3) driver.quit()
(6)selenium的其它使用方法:
selenium标签页的切换:
- 获取所有标签页的窗口句柄
- 利用窗口句柄切换到句柄指向的标签页
这里的窗口句柄指:指向标签页对象的标识- 具体的方法:
1.获取当前所有的标签页的句柄构成的列表
current_windows = driver.window_handles
2.根据标签页句柄列表索引下标进行切换
driver.switch_to.window(current_windows[-1])
import time from selenium import webdriver driver = webdriver.Chrome() url = 'https://gy.58.com' driver.get(url) print(driver.current_url) print(driver.window_handles) # 定位并点击租房按钮: el = driver.find_element_by_xpath('/html/body/div[3]/div[1]/div[1]/div/div[1]/div[1]/span[1]/a') el.click() print(driver.current_url) print(driver.window_handles) # 可以知道,当出现页面跳转时,selenium不会自动使用跳转页面的句柄! driver.switch_to.window(driver.window_handles[-1]) el_list = driver.find_elements_by_xpath('/html/body/div[6]/div[2]/ul/li/div[2]/h2/a') print(len(el_list)) driver.quit()
switch_to切换frame标签:
iframe是html中常用的一种技术,即一个页面中嵌套了另一个网页,selenium默认是访问不了frame中的内容的,对应的解决思路是 driver. switch_to .frame(frane_element)。
比如qq空间登录的页面:
它的登录区域在一个iframe中,而selenium不能直接操控该页面中的iframe(框架)!代码:
import time from selenium import webdriver driver = webdriver.Chrome() url = 'https://qzone.qq.com/' driver.get(url) # 第一种,先进行元素定位,再进行切换 el_frame = driver.find_element_by_xpath('//*[@id="login_frame"]') driver.switch_to.frame(el_frame) # 第二种,直接switch_to.frame(id的值) # driver.switch_to.frame('login_frame') driver.find_element_by_id('switcher_plogin').click() driver.find_element_by_id('u').send_keys(账号) driver.find_element_by_id('p').send_keys(密码) driver.find_element_by_id('login_button').click() time.sleep(5) driver.quit()
注意:使用id进行定位不一定正确,一是因为可能存在不同的标签中出现相同的id值,二是它的id中可能含有时间戳等变化的值,比如163邮箱的登录,它的iframe的id中就含有时间戳
selenium对cookie的处理:
selenium能够帮助我们处理页面中的cookie,比如获取、删除
获取cookie:
driver.get_cookies()返回列表,其中包含的是完整的cookie信息!不光有name、value,还有domain等cookie其他维度的信息。所以如果想要把获取的cookie信息和requests模块配合使用的话,需要转换为name,value作为键值对的cookie字典#获取当前标签页的全部cookie信息 print(driver.get_cookies()) #把cookie转化为字典 cookies_dict = {data['name']: data['value'] for data in driver.get_cookies()}
selenium控制浏览器执行js代码:
selenium可以让浏览器执行我们规定的js代码。
我们打开一个网页的时候,可能电脑只能显示该网页一部分,当我们对未出现在屏幕区域的a标签进行点击时,selenium会报错
eg:
import time from selenium import webdriver driver = webdriver.Chrome() url = 'https://sn.lianjia.com/' driver.get(url) el_button = driver.find_element_by_xpath('/html/body/div[10]/div/div[1]/div[1]/ul/li[1]/a') el_button.click() time.sleep(5) driver.quit()
所以在进行click()操作时,我们需要进行滚动条操作,艾,就是下拉滚动条咯!!!
所以修改后:
import time from selenium import webdriver driver = webdriver.Chrome() url = 'https://sn.lianjia.com/' driver.get(url) # 滚动条的拖动: js = 'scrollTo(0, 800)' # 水平拖动0个像素,垂直拖动800个像素 # 执行js driver.execute_script(js) el_button = driver.find_element_by_xpath('/html/body/div[10]/div/div[1]/div[1]/ul/li[1]/a') el_button.click() time.sleep(5) driver.quit()
driver.execute_script(‘scrollTo(x, y)’)
滚动条: x-->水平移动, y--->垂直移动 单位是像素!!!
当然还有更多的操作需要先运行js,但是我前端还没有学习,所以还是一知半解,哈哈哈哈
页面等待:
- 强制等待:
time,sleep()
- 隐式等待:
隐式等待针对的是元素定位,隐式等待设置了一个时间,在一段时间内判断元素是否定位成功,如果完成了,就进行下一步; 在设置的时间内没有定位成功,则会报超时加载
driver.implicitly_wait(10) # 10秒- 显式等待:
明确等待每一个元素,一般用于软件测试,爬虫中一般不用,比较复杂,可以了显式等待:
from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By driver = webdriver.Chrome() driver.get('https://www.baidu.com') # 显式等待 WebDriverWait(driver, 20, 0.5).until( EC.presence_of_element_located((By.LINK_TEXT, '好123'))) # 参数20表示最长等特2B秒 # 参数0.5表示.5秒检查一次规定的标签是否存在 # EC.presence_of_element_located((By.LINK_TEXT,'好123'))表示通过链接文本内容定位标签 # 每0.5秒一次检查,通过链接文本内容定位标签是否存在,如果存在就向下继续执行;如果不存在,直到20秒上限就报错 print(driver.find_element_by_link_text('好123').get_attribute('href')) driver.quit()
隐式或强制:
import time from selenium import webdriver driver = webdriver.Chrome() driver.get('https://www.taobao.com/') time.sleep(1) for i in range(10): i += 1 try: time.sleep(3) # driver.implicitly_wait(3) element = driver.find_element_by_xpath('/html/body/div[9]/div/div/div/div[1]/div/ul/li[1]/a') print(element.get_attribute('href')) break except: js = 'scrollTo(0, {})'.format(i*1000) # js语句 driver.execute_script(js) # 执行js的方法 driver.quit()
selenium开启无界面模式:
绝大多数服务器是没有界面的,selenium控制Google浏览器也是存在无界面模式的,下面学习如何开启无界面模式(又称为无头模式)
方法:
from selenium import webdriver url = 'https://www.baidu.com' # 创建配置对象 opt = webdriver.ChromeOptions() # 添加配置参数,这个是核心 opt.add_argument('--headless') opt.add_argument('--disable-gpu') # 创建浏览器对象的时候添加配置对象 driver = webdriver.Chrome(options=opt) driver.get(url) driver.save_screenshot('baidu2.png') driver.quit()
selenium使用代理和更换user-agent:
selenium控制浏览器也可以使用代理ip的
如下:与上面类似
创建配置对象: opt = webdriver.ChromeOptions()
使用代理ip: opt.add_argument('--proxy-server=http://113.254.44.242:8382')注意:
更换ip代理,必须重新启动浏览器
当我们频繁的爬取某一网站的数据时,更换浏览器的版本更容易隐藏自己爬虫的身份:创建配置对象: opt = webdriver.ChromeOptions()
更换user-agent: opt.add_argument('--user-agent=Mozilla/5.0 python37')
案例:斗鱼直播:
获取所有直播界面上,直播间的标题,类型,主播名,观看人数及图片链接:
from selenium import webdriver import time class Douyu(object): def __init__(self): self.url = 'https://www.douyu.com/directory/all' # opt = webdriver.ChromeOptions() # opt.add_argument('--user-agent=Mozilla/5.0 python37') self.driver = webdriver.Chrome() def parse_data(self): # 这里要等待一会,要让浏览器将所以直播间的信息加载完才能进行下一步,否则会报错的,但是效率实在不高!! time.sleep(3) room_list = self.driver.find_elements_by_xpath('//*[@id="listAll"]/section[2]/div[2]/ul/li/div') # print(room_list) data_list = [] # 遍历房间列表,从每一个房间节点中获取数据 for room in room_list: temp = {} temp['title'] = room.find_element_by_xpath('./a/div[2]/div[1]/h3').text temp['type'] = room.find_element_by_xpath('./a/div[2]/div[1]/span').text temp['owner_name'] = room.find_element_by_xpath('./a/div[2]/div[2]/h2/div').text temp['num'] = room.find_element_by_xpath('./a/div[2]/div[2]/span').text temp['img'] = room.find_element_by_xpath('./a/div[1]/div[1]/img').get_attribute('src') data_list.append(temp) self.save_data(data_list) def save_data(self, data_list): with open('douyu.txt', 'a', encoding='utf8') as f: for data in data_list: data = str(data) + '\n' f.write(data) def run(self): # url # driver # get self.driver.get(self.url) self.parse_data() # 获取下一页,直到最后一页 while True: try: el_next = self.driver.find_element_by_xpath('//*[@class=" dy-Pagination-next"]') # 注意这里是element el_next.click() self.parse_data() except: break if __name__ == '__main__': douyu = Douyu() douyu.run() # 用selenium效率太低了,其实这里用抓包很简单!!!
7.反爬:(重中之重)
(1)常见概念
服务器反爬的原因
爬虫占总PV较高。浪费资源
资源被批量抓走,丧失竞争力法律的灰色地带
服务器常反什么样的爬虫
十分低级的应届毕业生
十分低级的创业小公司
失控小爬虫
竞争对手
失控的搜索引擎(或者说犯病了)
(2)常见的反爬:
- 基于身份识别进行反爬
1.headers字段反爬:
headers中有很多字段,这些字段都有可能被对方服务器拿过来进行判断是否为爬虫1.1 通过headers中的User-Agent字段来反爬
*反爬原理:爬虫默认情况下没有User-Agent,而是使用模块默认设置
*解决方法:请求之前添加User-Aent即可;更好的方式是使用User-Agent池来解决(收集 一堆User-Agent的方式,或者是随机生成User-Agent)
1.2 通过referer字段或者是其他字段来反爬
*反爬原理:爬虫默认情况下不会带上referer字段,服务器端通过判断请求发起的源头, 以此判断请求是否合法
*解决方法:添加referer字段
1.3 通过cookie来反爬
*反爬原因:通过检查cookies来查看发起请求的用户是否具备相应权限,以此来进行反爬 *解决方案:进行模拟登陆,成功获取cookies之后在进行数据爬取2.通过请求参数反爬:
2.1 通过从html静态文件中获取请求数据(github登录数据)
*反爬原因:通过增加获取请求参数的难度进行反爬
*解决方案:仔细分析抓包得到的每一个包,搞清楚请求之间的联系
2.2 通过发送请求获取请求数据
*反爬原因:通过增加获取请求参数的难度进行反爬
*解决方案:仔细分析抓包得到的每一个包,搞清楚请求之间的联系,搞清楚请求参数的 来源
2.3 通过js生成请求参数
*反爬原理:js生成了请求参数
*解决方法:分析js,观察加密的实现过程,通过js2py获取js的执行结果,或者使用 selenium来实现
2.4 通过验证码来反爬
*反爬原理:对方服务器通过弹出验证码强制验证用户浏览行为
*解决方法:打码平台或者是机器学习的方法识别验证码,其中打码平台廉价易用,更值 得推荐
- 基于爬虫行为进行反爬
1.基于请求频率或总请求数量
爬虫的行为与普通用户有着明显的区别,爬虫的请求频率与请求次数要远高于普通用户
1.1 通过请求ip/账号单位时间内总请求数量进行反爬
*反爬原理∶正常浏览器请求网站,速度不会太快,同一个ip/账号大量请求了对方服务 器,有更大的可能性会被识别为爬虫
*解决方法:对应的通过购买高质量的ip的方式能够解决问题/购买个多账号
1.2 通过同一 ip/ 账号请求之间的间隔进行反爬
*反爬原理:正常人操作浏览器浏览网站,请求之间的时间间隔是随机的,而爬虫前后两 个请求之间时间间隔通常比较固定同时时间间隔较短,因此可以用来做反爬
*解决方法:请求之间进行随机等待,模拟真实用户操作,在添加时间间隔后,为了能够 高速获取数据,尽量使用代理池,如果是账号,则将账号请求之间设置随机体眠
1.3 通过对请求ip/账号每天请求次数设置阈值进行反爬
*反爬原理:正常的浏览行为,其一天的请求次数是有限的,通常超过某一个值,服务器 就会拒绝响应
*解决方法:对应的通过购买高质量的ip的方法/多账号,同时设置请求间随机休眠2.根据爬取行为进行反爬,通常在爬取步骤上做分析
2.1 通过js实现跳转来反爬
*反爬原理:js实现页面跳转,无法在源码中获取下一页url解决方法:多次抓包获取条状 url,分析规律
2.2 通过蜜罐陷阱)获取爬虫ip(或者代理ip),进行反爬
*反爬原理:在爬虫获取链接进行请求的过程中,爬虫会根据正则,xpath,css等方式进 行后续链接的提取,此时服务器端可以设置一个陷阱url(比如我们的目标是 所有li标签中的数据,但是他可以设置一个li标签的display为none,这样用户 就看不到,但是你爬虫会爬取,当年获取并点击里面的url,服务器就判断你 是爬虫并获取了你的ip),会被提取规则获取,但是正常用户无法获取,这 样就能有效的区分爬虫和正常用户
*解决方法:完成爬虫的编写之后,使用代理批量爬取测试/仔细分析响应内容结构,找出 页面中存在的陷阱
2.3 通过假数据反爬
*反爬原理:向返回的响应中添加假数据污染数据库,通常假数据不会被正常用户看到
*解决方法:长期运行,核对数据库中数据同实际页面中数据对应情况,如果存在问题/仔 细分析响应内容
2.4 阻塞任务队列
*反爬原理:通过生成大量垃圾url,从而阻塞任务队列,降低爬虫的实际工作效率
*解决方法:观察运行过程中请求响应状态/仔细分析源码获取垃圾url生成规则,对URL进 行过滤
2.5 阻塞网络IO
*反爬原理:发送请求获取响应的过程实际上就是下载的过程, 在任务队列中混入一个大 文件的url,当爬虫在进行该请求时将会占用网络io,如果是有多线程则会占用线程
*解决方法:观察爬虫运行状态/多线程对请求线程计时/发送请求钱
2.6 运维平台综合审计
*反爬原理:通过运维平台进行综合管理,通常采用复合型反爬虫策略, 多种手段同时使用
*解决方法:仔细观察分析,长期运行测试目标网站,检查数据采集速度,多方面处理
- 基于数据加密进行反爬
1.对响应中含有的数据进行特殊化处理
通常的特殊化处理主要指的就是css数据偏移/自定义字体/数据加密/数据图片/特殊编码 格式等1.1 通过自定义字体来反爬 (下图来自猫眼电影电脑版):
*反爬思路:使用自有字体文件
*解决思路:(这里不行)切换到手机版/解析字体文件进行翻译(推荐)
1.2通过css来反爬下图来自 (去哪儿网)
*反爬思路:源码数据不为真正数据,需要通过css位移才能产生真正数据
*解决思路:计算css的偏移
1.3 通过js动态生成数据进行反爬(安居客租房)
*反爬原理:通过js动态生成
*解决思路:解析关键js,获得数据生成流程,模拟生成数据(这里是进行点击操作)
1.4 通过数据图片化反爬
*反爬原理:不是直接的数据,而是图片
*解决思路:通过使用图片解析引擎从图片中解析数据
1 .5通过编码格式进行反爬
*反爬原理: 不适用默认编码格式,在获取响应之后通常爬虫使用utf-8格式进行解码,此 时解码结果将会是乱码或者报错
*解决思路:根据源码进行多格式解码,或者真正的解码格式
(3)验证码
- 基础知识
1.1 什么是图片验证码
*验证码(CAPTCHA)是"Completely Automated Public Turing test to tell Computers and HumansApart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户 是计算机还是人的公共全自动程序。
1.2 验证码的作用
*防止恶意破解密码、刷票、论坛灌水、刷页。有效防止某个黑客对某一个特定注册用 户用特定程序暴力破解方式进行不断的登录尝试,实际上使用验证码是现在很多网站 通行的方式(比如招商银行的网上个人银行,百度社区〉,我们利用比较简易的方式 实现了这个功能。虽然登录麻烦一点,但是对网友的密码安全来说这个功能还是很有 必要,也很重要。
1.3 图片验证码在爬虫中的使用场景
*注册
*登录
*频繁发送请求时,服务器弹出验证码进行验证
1.4 图片验证码的处理方案
*手动输入(input)这种方法仅限于登录一次就可持续使用的情况
*图像识别引擎解析使用光学识别引擎处理图片中的数据,目前常用于图片数据提取, 较少用于验证码处理
*打码平台爬虫常用的验证码解决方案
- 图片识别引擎
1. 什么是tesseract
*Tesseract,一款由HP实验室开发由Google维护的开源OCR引擎,特点是开源,免 费,支持多语言,多平台。
*项目地址: https:/github.com/tesseract-ocr/tesseract2. 引擎的安装
*mac环境下直接执行命令:
brew install --with-training-tools tesseract
*windows环境下的安装可以通过exe安装包安装,下载地址可以从GitHub项目中的wiki 找到。安装完成后记得将Tesseract 执行文件的目录加入到PATH中,方便后续调用。
* linux环境下的安装:
sudo apt-get install tesseract-ocr
3. Python库的安装# PIL用于打开图片文件 pip/pip3 install pillow # pytesseract模块用于从图片中解析数据 pip/pip3 install pytesseract
4. 图片识别引擎的使用
●通过pytesseract模块的 image_to_ string 方法就能将打开的图片文件中的数据提取成字符串数据,具体方法如下from PIL import Image import pytesseract # 打开一个图片文件 im = Image.open('image1.jpg') result = pytesseract.image_to_string(im) print(result)
- 打码平台
1.为什么使用打码平台,因为很多时候的验证码通过什么的图片识别引擎识别不了,比如
识别率太低2. 这里我用的是超级鹰,当然你们可以选择其他的打码平台,这个看自己!
*你在注册完后,将下面的代码copy到你的python项目中,在其他文件中使用时只需要 import即可,只需要填写你的超级鹰用户名和密码即可!!是不是so easy!
*下面的代码其实在超级鹰开发文档中有的,我纯粹是为了凑字数,哈哈哈!!#!/usr/bin/env python # coding:utf-8 import requests from hashlib import md5 class Chaojiying_Client(object): def __init__(self, username, password, soft_id): self.username = username password = password.encode('utf8') self.password = md5(password).hexdigest() self.soft_id = soft_id self.base_params = { 'user': self.username, 'pass2': self.password, 'softid': self.soft_id, } self.headers = { 'Connection': 'Keep-Alive', 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', } def PostPic(self, im, codetype): """ im: 图片字节 codetype: 题目类型 参考 http://www.chaojiying.com/price.html """ params = { 'codetype': codetype, } params.update(self.base_params) files = {'userfile': ('ccc.jpg', im)} r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers) return r.json() def ReportError(self, im_id): """ im_id:报错题目的图片ID """ params = { 'id': im_id, } params.update(self.base_params) r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers) return r.json() if __name__ == '__main__': # 用户中心>>软件ID 生成一个替换 96001 chaojiying = Chaojiying_Client('超级鹰用户名', '超级鹰用户名密码', '96001') # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要// im = open('a.jpg', 'rb').read() # 1902是验证码类型(一定要去官方文档查看你需要识别的验证码的类型代号) 官方网站>>价格体系 print (chaojiying.PostPic(im, 1902))
8.chrome浏览器的使用
(1)新建隐身窗口
浏览器中直接打开网站,会自动带上之前网站时保存的cookie,但是在爬虫中首次获取页面是没有携带cookie的,这种情况如何解决呢?
使用隐身窗口,首次打开网站,不会带上cookie,能够观察页面的获取情况,包括对方服务器如何设置cookie在本地
(2)Chrome中network的更多功能
2.1 Preserve log
默认情况下,页面发生跳转后,之前的请求url地址等信息都会消失(相当于刷新),勾 选perserve log后,之前的请求都会被保留2.2 filter过滤
在url地址很多的时候,可以在filter中输入部分url地址,对所有的uri地址起到一定的过 滤效果,具体位置在上面第二幅图中的2的位置2.3 观察特定种类的请求
在上面第二幅图中的3的位置,有很多选项,默认是选择的all,即会观察到所有种类的请 求很多时候处于自己的目的可以选择all右边的其他选项,比如常见的选项:
*XHR:大部分情况表示ajax请求
*JS:js请求
*CSS:css请求
但是很多时候我们并不能保证我们需要的请求是什么类型,特别是我们不清楚一个请求是 否为ajax请求的时候,直接选择all ,从前往后观察即可,其中js,css,图片等不去观察即可
不要被浏览器中的一堆请求吓到了,这些请求中除了js,css,图片的请求外,其他的请求 并没有多少个
(3)js解析
一些url参数是通过js生成的,这时候我们只能去查找这些js并观察提取出生成参数的部分
1.确定js的位置
既然一些url参数是通过js生成的,那我们就先要确定该参数是由那个js生成的,所以我 们需要定位该js1.1 观察按钮的绑定事件
当然,另外一个也有可能!
1.2 通过search来搜索
部分网站的按钮可能并没有绑定js事件监听,那么这个时候可以通过搜索请求中的关键 字来找到js的位置,比livecell点击美化:
1.3 通过initiator(发起)来判断
我点登录后,就会向服务器发起这个post请求!补:一般来说,就按这三个能找到正确的js
2. 观察js的执行过程,找到关键代码
找到js的位置之后,我们可以来通过观察js的位置,找到js具体在如何执行,后续我们 可以通过python程序来模拟js的执行,或者是使用类似js2py直接把js代码转化为python程 序去执行
观察js的执行过程最简单的方式是添加断点添加断点的方式:在左边行号点击即可添加,对应的右边BreakPoints中会出现现有的所有断点
添加断点之后继续点击登录,每次程序在断点位置都会停止,通过如果该行有变量产生,都会把变量的结果展示在Scoope中
在上图的右上角有1,2,3三个功能,分别表示:
- 1:继续执行到下一个断点
- 2:进入调用的函数中
- 3:从调用的函数中跳出来3. js2py的使用(pip/pip3 install js2py)
在知道了js如何生成我们想要的数据之后,那么接下来我们就需要使用程序获取js执行 之后的结果了
3.1 js2py的介绍
js2py是一个js的翻译工身,也是一个通过纯python实现的js的解释器,github有源码与 示例
3.2 js的执行思路
js的执行方式大致分为两种:
*在了解了js内容和执行顺序之后,通过python来完成js的执行过程,得到结果
*在了解了js内容和执行顺序之后,使用类似js2py的模块来执js代码,得到结果
但是在使用python程序实现js的执行时候,需要观察的ijs的每一个步骤,非常麻烦,所以 更多的时候我们会选择使用类似js2py的模块去执行js,接下来我们来使用js2py实现人人网 登录参数的获取
-
实例-有道翻译:
在对有道翻译测试时,发现只有3个参数是要算的,其他的参数都是固定的:
在定位到js中,找到关于这仨个参数的生成部分:
可以看出,ts是时间戳,salt是在ts的末尾加一个随机数,而sign是hash得到,可以用 python中的hashlib实现:
代码:
import requests import json import time import hashlib import random class Youdao_tanslation(object): def __init__(self, word): self.url = 'https://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule' # 这里三剑客缺一不可!! self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36', 'Referer': 'https://fanyi.youdao.com/', 'Cookie': 'OUTFOX_SEARCH_USER_ID=607328047@119.4.175.209; OUTFOX_SEARCH_USER_ID_NCOO=2132201900.6876037; JSESSIONID=aaaWrnEcvfJNlepESb1Ux; fanyi-ad-id=115021; fanyi-ad-closed=1; ___rl__test__cookies=1630832149909' } # 需要翻译的句子 self.word = word self.formdata = None # n.md5("fanyideskweb" + e + i + "Y2FYu%TNSbMCxc3t2u^XT") def md5(self, salt): # 创建一个hash对象,还有其他运算,不只是md5 md5 = hashlib.md5() # 向hash对象中添加需要做hash运算的字符串 data = "fanyideskweb" + self.word + str(salt) + "Y2FYu%TNSbMCxc3t2u^XT" md5.update(data.encode()) # 注意,这里要encode,因为接收的是字节 # 获取字符串的hash值,注意,我们需要的是16进制!! return md5.hexdigest() def run(self): # 要算的三个值 lts = str(int(time.time()*1000)) salt = lts + str(random.randint(0, 9)) # 构造表单 self.formdata = { 'i': self.word, 'from': 'AUTO', 'to': 'AUTO', 'smartresult': 'dict', 'client': 'fanyideskweb', 'salt': salt, 'sign': self.md5(salt), 'lts': lts, 'bv': 'eda468fc64295ecf2810ab8a672c2db1', # 固定值 'doctype': 'json', 'version': '2.1', 'keyfrom': 'fanyi.web', 'action': 'FY_BY_REALTlME' } # 发送post请求,获取响应 response = requests.post(url=self.url, headers=self.headers, data=self.formdata) result = json.loads(response.content) # 转化为python字典 return result['translateResult'][0][0]['tgt'] if __name__ == '__main__': youdao = Youdao_tanslation('人生苦短,我用爬虫!') print(youdao.run())
总结:
学习更注重于实践,加油老铁(应该大多数都是男的吧,哈哈哈)