案例背景
假設我們需要爬取一家內部測試系統的動態數據API接口。該系統前端頁面使用了復雜的JavaScript混淆技術來防止接口被直接調用,同時對請求參數進行了加密簽名。另外,登錄環節帶有圖形驗證碼用于防護。我們的目標是:
- 分析JavaScript代碼,逆向加密簽名算法。
- 模擬登錄過程,自動識別圖形驗證碼并提交。
- 構造正確請求參數,獲取動態數據。
- 完整實現Python爬蟲,穩定批量抓取數據。
環境準備
- Python 3.8+
- 主要依賴庫:
- requests (HTTP請求)
- execjs (調用JavaScript引擎)
- Pillow & pytesseract(驗證碼圖像處理與OCR)
- jsbeautifier(JS格式化輔助閱讀)
- lxml(HTML解析)
- selenium & webdriver-manager(動態交互及驗證碼抓取,可選)
Step 1:分析網頁結構和JavaScript代碼
模擬環境下,打開前端頁面,按F12打開開發者工具:
- 頁面HTML框架簡單,核心數據通過POST提交參數調用?
/api/v1/getData
?接口,返回JSON。 - POST請求中的參數均為加密后的簽名串,且請求頭帶有特殊字段?
X-Custom-Token
。 - 登錄頁帶有斷碼的圖形驗證碼,圖片URL是?
/captcha/image
,驗證碼刷新時參數帶時間戳。
1.1 網絡數據初探
使用Chrome Network面板,關注XHR請求:
POST https://internal.test/api/v1/getData
Request Payload:
{"param": "EncryptedStringHere","sign": "GeneratedSignature"
}
Response:
{"code": 0,"data": [ ... ]
}
1.2 找到加密簽名函數
通過Sources面板,加載執行的JS文件(例如main.min.js
,經過混淆壓縮),使用jsbeautifier進行格式化,定位請求相關代碼片段。
逆向發現關鍵函數generateSign(params)
:
function generateSign(params) {var a = btoa(encodeURIComponent(JSON.stringify(params)));var b = someObfuscatedFunction(a);return md5(b + secretKey);
}
大致邏輯是:
- 將參數JSON字符串化,編碼成URI,再做Base64編碼。
- 經過部分混淆函數處理(
someObfuscatedFunction
)。 - 最后加上固定密鑰用MD5加密。
Step 2:JavaScript逆向與關鍵代碼還原
2.1 還原混淆函數
原始混淆函數結構類似:
function someObfuscatedFunction(str) {var res = '';for (var i = 0; i < str.length; i++) {res += String.fromCharCode(str.charCodeAt(i) ^ 123); // 按位異或123}return res;
}
這是典型的異或加密,解密時再次異或同樣的數字即可還原。
2.2 Python實現等效簽名函數
利用Python的base64
,hashlib
和自定義異或函數實現:
import base64
import hashlib
import urllib.parsesecret_key = 'FixedSecretKey123' # 從JS中提取的密鑰def xor_str(s, key=123):return ''.join(chr(ord(c) ^ key) for c in s)def generate_sign(params):# JSON序列化import jsonparam_str = json.dumps(params, separators=(',', ':'), ensure_ascii=False)# URI編碼encoded = urllib.parse.quote(param_str)# Base64編碼b64 = base64.b64encode(encoded.encode('utf-8')).decode('utf-8')# 異或處理xor_result = xor_str(b64)# 計算MD5簽名sign_str = xor_result + secret_keymd5_hash = hashlib.md5(sign_str.encode('utf-8')).hexdigest()return md5_hash
Step 3:驗證碼識別技術
3.1 獲取驗證碼圖片
驗證碼在登錄過程中返回,URL例子:
https://internal.test/captcha/image?_t=時間戳
3.2 圖片預處理與OCR識別
驗證碼為簡單斷碼數字。使用Pillow處理,pytesseract識別。
示例預處理代碼:
from PIL import Image, ImageFilter
import pytesseract
import io
import requestsdef get_captcha(session):url = f'https://internal.test/captcha/image?_t={int(time.time()*1000)}'response = session.get(url)img = Image.open(io.BytesIO(response.content))# 灰度化img = img.convert('L')# 二值化img = img.point(lambda x: 0 if x < 140 else 255, '1')# 去噪img = img.filter(ImageFilter.MedianFilter())return imgdef recognize_captcha(img):text = pytesseract.image_to_string(img, config='--psm 7 digits')text = text.strip().replace(' ', '')return text
Step 4:登錄流程模擬
登錄時需提交用戶名、密碼、驗證碼。
def login(session, username, password):# 獲取驗證碼并識別captcha_img = get_captcha(session)captcha_text = recognize_captcha(captcha_img)login_data = {'username': username,'password': password,'captcha': captcha_text,}response = session.post('https://internal.test/api/login', data=login_data)result = response.json()if result['code'] == 0:print('登錄成功')else:print('登錄失敗:', result['msg'])raise Exception('登錄失敗')
Step 5:核心數據接口調用
構造請求參數,調用數據接口:
def fetch_data(session, params):sign = generate_sign(params)post_data = {'param': params, # 通常是原始參數JSON對象,部分實現會轉換為字符串,請按實際情況調整'sign': sign,}headers = {'X-Custom-Token': 'token-from-cookie-or-js', # 需要通過登錄等動態獲取'Content-Type': 'application/json',}resp = session.post('https://internal.test/api/v1/getData', json=post_data, headers=headers)data = resp.json()if data['code'] == 0:return data['data']else:raise Exception(f"接口調用失敗: {data['msg']}")
Step 6:整體爬蟲流程整合
import requests
import timedef main():session = requests.Session()# 登錄login(session, 'test_user', 'test_password')# 偽裝UA頭session.headers.update({'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \(KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'})# 請求示例參數params = {'type': 'recent', 'limit': 20, 'timestamp': int(time.time())}# 調用接口data = fetch_data(session, params)print('獲取數據:', data)if __name__ == '__main__':main()
總結
本案例通過模擬環境設計,綜合講述了下一些高級Python爬蟲技術:
- JavaScript逆向:理解并還原混淆及加密算法核心。
- 簽名構造:使用Python重現JS簽名邏輯,成功通過接口認證。
- 圖形驗證碼識別:圖像預處理加OCR,自動突破登錄驗證。
- 會話管理:使用requests.Session維護登錄態。
- 爬蟲實戰:整合流程實現自動登錄、數據抓取的完整爬蟲。