Python3爬蟲:狀態(tài)碼521網(wǎng)頁的爬取

一、問題發(fā)現(xiàn)

近期我在做代理池的時候,發(fā)現(xiàn)了一種以前沒有見過的反爬蟲機制。當我用常規(guī)的requests.get(url)方法對目標網(wǎng)頁進行爬取時,其返回的狀態(tài)碼(status_code)為521,這是一種以前沒有見過的狀態(tài)碼。再輸出它的爬取內容(text),發(fā)現(xiàn)是一些js代碼??磥硎切聠栴},我們來探索一下。

狀態(tài)碼和爬取內容.png

二、原理分析

打開Fiddler,抓取訪問網(wǎng)站的包,我們發(fā)現(xiàn)瀏覽器對于同一網(wǎng)頁連續(xù)訪問了兩次,第一次的訪問狀態(tài)碼為521,第二次為200(正常訪問)??磥砭W(wǎng)頁加了反爬蟲機制,需要兩次訪問才可返回正常網(wǎng)頁。

Fiddler抓包信息.png

下面我們來對比兩次請求的區(qū)別:

521請求:

521請求.png

200請求:

200請求.png

通過對比兩次請求頭,我們發(fā)現(xiàn)第二次訪問帶了新的cookie值。再考慮上面程序對爬取結果的輸出為js代碼,可以考慮其操作過程為:第一次訪問時服務器返回一段可動態(tài)生成cookie值的js代碼;瀏覽器運行js代碼生成cookie值,并帶cookie重新進行訪問;服務器被正常訪問,返回頁面信息,瀏覽器渲染加載。

三、解決流程

弄清楚瀏覽器的執(zhí)行過程后,我們就可以模擬其行為通過python作網(wǎng)頁爬取。操作步驟如下:

  1. 用requests.get(url)獲取js代碼

  2. 通過正則表達式對代碼進行解析,獲得JS函數(shù)名,JS函數(shù)參數(shù)和JS函數(shù)主體,并將執(zhí)行函數(shù)eval()語句修改為return語句返回cookie值

  3. 調用execjs庫的executeJS()功能執(zhí)行js代碼獲得cookie值

  4. 將cookie值轉化為字典格式,用requests.get(url, cookies = cookie)方法獲取得到正確的網(wǎng)頁信息

四、代碼實現(xiàn)

實現(xiàn)程序所需要用到的庫:

import re                  #實現(xiàn)正則表達式
import execjs              #執(zhí)行js代碼
import requests            #爬取網(wǎng)頁

第一次爬取獲得包含js函數(shù)的頁面信息后,通過正則表達式對代碼進行解析,獲得JS函數(shù)名,JS函數(shù)參數(shù)和JS函數(shù)主體,并將執(zhí)行函數(shù)eval()語句修改為return語句返回cookie值。

# js_html為獲得的包含js函數(shù)的頁面信息
# 提取js函數(shù)名
js_func_name = ''.join(re.findall(r'setTimeout\(\"(\D+)\(\d+\)\"', js_html))

# 提取js函數(shù)參數(shù)
js_func_param = ''.join(re.findall(r'setTimeout\(\"\D+\((\d+)\)\"', js_html))

# 提取js函數(shù)主體
js_func = ''.join(re.findall(r'(function .*?)</script>', js_html))

將執(zhí)行函數(shù)eval()語句修改為return語句返回cookie值

# 修改js函數(shù),返回cookie值
js_func = js_func.replace('eval("qo=eval;qo(po);")', 'return po')

調用execjs庫的executeJS()功能執(zhí)行js代碼獲得cookie值

# 執(zhí)行js代碼的函數(shù),參數(shù)為js函數(shù)主體,js函數(shù)名和js函數(shù)參數(shù)
def executeJS(js_func, js_func_name, js_func_param):
    jscontext = execjs.compile(js_func)    # 調用execjs.compile()加載js函數(shù)主體內容
    func = jscontext.call(js_func_name,js_func_param)  # 使用call()通過函數(shù)名和參數(shù)執(zhí)行該函數(shù)
    return func
cookie_str = executeJS(js_func, js_func_name, js_func_param)

將cookie值轉化為字典格式

# 將cookie值解析為字典格式,方便后面調用
def parseCookie(cookie_str):
    cookie_str = cookie_str.replace("document.cookie='", "")
    clearance = cookie_str.split(';')[0]
    return {clearance.split('=')[0]: clearance.split('=')[1]}
cookie = parseCookie(cookie_str)

獲得cookie后,采用帶cookie的方式重新進行爬取,即可獲得我們需要的網(wǎng)頁信息了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容