零、寫在前面
本文涉及的反爬技術,僅供個人技術學習,禁止並做到:
- 干擾被訪問網站的正常運行
- 抓取受到法律保護的特定類型的數據或信息
- 搜集到的數據禁止傳播、交給第三方使用、或者牟利
- 如有可能,在爬到數據后24小時候內刪除
具體可參考 2019年5月28號 頒布的 《數據安全管理辦法(征求意見稿)》
一、背景
今天在爬另一家網站數據時,想直接從 ajax 接口入手,但是發現這些 request 加了額外參數來防止爬取,即在 request header 里,有一對隨機的 key-value
參數:形如 e931588bc0dfbc5e6323 : c43dfe7cdc49b6318f43907ad4e7d9b69a23719d2e3b7b59799124408aa11cf383f459a4a558af8c64b289b7d974982aad58db705ac6784460733bd21784bde0
,故意讓你猜不到。
但是操作了一會,我總結了如下規律:
1、每個不同的接口 url,對應不同的 key ,但這個 key 刷新頁面是不會變得。(看來 key 跟 url 有關)
2、每個 key 對應的 value 一直在變。(事后才知道 value 是根據 url 和 post body 共同決定的)
二、破解
於是通過網站被混淆過的 js ,耐心的打斷點分析。
過程略。
三、結果
最后發現:
key 和 value 都是通過 HMAC(Keyed-Hashing for Message Authentication)
算法得來的。
1、HMAC 算法
HMAC 其實就等於我們平常用的 MD5 / SHA-1 去加 salt 的操作。而采用 HMAC 的好處就是,替代我們自己的 salt 算法,使得程序算法更標准化,也更安全。
HMAC 可選擇搭配 MD5 / SHA-1 或等等。這里用的是 SHA512
。
>>> import hmac
>>> key = b'secret'
>>> message = b'Hello, world!'
>>> h = hmac.new(key, message, digestmod='MD5')
>>> h.hexdigest()
'fa4ee7d173f2d97ee79022d1a7355bcf'
注意:傳入的 key 和 message 都是bytes
類型,所以str
類型需要首先被編碼為bytes
。
2、最終代碼
注意:敏感信息被隱藏處理。
import hashlib
import hmac
import requests
import json
host = 'http://example.com'
# request's param - 變化值
uri = '/api/search/example'
data = {
"cityName": "上海",
"cityCode": "31",
# 等等
}
# hmac's secret - 固定值
secret = b'abcdefg'
# calculate key
sign_key = hmac.new(secret, uri.lower().encode(), hashlib.sha512).hexdigest()
header_key = sign_key[10:30]
# calculate value
sign_value = (uri.lower()+uri.lower() +
json.dumps(data, separators=(',', ':'), ensure_ascii=False)).lower()
header_value = hmac.new(secret, sign_value.encode(),
hashlib.sha512).hexdigest()
# print result
print(header_key, header_value)
然后我們把算出來的 key 和 value 塞入到每一次請求的 header 里,即可成功調用。
3、坑
上面代碼在算 value 的時候,用了 json.dumps()
,把中文 上海
轉為了 "\u4e0a\u6d77"
,而不是上海
,導致接口一直報錯。
解決辦法:json.dumps 多傳一個參數 ensure_ascii=False
。