爬虫
- 开源模块:
- requests
response = requests.get(“url”)
response.text
response.content
response.encoding = response.apparent_encoding # 使用网站编码
response.status_code
response.cookies.get_dict()
requests.get(“url”, cookie={‘xx’: ‘yy’})
requests.request方法参数:
-method: 提交方式
-url: 提交URL
-params: 在URL中传递的参数
requests.request(
method='GET',
url='http://www.baidu.com',
params={'k1':'v1','k2':'v2'}
)
-data: 在请求体重传递的参数
requests.request(
method='GET',
url='http://www.baidu.com',
data={'k1':'v1','k2':'v2'}
)
-json: 在请求体重传递的参数(字典中嵌套字典时使用)
requests.request(
method='GET',
url='http://www.baidu.com',
data={'k1':'v1','k2':{'k3':'v3'}}
)
-headers: 请求头
requests.request(
method='POST',
url='https://github.com/login',
data={'k1':'v1','k2':{'k3':'v3'}},
headers={
'Referer': 'https://github.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
}
)
-cookies: Cookies
-session: 用于保存客户端历史访问信息
import requests
def requests_session():
session = requests.Session()
### 1、首先登陆任何页面,获取cookie
i1 = session.get(url="http://dig.chouti.com/help/service")
### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权
i2 = session.post(
url="http://dig.chouti.com/login",
data={
'phone': "8615131255089",
'password': "xxxxxx",
'oneMonth': ""
}
)
i3 = session.post(
url="http://dig.chouti.com/link/vote?linksId=8589623",
)
print(i3.text)
- beautisoup
安装:pip install requests beautifulsoup4
饭粒1:爬取汽车之家新闻页
import requests
from bs4 import BeautifulSoup
url = 'https://www.autohome.com.cn/all/'
response = requests.get(url)
response.encoding = response.apparent_encoding # 使用网站编码
soup = BeautifulSoup(response.text, features='html.parser')
content = soup.find(id='auto-channel-lazyload-article')
# 组合条件:soup.find('div', class='c1')
li_list = content.find_all('li') # 返回列表
for i in li_list:
a = i.find('a')
if a:
# 新闻URL
url = a.attrs.get('href').strip('//')
print(url)
# 新闻标题
text = a.find('h3').text # 获取标签内容
print(text)
# 新闻图片URL
img_url = 'http://' + a.find('img').attrs.get('src').strip('//')
# 保存图片
img_response = requests.get(url=img_url)
file_name = 'image' + os.path.sep + str(uuid.uuid4()) + '.jpg'
with open(file_name, 'wb') as f:
f.write(img_response.content)
饭粒2:登陆github
import requests
from bs4 import BeautifulSoup
# 获取token
r1 = requests.get('https://github.com/login')
s1 = BeautifulSoup(r1.text, 'html.parser')
token = s1.find(name='input', attrs={'name': 'authenticity_token'}).get('value')
r1_cookie_dict = r1.cookies.get_dict()
# 将用户名密码token发送到服务端,post
"""
github登陆默认post提交的信息:
utf8:✓
authenticity_token:ollV+avLm6Fh3ZevegPO7gOH7xUzEBL0NWdA1aOQ1IO3YQspjOHbfnaXJOtVLQ95BtW9GZlaCIYd5M6v7FGUKg==
login:asdf
password:asdf
commit:Sign in
"""
r2 = requests.post(
'https://github.com/session',
data={
"utf8": '✓',
"authenticity_token": token,
'login': 'afengbo',
'password': 'woshimima',
'commit': 'Sign in'
},
cookies=r1_cookie_dict
)
r2_cookie_dict = r2.cookies.get_dict()
cookie_dict = {}
cookie_dict.update(r1_cookie_dict)
cookie_dict.update(r2_cookie_dict)
# 获取个人信息页面
r3 = requests.get(
url='https://github.com/settings/emails',
cookies=cookie_dict
)
print(r3.text)
网站反爬策略
为啥要反爬
出于保护自身网站有价值的数据资源、保护自身网站服务器资源的目的,识别出爬虫程序,限制恶意访问请求。
网站如何识别爬虫
爬虫程序和一般的人类使用的如浏览器、app等对于网站服务端来说,都是客户端程序。`网站反爬本质上是需要先找出真正人类用户和爬虫程序的区别`。
- 通过请求信息识别
- Headers
UA
Referer - Cookie
登录
验证码 - 特定请求参数
是否具备且是正确token、sign等加密参数
- Headers
- 基于用户行为识别
- 单位时间的单个客户端的请求频率
IP
UA
账号(cookie)
三者组合 - 鼠标移动轨迹
是否有鼠标移动事件
鼠标移动是否符合人类行为特征(如先加速后减速特征) - 键盘输入事件
- 单位时间的单个客户端的请求频率
- 频繁/定期更换反爬措施
- 网站如何惩治爬虫
网站一旦识别出某个请求是爬虫,通常会采取以下措施:
- 直接拒绝爬虫请求
4xx
2xx + 空数据 - 返回验证码验证,验证不通过,则拒绝访问或再次返回验证
- 不拒绝爬虫,但返回比较真实的伪数据和真数据混在一起
要实现网站的反反爬,必须先分析出网站的反爬策略到底是怎样的,然后才能对症下药。
- 确定自己被反爬
- 找出原因如何被反爬的
对于反爬策略常年不变的,那么只需要写几个简单的测试爬虫,就能找到网站的反爬策略。但如果是频繁更新反爬策略的网站,那么你就需要定期检测网站反爬策略,或利用爬虫程序监控,一旦爬虫获取不到正确的数据时,发出警报,然后立刻检查,看网站反爬策略是否发生变化,因为通常都是这个导致的爬虫运行一段时间后,出现无法正确获取数据的情况。
网站反爬策略应对
通常网站来说,其反爬措施如果过于严厉,则可能出现误判,如误将正常的用户请求封杀了,这样势必会影响网站的用户体验。
因此网站限制的不是爬虫的所有访问,而是主要限制短时间内发起的批量性爬虫请求。如果爬虫程序如同正常人一样访问网站,一般情况下,网站即使识别出你是爬虫,但出于对用户体验的考虑,而选择不处理你。
应对反爬策略基本方案,也就是别让自己的爬虫程序太肆无忌惮,这样可能让网站即使识别出你而不反爬你:
- 能慢下来的就不要太快
- 破解困难较大的,适当的牺牲一点自动化(如登录时手动打码)
当想要进一步提高程序的自动化和采集效率时,那么必须要根据网站的反爬策略来制定相应的应对策略:
也就是说,我们的反反爬其实就是解决两个问题:
- 采集效率问题
UA池+IP池+Cookie池模拟多个客户端发起请求 - 程序完全自动化运行问题
爬虫程序自动生成加密参数
爬虫程序自动化处理验证码
UA池、IP池、Cookie池
-
UA池
UA池是最好解决的,一般使用时不用考虑更新。可自行收集一些ua,或者使用fake-useragent等模块 -
IP池
IP池相对复杂一点,使用时需要考虑定期更新,且需要不断检测IP的质量情况 -
代理池维护架构:
- 获取器:定期从代理IP来源处获取IP,如代理IP商、免费代理。实现参考:利用reqeusts等定义将获取代理IP
- 过滤器:对代理IP进行入库检测,初步过滤不可用代理IP。实现参考:利用tornado.httpclient或aiohttp等(因为自身是异步的),通过访问百度首页等,逐一过滤判断代理IP的可用性
- 代理队列:将过滤的可用代理IP存储起来,由于是分布式,因此可考虑将代理IP放入redis中存储。实现参考:利用Redis的有序集合类型存储代理IP,默认权重可设为100
- 定时检测器:由于IP一定时间后可能会失效,因此需要定期检测代理队列中所有的IP的可用性。
- 实现参考:定期遍历Redis有序集合中的代理IP,使用过滤器中的同样方法检测,一旦发现有不可用代理IP,则将权重降低,当降低到一次分值时,如90、80等,则可删除该IP。
-
代理IP来源:
- 免费代理:质量普遍较差,不推荐
- 代理IP提供商(如蘑菇代理、讯代理):属于常用方式
- 代理隧道服务(如阿布云的HTTP隧道,推荐使用):其原理是,请求交由固定的阿布云转发,它内部维护着一个庞大的IP池,由它随机或指定一个IP进行转发。优点是不用自己维护代理池,代理速度与稳定性都不错,缺点是IP无法自己筛选和维护。
- ADSL拨号代理:原理是购买一批ADSL VPS服务器来,通过不断的拨号和重拨号,获得批量的IP,对应服务商有云立方、青果云、无极网络等。优点是IP独享使用,速度特别快、稳定性也极强,缺点是成本高昂,实现相对比较复杂。
ADSL拨号代理实现原理:
Cookie池(主要指记录有登录状态的Cookie)
当网站登录接口比较复杂(如需要处理验证码),或如果在代码中频繁登录可能导致的账号被限制等情况,同时又需要使用多用户解决反爬问题时,那么就需要使用到Cookie池
Cookie池的维护,但主要是还考虑以下几个问题:
- Cookie获取:手动登录后,存储Cookie,注意池中每个用户只有一个Cookie,也就是有多少个用户,就可以考虑建立多大的池
- Cookie的更新:根据网站设定Cookie的过期时间,定期更新Cookie
自动生成加密参数
网站请求参数中部分参数是通过加密后发出的,对于PC web端,该加密参数都是通过JS代码生成的,其自动化实现思路都是通过分析网站前端JS代码,找到对应加密算法/函数,然后利用js2py/pyexecjs等技术实现在python中自动化生成加密参数。
举栗:百度翻译
利用搜索定位与chrome的断点调试找到对应的sign值生成函数
- 验证码处理方案
- 简单验证码:直接利用OCR识别,
tessersor
模块 - 滑动验证码、点触类型验证码:计算坐标
- 手机验证码、网银U盾等:无解,无法实现自动化
现在很多滑动、点触验证码都是使用的极验验证码,对于这类验证码如果考虑破解参数、还原图片等方式处理,是非常耗时耗力的,而且不同网站的结构不一,无法做到统一。
如果是滑动,那么就利用缺口图和原图进行像素比对,找到缺口位置的坐标,利用selenium进行拟人化的拖拽;
对于如点触、混淆的文字识别都是考虑利用第三方打码平台(如超级鹰),进行识别获取坐标等。因为它们有非常强大的素材库,识别度高。然后利用seleinum来模拟鼠标点击行为实现,但要注意的是鼠标轨迹的人类特征模拟。