python常見反爬蟲類型(信息校驗型、動態渲染、文本混淆、特徵識別反爬蟲等)

注:筆記大部分來源書本,僅供學習交流:【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
# 1、獲取css屬性是vhkbvu對應的座標值
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)
    # 2、獲取svg標籤對應的y值,規則是svg_y>=css_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]
    # 3、根據svg_y確定具體的text的標籤
    svg_text = svg_data.xpath(f'//text[@y="{svg_y}"]/text()').extract_first()
    # 4、確認SVG中的文字大小
    font_size = re.search(r'font-size:(\d+)px', svg_resp).group(1)
    # 5、得到css樣式vhkbvu屬性映射svg的位置
    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接口拒絕連接,此代碼運行不起來,僅提供思路"""
# 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"}
# 按照Splash規定提交命令
data = json.dumps({"lua_source": script})
# 向Splash接口發出請求並攜帶上請求頭和命令參數
resp = requests.post(render, data=data, headers=header)
# 將Splash返回結果賦值給
images = resp.json()
for kye, value in images.items():
    # Splash返回的圖片使用了base64進行編碼,所以我們需要解碼
    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')
# 編寫修改navigator.webdriver值的Javascript代碼
script = 'Object.defineProperty(naviagtor, "webdriver", {get:() => false,});'
# 運行JavaScript代碼
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)
    
# p30's data,you get.
# p30pro's data,you get.
# Got a spider.
# Got a spider.
# Got a spider.
# Got a spider.
# Got a spider.
# Got a spider.

5、App反爬蟲

6、驗證碼反爬蟲

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章