원클릭으로
web-scraping-bypass
爬虫绕过网站反爬机制的实战技巧合集 — 请求头伪装、TLS指纹、代理轮换、频率控制、JS渲染、验证码绕过、字体反爬等
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
爬虫绕过网站反爬机制的实战技巧合集 — 请求头伪装、TLS指纹、代理轮换、频率控制、JS渲染、验证码绕过、字体反爬等
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| 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:域名 年月)验证内容时效性。交付数据前先检查内容日期。