反爬
常见反爬
- 通过User-Agent来反爬
- 通过Referer来反爬
- 通过Cookie来反爬
- 通过js加密请求参数来反爬
- 通过行为验证来反爬
- 通过ip地址来反爬
- 通过自定义字体来反爬
- 通过css样式来反爬
通过User-Agent来反爬
- 反爬原理:
-
爬虫发送请求时,请求头中默认没有User-Agent,或提供非正常的UA
-
- 应对思路:
-
在请求时添加UA
-
- 具体应对:
-
requests模块发送请求时在headers参数中添加UA键值对
-
selenium默认自带被控制浏览器的UA,也可以替换UA
- 随机User-Agent,如 faker模块.
-
通过Referer来反爬
Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的
- 反爬原理:
-
爬虫发送请求时,请求头中默认情况下不会带上Referer字段
-
- 应对思路:
-
在请求时添加Referer
-
- 具体应对:
-
requests模块发送请求时在headers参数中添加Referer键值对,从抓包信息中复制Referer信息
-
selenium默认自带Referer
-
通过Cookie来反爬
无论是否需要登陆,web服务器都可以在用户的浏览器中设置Cookie;Cookie是header的一部分,当浏览器向web服务器发送请求的时候,如果存在Cookie就一定会携带
- 反爬原理:
-
web服务器检查请求头中的cookie是否是之前设置的cookie
-
- 应对思路:
-
携带cookie发送请求
-
- 具体应对:
-
requests模块发送请求时,使用requests.session自动处理cookie
-
用requests模块发送请求时,在cookies参数或在headers参数中,使用selenium获取的cookie;注意cookie的过期时间
-
构建cookie池(根据cookie的过期时间,定期批量获取的cookie,放到数据库中),requests模块发送请求时,使用从cookie池中获取的cookie
-
通过js来反爬
很多时候,网站利用用户的浏览器对返回的数据或请求的参数进行解密或加密,比如百度翻译PC版
- 反爬原理:
-
利用用户的浏览器执行web服务器返回的js代码来对加密的响应内容进行解密(不常见)
-
利用用户的浏览器执行web服务器返回的js代码来对请求参数进行加密,之后再发送请求(常见)
-
- 应对思路:
-
python重写js代码的功能、或执行js代码拿到结果
-
- 具体应对:
-
完全看不懂js代码:selenium
-
完全看懂js代码:python重写js代码的功能
-
能够看懂js代码执行的大致过程:使用js2py模块运行相关的js代码,获取运行结果(js2py模块在下面将介绍到)
-
通过验证码验证行为来反爬
我们在浏览网站时,经常看见类似12306或者这样的等一些用户行为验证
- 反爬原理:
-
对方服务器通过弹出验证码强制验证用户浏览行为
-
- 应对思路:
-
使用打码平台或深度学习的方式破解验证码
-
- 具体应对:
- 一些打码平台(自己搜索)
通过ip地址来反爬
正常用户很难在很短的时间内打开需要点击才能访问的链接,那么网站就可以根据ip地址和cookie以及user-agent等能区分不同用户身份的信息来进行反爬
- 反爬原理:
-
检测同一个ip在单位时间内是否发送了大量请求
-
经常和cookie以及user-agent配合检查
-
- 应对思路:
-
网上获取免费的代理ip
-
购买代理ip
-
使用代理ip池
-
- 具体应对:
-
构建代理ip池
- 把免费以及收费的代理ip放到数据库中
- 使用时随机获取一个代理ip
- 向目标url发送请求,并设置超时
- 如果超时或无法使用就在数据库中标记该代理ip对具体访问的url不可用
-
requests模块发送请求使用proxies参数
-
selenium可以通过配置对象来使用代理ip
-
通过自定义字体来反爬
打开猫眼电影PC页面,右键检查用户评分,查看网页源代码
- 反爬原理:
-
利用浏览器能够加载渲染并正确显示自定义字体的功能,使用自定义字体不影响正常用户浏览
-
- 应对思路:
-
从移动端页面获取数据
-
处理并解析自定义字体
-
- 具体应对:
- 切换到移动端的页面,如用浏览器移动端模式访问猫眼电影移动端页面
- 使用fontTools模块来处理猫眼电影的自定义字体文件
通过css样式来反爬
打开去哪儿网PC端页面,搜索并查看飞机票信息
- 反爬原理:
- 利用css样式来便宜标签,且不影响正常用户查看
- 应对思路:
- 计算css偏移
js2py模块的使用
当我们仅了解js运行的大致情况,又不能使用selenium的时候,就可以使用js2py来解决js执行和js加密的问题
js2py简介
- 作用:
- js2py模块能够帮助我们在python代码中执行js代码,并获取js代码运行的结果或其中的变量
- 安装:
- pip install js2py
简单使用
import js2py
js_str = '''
function func(x)
{
return x
}
'''
# 实例化js解释器对象
js_content = js2py.EvalJs()
js_content.execute(js_str) # 传入并执行js代码
# 调用js代码中的函数并获取返回值
ret = js_content.func('hahaha')
print(ret)
js_var = 'var abc = 1'
js_content.execute(js_var) # # 传入并执行js代码(变量)
# 获取js代码中的变量
print(js_content.abc)
# 向js执行解释器中传入变量
js_content.abc = 2
print(js_content.abc)
- 步骤:
- 实例化js解释器对象
-
js_content = js2py.EvalJs()
-
- 传入并执行js代码
-
js_content.execute(js代码)
-
- 调用js中的函数并获取返回结果
-
ret = js_content.func('参数')
-
- 获取js中的变量
-
js_content.变量名
-
- 向js中传入变量
-
js_content.变量名 = 值
-
- 实例化js解释器对象
案例:人人网登录
利用谷歌浏览器抓包确定登录的url、请求方法以及请求参数
- url:http://activity.renren.com/livecell/ajax/clog
- 请求方法:POST
- 请求体参数:
- phoneNum: 账号
- password: 加密之后的密码
- c1: 0
- rKey: rkey请求获取的
通过谷歌浏览器抓包知道需要以下三步
- 获取rkey
- 执行密码加密js
- 执行密码加密js过程中会执行BigInt.js 、RSA.js 、Barrett.js 三个js文件
-
获取rkey
-
url:http://activity.renren.com/livecell/rKey
-
请求方法:GET
-
-
密码加密js:
t.password = t.password.split("").reverse().join(""),
setMaxDigits(130);
var o = new RSAKeyPair(n.e,"",n.n),
r = encryptedString(o, t.password);
- 拷贝BigInt.js 、RSA.js 、Barrett.js 三个js文件到本地
参考代码:
import requests
import json
import js2py
# 实例化session对象
session = requests.session()
# 自定义头文件
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Mobile Safari/537.36",
"X-Requested-With": "XMLHttpRequest",
"Content-Type":"application/x-www-form-urlencoded"
}
# 设置session的请求头信息
session.headers = headers
# 获取js代码中所需要的n值和rKey参数
response = session.get("http://activity.renren.com/livecell/rKey")
# print(response.content.decode())
n = json.loads(response.content)['data']
# 根据获取信息对密码进行加密
# 准备用户名和密码
phoneNum = "131..."
password = "****"
# 使用js2py生成js的执行环境:context
context = js2py.EvalJs()
# 拷贝使用到js文件的内容到本项目中
# 读取js文件的内容,使用context来执行它们
with open("BigInt.js", 'r', encoding='utf8') as f:
context.execute(f.read())
with open("RSA.js", 'r', encoding='utf8') as f:
context.execute(f.read())
with open("Barrett.js", 'r', encoding='utf8') as f:
context.execute(f.read())
# 向context环境中添加需要数据
context.t = {'password': password}
context.n = n
# 执行加密密码的js字符
js = '''
t.password = t.password.split("").reverse().join(""),
setMaxDigits(130);
var o = new RSAKeyPair(n.e,"",n.n)
, r = encryptedString(o, t.password);
'''
context.execute(js)
# 通过context获取加密后密码信息
# print(context.r)
password = context.r
# 使用session发送登录请求
data = {
'phoneNum': '131....',
'password': password,
'c1':0,
'rKey':n['rkey']
}
response = session.post("http://activity.renren.com/livecell/ajax/clog", data=data)
print(response.content.decode())
# 访问需要登录的资源
response = session.get("http://activity.renren.com/home#profile")
print(response.content.decode())
cooike代理池和ip代理池
cooike代理池
- cookie池的应用场景
- 当需要很多账号进行大批量采集数据时,在发送获取数据的请求时都需要登录
- 或在发送获取数据的请求时都需要发送前置请求
- cookie池的作用
- 省资源(减少耗时、减少请求)
- 保护账号或ip(避免频繁访问)
- 定期获取cookie:可以使用requests模块或selenium
- 存储cookie
- 获取cookie的函数或webapi
- 删除失效cookie的函数或webapi(获取cookie的时间周期可以设置在cookie有效期内)
总结:我们可以不断的去获取cooike然后把这些cookie储存起来,我们要用的时候随机拿一个,如果遇到过期的cooike就把它从储存的地方删除
ip代理池
- 应用场景:大量代理ip频繁切换时,需要使用代理ip池
- 作用:提高数据抓取的效率,让服务器以为不是同一个客户端在请求,避免真实ip被封
- 定期获取代理ip:更新存储的代理ip
- 免费的
- 收费的
- 存储代理ip
- 提供代理ip的接口
- 删除/标记代理ip的接口
- 某个经过初步检查(请求下baidu等)代理ip对一个目标url不可用时,不代表对其它域名的url也不好使
- 这个时候需要在代理ip存储的载体中,标记该代理ip对某个域名无效
总结:我们可以通过一些代理ip网站去获取一些代理ip,然后把这些ip储存起来,我们要用的时候随机拿一个,通过这个ip去访问我们需要爬取的网站,这样可以避免我们使用一个ip频繁访问而被封的情况。