with one click
web-scraping-bypass
爬虫绕过网站反爬机制的实战技巧合集 — 请求头伪装、TLS指纹、代理轮换、频率控制、JS渲染、验证码绕过、字体反爬等
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
爬虫绕过网站反爬机制的实战技巧合集 — 请求头伪装、TLS指纹、代理轮换、频率控制、JS渲染、验证码绕过、字体反爬等
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
| name | web-scraping-bypass |
| description | 爬虫绕过网站反爬机制的实战技巧合集 — 请求头伪装、TLS指纹、代理轮换、频率控制、JS渲染、验证码绕过、字体反爬等 |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| platforms | ["linux","wsl","macos"] |
| metadata | {"hermes":{"tags":["web-scraping","anti-bot","crawler","bypass"],"related_skills":["web-to-documents"]}} |
遇到反爬,按以下优先级递进尝试:
1. web_extract 工具(最简单,先试试)
2. curl + 完整浏览器 headers(静态页面,见我 headers 清单)
3. 搜索引擎找镜像/转载(绕过反爬)
4. 浏览器工具(JS 渲染页面用)
5. 代理 + 频率控制(IP 被限时用)
6. 专项对抗(字体反爬/验证码/Cloudflare 等)
curl -sL --max-time 15 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8" \
-H "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8" \
-H "Accept-Encoding: gzip, deflate, br" \
-H "Cache-Control: no-cache" \
-H "Sec-Ch-Ua: \"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"" \
-H "Sec-Ch-Ua-Mobile: ?0" \
-H "Sec-Ch-Ua-Platform: \"Windows\"" \
-H "Sec-Fetch-Dest: document" \
-H "Sec-Fetch-Mode: navigate" \
-H "Sec-Fetch-Site: none" \
-H "Sec-Fetch-User: ?1" \
-H "Upgrade-Insecure-Requests: 1" \
-H "Referer: https://www.google.com/" \
--compressed \
"URL"
| Header | 作用 | 容易被忽略 |
|---|---|---|
User-Agent | 浏览器标识 | 太旧的 UA 会被识别为爬虫 |
Sec-Ch-Ua | 客户端品牌信息 | 很多站点靠这个判断是不是真浏览器 |
Sec-Fetch-* 系列 | 请求来源/模式/目的 | 新浪博客缺了必 418 |
Accept-Encoding: gzip | 告诉服务器可以解压 | 缺了可能返回乱码 |
Referer | 从哪里来的 | 有些站校验收 Referer |
Accept-Language | 语言偏好 | 中文站缺了这个容易被打回 |
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.230 Mobile Safari/537.36",
]
注意:UA 要搭配匹配的 Sec-Ch-Ua 和 Sec-Ch-Ua-Platform。
成功请求 → 随机延迟 1-3 秒 失败(429/503) → 指数退避:等待 2^n 秒(n=1,2,3...),最大 60 秒 连续失败 → 切 IP 再重试
import time, random
def smart_delay(base_min=1.0, base_max=3.0):
time.sleep(random.uniform(base_min, base_max))
def exponential_backoff(attempt, max_wait=60):
wait = min(2 ** attempt + random.uniform(0, 1), max_wait)
time.sleep(wait)
不要固定等 2 秒——固定间隔更容易被识别。
curl -x http://proxy_ip:port URL
curl -x http://user:pass@proxy_ip:port URL
import random, requests
PROXY_LIST = ["http://proxy1:port", "http://proxy2:port", ...]
def fetch_with_proxy(url, headers=None):
proxy = {"http": random.choice(PROXY_LIST), "https": random.choice(PROXY_LIST)}
try:
return requests.get(url, headers=headers, proxies=proxy, timeout=15)
except (requests.ConnectionError, requests.Timeout):
return None
服务器通过 TLS 握手参数生成哈希值识别客户端。curl 和 Python requests 的默认指纹容易被识别。
实战建议: 目标站检测 TLS 指纹时(Cloudflare、Akamai),直接上 browser 工具,别折腾 curl 伪装。
# 获取 Cookie
curl -c /tmp/cookies.txt -L "https://example.com/login"
# 带着 Cookie 访问
curl -b /tmp/cookies.txt -L "https://example.com/protected"
import requests
session = requests.Session()
session.headers.update({"User-Agent": "..."})
resp = session.get("https://example.com/protected")
判断方法: curl 抓 HTML,body 只有 <div id="root"> 或 <script> 没有可见文字 → JS 渲染。
处理方案(按推荐顺序):
原因:请求头不完整。对策:加完整 Sec-Fetch* headers + --compressed。
原因:请求太频繁。对策:降频 + 随机延迟 + 切 IP。
原因:IP 黑名单 / Referer 检查。对策:切 IP、检查 Referer。
原理:自定义字体映射字符到不同 Unicode。对策:下载 woff,用 fonttools 解析映射,或 browser 渲染后取最终文字。
最简单对策:browser 工具渲染后提取。
文字→OCR,滑块→模拟轨迹+browser,reCAPTCHA→浏览器+人工过一次。 实战:能走搜索绕过就不要搞验证码。
web_search("文章标题")
web_search("site:原始域名 关键字")
找到转载/镜像 URL 再去抓。很多站的反爬只防自己的域名。
已验证的转载/聚合站: 见 references/aggregator-sites.md。包括 tophub.today(最全)、readep.com(知乎)、rebang.today 等。
网站的"排行榜"或"今日热文"往往是历史总点击排名,不是真的今日内容。要找真正的新文章,用日期搜索:
# 找2026年的内容
web_search("site:blog.sina.com.cn 2026年")
# 找具体月份
web_search("site:blog.sina.com.cn 2026年5月")
# 找具体日期
web_search('"2026年5月21日" site:blog.sina.com.cn')
通用原则: 不要相信网站排行榜的"今日"标签。用搜索引擎的时间过滤来验证内容时效性。
| 站点 | 反爬类型 | 绕过难度 | 推荐方案 |
|---|---|---|---|
| 新浪博客 | Header检测(418) | 低 | 完整Sec-Fetch* headers |
| 知乎 | zse-ck JS挑战 + 登录验证 | 中 | 搜索引擎找转载站(readep.com / tophub.today) |
| 微信公众号 | 封闭生态 | 高 | 搜索转载 |
| BOSS直聘 | 字体反爬 + 登录 | 高 | 浏览器渲染 + 字体映射 |
| 豆瓣 | 限频严格 | 中 | 低频率 + 代理轮换 |
| 小红书 | 签名加密 + 登录 | 高 | 浏览器 + Cookie |
| 淘宝(搜索热门榜) | 重度JS渲染 + App封闭 | 高 | 第三方聚合(tophub.today 淘宝热卖) |
| 百度百科 | 基本无 | 低 | web_extract 直接可用 |
| Twitter/X | 限频 | 低 | xurl skill |
开始 → web_extract → curl+完整headers → 搜索引擎找转载
JS渲染? → browser工具
字体反爬? → 下载woff解析
需要登录? → 加Cookie
限频? → 代理+降频
都不行 → 搜API接口(SPA常见隐藏JSON API)
最后一关:验证数据真实性
检查内容日期是否真的是"今日"
生成HTML/文件 → 给用户检验
你用 curl+完整headers 成功抓取的内容,原文链接在浏览器中仍然打不开(反爬还在)。用户点原文链接只会看到 418/403/空白页,容易被误解为"你胡编数据"。
正确做法:
新浪博客的正文在 class="articalContent" 的 div 中。需要用嵌套 div 计数法来找到正确的闭合标签:
import re
def extract_artical_content(html):
"""从新浪博客HTML中提取正文"""
m = re.search(r'class="articalContent[^"]*"[^>]*>', html)
if not m:
return ""
start = m.end()
depth = 0
pos = start
while pos < len(html):
if html[pos:pos+5] == '</div' and (pos+5 >= len(html) or html[pos+5] in '> \t\r\n'):
if depth == 0:
return html[start:pos]
depth -= 1
pos += 5
elif html[pos:pos+4] == '<div' and (pos+4 >= len(html) or html[pos+4] in '> \t\r\n'):
depth += 1
pos += 4
else:
pos += 1
return ""
# 清理HTML标签
text = re.sub(r'<br\s*/?>', '\n', content)
text = re.sub(r'<[^>]+>', '', text)
text = re.sub(r' ', ' ', text)
抓取新浪博客文章时,检查 <title> 标签:
山_古天乐_新浪博客)→ 文章有效新浪博客 → 文章已失效/404<title></title> → 文章不存在不要在所有失效文章上浪费时间,快速诊断后跳过。
新浪博客排行页面(/lm/rank/)返回的内容通过 web_extract 会有编码乱码问题(标题和正文均为 garbled text)。但其中的文章 URL 是有效的。直接从排行页面提取 URL,然后用完整 headers 逐个抓取单篇文章获取真实内容。
site:域名 年月)验证内容时效性。交付数据前先检查内容日期。