注:笔记大部分来源书本,仅供学习交流:【Python3反爬虫原理与绕过实战—韦世东】
将爬虫的爬取过程分为网络请求,文本获取和数据提取3个部分。
信息校验型反爬虫主要出现在网络请求阶段,这个阶段的反爬虫理念以预防为主要目的,尽可能拒绝反爬虫程序的请求。
动态渲染和文本混淆则出现在文本获取及数据提取阶段,这个阶段的反爬虫理念以保护数据为主要目的,尽可能避免爬虫获得重要数据
特征识别反爬虫通过客户端的特征、属性或用户行为特点来区分正常用户和爬虫程序的手段
1、信息校验型反爬虫
信息:客户端发起网络请求时的请求头和请求正文
校验:服务器端通过对信息的正确性、完整性或唯一性进行验证或判断,从而区分正常用户和爬虫程序的行为
信息校验主要解决了客户端身份鉴别、数据来源判断和请求合法判断等问题,避免数据接收者使用被篡改过的数据,保证数据的有效性
(1)User-Agent、Host、Referer等反爬虫
服务器端通过校验请求头中的User-Agent、Host、Referer值来区分正常用户和爬虫程序的手段
判断:客户端类型、域名、来源指向
(2)Cookie反爬虫
服务器端通过校验请求头中的Cookie值来区分正常用户和爬虫程序的手段
Cookie:用于Web服务器的用户身份信息存储或状态保持,也能用于反爬虫(大部分的爬虫程序在默认情况下只请求HTML文本资源,这意味着它们并不会主动完成浏览器保存Cookie的操作)
(3)签名验证反爬虫
服务器通过验证请求正文中的字段值来区分正常用户和爬虫程序的手段
签名:根据数据源进行计算或加密的过程,签名的结果是一个具有唯一性和一致性的字符串
签名结果:使它成为验证数据来源和数据完整性的条件,可以有效避免服务器端将伪造的数据或篡改的数据当成正常数据处理;
签名验证反爬原理:由客户端生成一些随机值和不可逆的MD5加密字符串,并在发起请求时将这些值发送给服务器端,服务器端使用相同的方式对随机值进行计算以及MD5加密,如果服务器端得到的MD5值与前端提交的MD5值相等,就代表是正常需求,否则返回403;
例子:有道翻译
(4)WebSocket握手验证反爬虫
WebSocket握手成功之后,进入消息互相推送阶段,在开发者工具Network>WS选项可以看到状态为101的请求;双方传输的数据在Messages面板(箭头朝上是客户端发送给服务器端的消息,箭头朝下,反之)
例子:乐鱼足球
2、动态渲染反爬虫
动态玩也中常见的表现形式有下拉刷新、点击切换和悬停显示等;由Javascript改变HTML DOM导致页面内容发生变化的现象称为动态渲染
(1)常见的动态渲染反爬虫案例
(2)动态渲染的通用解决办法
Selenium套件(同步请求)
异步渲染库Puppeteer(异步请求)
Splash(异步渲染服务、分布式爬虫,渲染较差)
分析javascript绑定事件
3、文本混淆反爬虫
文本混淆可以有效的避免爬虫获取Web应用中重要的文字数据,使用文本混淆限制爬虫获取文字数据的方法称为混淆反爬虫(CSS特性来实现混淆)
(1)图片伪装反爬虫
例子:使用光学字符识别(PyTesseract)(广西人才网) 图片伪装
缺陷:光学字符识别在面对扭曲文字、生僻字和有复杂干扰信息的图片时,就无法发挥作用
import io
import pytesseract
from PIL import Image
import requests
image_url = "http://www.porters.vip/confusion/phonenumber.png"
content = requests. get( image_url) . content
image_stream = Image. open ( io. BytesIO( content) )
print ( pytesseract. image_to_string( image_stream) )
(2)CSS偏移反爬虫
利用CSS样式将乱序的文字排版为非人类正常阅读顺序的行为(去哪儿票价)
例子,通过观察style="width: 16px;left:-48px"确定排序:票价偏移
CSS样式可以改变页面显示,但这种“改变”仅存在于浏览器(能够解释CSS的渲染工具)中,即使借助渲染工具,也无法获得“见到”的内容
(3)SVG映射反爬虫
SVG是用于描述二维矢量图形的一种图形格式。它基于XML描述图形,对图形进行放大或缩小操作都不会影响图形质量。(大众点评)
例子,通过class里的属性与数字形成映射关系,一一对应:美食商家评价
难点:上面例子通过class属性与数字形成映射关系,这种手段的绕过方法过于简单,对于一些复杂的网站并不适用,如大众点评的文字映射,此时需找到文字映射规律,并且能够用Python语言实现映射算法,无论目标网站文字映射的对应关系如何变化,我们都能使用这套映射算法得到正确的结果。
突破:css样式与svg对应的图片对应关系逻辑;svg_y>=int(css_y)
import requests
import re
from parsel import Selector
url_css = "http://www.porters.vip/confusion/css/food.css"
url_svg = "http://www.porters.vip/confusion/font/food.svg"
css_resp = requests. get( url_css) . text. replace( "\n" , "" ) . replace( " " , "" )
svg_resp = requests. get( url_svg) . text
css_class_name = 'vhkbvu'
pattern = re. compile ( r"vhkbvu{background:-(\d+)px-(\d+)px;}" )
coord = pattern. findall( css_resp)
if coord:
x, y = coord[ 0 ]
css_x, css_y = int ( x) , int ( y)
svg_data = Selector( svg_resp)
texts = svg_data. xpath( "//text" )
svg_y = [ i. attrib. get( 'y' ) for i in texts if css_y <= int ( i. attrib. get( 'y' ) ) ] [ 0 ]
svg_text = svg_data. xpath( f'//text[@y="{svg_y}"]/text()' ) . extract_first( )
font_size = re. search( r'font-size:(\d+)px' , svg_resp) . group( 1 )
position = css_x // int ( font_size)
number = svg_text[ position]
print ( number)
(4)字体反爬虫
开发者通过使用@font-face为网页指定字体,为用户计算机提供字体的依赖;
难点:依赖已有的字体文件,如果开发者频繁改动字体文件或准备多套字体文件并随机切换,则之前的可能失效,不再起作用
例子:猫眼评分反爬虫
WOFF(Web Open Font Format,Web开放字体格式)是一种网页所采用的字体格式标准,本质上是基于SFNT字体(如TrueType)。TrueType字体中的每个字形由网格上的一系列点描述,点是字体中的最小单位;字体文件不仅包含字形数据和点信息,还包括字符到字形映射、字体标题、命名和水平指标等,这些信息存在对应的表中:
表
作用
cmap
字符到字形映射
glyf
字形数据
head
字体标题
hhea
水平标题
hmtx
水平指标
loca
索引到位置
maxp
最大限度的
name
命名
post
后记
解决反爬方法:建议使用kNN算法,将基准字体样本与测试样本进行比较,确定字体,点击链接见思路
(5)文本混淆反爬虫的通用方法
光学字符识别OCR只能从图片中识别文字,WOFF是字体文件,SVG中的文字太多,但可以根据需求将页面中所需的部分数据截图保存,然后再用光学字符识别的手段从截图中提取文字。
import requests
import json
import base64
import pytesseract
"""由于Splash接口拒绝连接,此代码运行不起来,仅提供思路"""
render = 'http://www.porters.vip:8050/execute'
url = 'http://www.porters.vip/confusion/movie.html'
script = """
function = main(splash)
assert(splash:go('%s'))
assert(splash:wait(0.5))
-- 截取票房
total_png = splash:select('.movie-index-content.box .stonefont'):png()
return {
-- 将图片信息以键值对的形式返回
total = total_png
}
end
""" % url
header = { 'content-type' : "application/json" }
data = json. dumps( { "lua_source" : script} )
resp = requests. post( render, data= data, headers= header)
images = resp. json( )
for kye, value in images. items( ) :
image_body = base64. b64decode( value)
with open ( "1.png" , 'wb' ) as f:
f. write( image_body)
print ( pytesseract. image_to_string( "1.png" ) )
PyTesseract的优缺点:能够精确识别没有干扰信息、轮廓清晰的数字。对于模糊、有干扰因素的图片以及汉字的识别率很低;PyTesseract库能够识别的字号30px。
文字识别API: 腾讯公司推出了文字识别OCR服务 ,该服务支持印刷体、手写体及定制化场景的图片文字识别。
4、特征识别反爬虫
爬虫程序可以借助渲染工具(如selenIum,puppeteer)从动态网页中获取数据。开发者可以根据客户端是否包含浏览器驱动WebDriver这一特征来区分正常用户和爬虫程序
WebDriver检测的结果有3种,分别是true、false和undefined。当我们使用的渲染工具有webdriver属性时,navigator.webdriver的返回值就是true。反之则返回false或者undefine
(1)WebDriver识别反爬虫
示例:淘宝网 ,使用了WebDriver反爬
示例:网址 ,使用Puppeteer获取点击详情的数据,但是在网页请求时会判定为“自动化测试工具
import asyncio
from pyppeteer import launch
async def main ( ) :
browser = await launch( )
page = await browser. newPage( )
await page. goto( 'http://www.porters.vip/features/webdriver.html' )
await page. click( '.btn.btn-primary.btn-lg' )
await asyncio. sleep( 1 )
await page. screenshot( { 'path' : "webdriver.png" } )
await browser. close( )
asyncio. get_event_loop( ) . run_until_complete( main( ) )
反爬原因:js代码中使用了Navigator对象(即windows.navigator对象)的webdriver属性来判断客户端是否通过WebDriver来驱动浏览器。
绕过方法1:navigator.webdriver只适用于使用WebDriver的渲染工具,对于Splash这种使用WebKit内核开发的渲染工具来说时无效的;
绕过方法2:只要我们使用的渲染工具没有webdriver属性,就能获得目标数据。WebDriver检测的结果有3种,分别是true、false和undefined。当我们使用的渲染工具有webdriver属性时,navigator.webdriver的返回值就是true。反之则返回false或者undefine
from selenium. webdriver import Chrome
import time
browser = Chrome( )
browser. get( 'http://www.porters.vip/features/webdriver.html' )
script = 'Object.defineProperty(naviagtor, "webdriver", {get:() => false,});'
browser. execute_script( script)
time. sleep( 1 )
绕过方法3:mitmproxy过滤,客户端使用它提供的API过滤JavaScript文件中检测navigator.webdriver属性值的代码
(2)浏览器特征
除了Navigator对象的serAgent、cookieEnable、platform、plugins等属性外,Screeb对象(即window.screen对象)的一些属性也可以作为判断依据
不同渲染工具访问,WebDriver示例 会有不同的特性属性值,可以截图比较;会出现User-Agent、屏幕分辨率、CPU核心数量等不同
名称
Chrome
Splash
Puppeteer
User-Agent
Chrome
Splash
HeadlessChrome
屏幕分辨率
1920✖1080
1024✖768
800✖600(可设置)
CPU核心数量
4
1
4
浏览器指纹:比如通过判断IP地址、cookie、token、还有一些浏览器指纹(UUID、Canvas和Webgl、Fingerprint.js)限制爬虫访问频率
淘宝浏览器指纹案例
(3)爬虫特性
访问频率:单位时间内客户端向服务器端发出网络请求的次数,它是描述网络请求频繁程度的量。(正常用户浏览网页的频率不会像爬虫程序那么高,开发者可以将访问频率过高的客户端视为爬虫程序。)
(4)隐藏链接反爬虫
CSS样式隐藏了标签,所以正常情况下,用户不会点击到href中带有/details/的标签
例子:该例子页面上只看到6件商品,但爬虫程序却提取到8键商品的URL。只要客户端访问URL为/details/的接口,就将该客户端视为爬虫,并且拒绝来自该IP的请求,详情页
import requests
from parsel import Selector
from urllib. parse import urljoin
url = "http://www.porters.vip:8202/"
resp = requests. get( url)
text = Selector( resp. text)
shops = text. css( '.col-md-3 a::attr("href")' ) . extract( )
for s in shops:
detail = urljoin( url, s)
detail_resp = requests. get( detail)
print ( detail_resp. text)
5、App反爬虫
6、验证码反爬虫