注:筆記大部分來源書本,僅供學習交流:【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、驗證碼反爬蟲