做爬虫踩过的坑,记一下
2026-06-12 19:07
做爬虫踩过的坑,记一下
前阵子接了个电商价格监控的活,需求不复杂:定时抓某平台商品价格和库存。但这家的反爬有点东西,踩了几个坑,记下来免得忘。
============================================================
一、别省请求头
============================================================
很多人觉得加个 UA 就完事了。这家会静默检查请求头完整性。
Resync 在《Bot Detection 101》里提过:现代反爬不只是看 UA,而是看整个 TLS 指纹和 HTTP 头的组合偏差。浏览器发出来的头是什么样,你就得是什么样。少一个 Sec-Ch-Ua,返回的就是空白页——不报错,不弹验证码,就是空。这种"什么都不说"的反爬反而最难定位。
session = requests.Session()
session.headers.update({
"User-Agent": "...",
"Accept": "text/html,application/xhtml+xml,"
"application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Sec-Ch-Ua": "\"Chromium\";v=\"124\"",
"Sec-Ch-Ua-Platform": "\"Windows\"",
})
============================================================
二、JS 生成的 Cookie
============================================================
首次访问页面会用 JS 在本地跑一段指纹逻辑,生成一个 cookie。不带这个 cookie 的话,后面所有请求都给你甩验证码。
思路:Playwright 开个 headless 浏览器,等 JS 跑完,把 cookie 倒出来给 requests 用。有效期两三个小时,定时刷新就行。
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://目标站.com",
wait_until="networkidle")
cookies = page.context.cookies()
browser.close()
============================================================
三、参数签名——卡最久的地方
============================================================
搜索接口有个 sign 参数,32 位小写 hex,一看就是 MD5。但原文是什么?
翻压缩 JS 找到加密那段:
var sign = md5(JSON.stringify(params)
+ "&key=" + _0x3f2a)
_0x3f2a 往回追,发现是从一个配置接口返回的。所以流程是:先调配置接口拿 key → 组装参数 → md5 → 带 sign 发请求。
Python 复现的时候有个细节卡了很久:
import hashlib
import json
def build_sign(params: dict, secret_key: str) -> str:
raw = (json.dumps(params, separators=(",", ":"))
+ "&key=" + secret_key)
return hashlib.md5(raw.encode()).hexdigest()
注意 separators=(",", ":")。JS 的 JSON.stringify 默认不带空格,Python 的 json.dumps 默认带。两边算出来的 sign 不一样,排查了好久。
Intoli 的《The State of Web Scraping》总结过:爬虫工程大部分时间花在逆向和对抗上,真正写采集逻辑的代码反而是少数。确实是这样。
============================================================
四、频率和假数据
============================================================
这家单 IP 每分钟大概 30 次请求到顶。超了不封你,给你假数据——价格全显示 0,库存全显示有货。比直接封更恶心,因为你不会立刻发现。
import time
import random
def safe_request(url, params):
time.sleep(2 + random.uniform(0, 1.5))
resp = session.get(url, params=params)
if all(item["price"] == 0
for item in resp.json()["items"]):
raise ValueError("疑似被风控,数据异常")
return resp.json()
Cloudflare 2024 年的 bot management 报告提到:越来越多的站点从"硬阻断"转向"软对抗"——给你假数据或者降级数据,而不是直接弹验证码。所以爬虫必须自己做数据校验。
============================================================
五、总结
============================================================
请求头校验 → 浏览器发什么你发什么
JS生成Cookie → Playwright 预热后导出
参数签名 → 逆向 JS 再用 Python 复现
频率限制 → 随机延时加代理池
假数据 → 自己加数据校验
写爬虫最大的感受:代码本身不难,难的是搞清楚对方到底用了什么手段。很多坑不在技术上,在你能不能对着 Network 面板,一笔一笔把完整请求链路还原出来。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
目前接一些爬虫、数据采集的私活,有电商数据、价格监控、批量采集需求的可以聊聊。
引用:
Resync —— Bot Detection 101
Intoli —— The State of Web Scraping
Cloudflare —— 2024 Bot Management Report
浏览
1前阵子接了个电商价格监控的活,需求不复杂:定时抓某平台商品价格和库存。但这家的反爬有点东西,踩了几个坑,记下来免得忘。
============================================================
一、别省请求头
============================================================
很多人觉得加个 UA 就完事了。这家会静默检查请求头完整性。
Resync 在《Bot Detection 101》里提过:现代反爬不只是看 UA,而是看整个 TLS 指纹和 HTTP 头的组合偏差。浏览器发出来的头是什么样,你就得是什么样。少一个 Sec-Ch-Ua,返回的就是空白页——不报错,不弹验证码,就是空。这种"什么都不说"的反爬反而最难定位。
session = requests.Session()
session.headers.update({
"User-Agent": "...",
"Accept": "text/html,application/xhtml+xml,"
"application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Sec-Ch-Ua": "\"Chromium\";v=\"124\"",
"Sec-Ch-Ua-Platform": "\"Windows\"",
})
============================================================
二、JS 生成的 Cookie
============================================================
首次访问页面会用 JS 在本地跑一段指纹逻辑,生成一个 cookie。不带这个 cookie 的话,后面所有请求都给你甩验证码。
思路:Playwright 开个 headless 浏览器,等 JS 跑完,把 cookie 倒出来给 requests 用。有效期两三个小时,定时刷新就行。
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://目标站.com",
wait_until="networkidle")
cookies = page.context.cookies()
browser.close()
============================================================
三、参数签名——卡最久的地方
============================================================
搜索接口有个 sign 参数,32 位小写 hex,一看就是 MD5。但原文是什么?
翻压缩 JS 找到加密那段:
var sign = md5(JSON.stringify(params)
+ "&key=" + _0x3f2a)
_0x3f2a 往回追,发现是从一个配置接口返回的。所以流程是:先调配置接口拿 key → 组装参数 → md5 → 带 sign 发请求。
Python 复现的时候有个细节卡了很久:
import hashlib
import json
def build_sign(params: dict, secret_key: str) -> str:
raw = (json.dumps(params, separators=(",", ":"))
+ "&key=" + secret_key)
return hashlib.md5(raw.encode()).hexdigest()
注意 separators=(",", ":")。JS 的 JSON.stringify 默认不带空格,Python 的 json.dumps 默认带。两边算出来的 sign 不一样,排查了好久。
Intoli 的《The State of Web Scraping》总结过:爬虫工程大部分时间花在逆向和对抗上,真正写采集逻辑的代码反而是少数。确实是这样。
============================================================
四、频率和假数据
============================================================
这家单 IP 每分钟大概 30 次请求到顶。超了不封你,给你假数据——价格全显示 0,库存全显示有货。比直接封更恶心,因为你不会立刻发现。
import time
import random
def safe_request(url, params):
time.sleep(2 + random.uniform(0, 1.5))
resp = session.get(url, params=params)
if all(item["price"] == 0
for item in resp.json()["items"]):
raise ValueError("疑似被风控,数据异常")
return resp.json()
Cloudflare 2024 年的 bot management 报告提到:越来越多的站点从"硬阻断"转向"软对抗"——给你假数据或者降级数据,而不是直接弹验证码。所以爬虫必须自己做数据校验。
============================================================
五、总结
============================================================
请求头校验 → 浏览器发什么你发什么
JS生成Cookie → Playwright 预热后导出
参数签名 → 逆向 JS 再用 Python 复现
频率限制 → 随机延时加代理池
假数据 → 自己加数据校验
写爬虫最大的感受:代码本身不难,难的是搞清楚对方到底用了什么手段。很多坑不在技术上,在你能不能对着 Network 面板,一笔一笔把完整请求链路还原出来。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
目前接一些爬虫、数据采集的私活,有电商数据、价格监控、批量采集需求的可以聊聊。
引用:
Resync —— Bot Detection 101
Intoli —— The State of Web Scraping
Cloudflare —— 2024 Bot Management Report
评论
