爬虫总被封 IP 怎么办?
写爬虫的朋友应该都有过这样的经历:代码跑得好好的,突然开始出现 403、验证码,甚至直接 Connection Refused。换了一批代理 IP,过了几个小时又封了。
2026 年了,网站的反爬手段早就不是简单的"封 IP"那么简单。如果你还在用"被封就换 IP"的思路,很可能一直在做无用功。下面从根因出发,给出完整的解决方案。
先搞清楚:你的 IP 是怎么被识别的?
网站识别爬虫,通常有以下几个维度,按检测难度从低到高排列:
第一层:IP 基础信息
- 数据中心 IP:云服务器、托管机房的 IP 段是公开的,网站直接查 ASN 就能判断
- 代理 IP 黑名单:很多网站维护了已知代理 IP 的黑名单(包括免费代理、部分商业代理)
- IP 访问频率:同一个 IP 短时间内大量请求,明显不是人类行为
解决方式:使用住宅代理或隧道代理,避开数据中心 IP 段。
第二层:HTTP 请求特征
- User-Agent:使用默认的 Python-requests、curl 等 UA,一眼就是爬虫
- 缺少常见 Header:真实浏览器请求会携带 Accept、Accept-Language、Referer 等,爬虫经常遗漏
- Cookie 异常:没有正常浏览流程产生的 Cookie,直接访问目标接口
解决方式:完整模拟浏览器请求头,使用 requests.Session 保持 Cookie 状态。
第三层:TLS/HTTP2 指纹
这是 2026 年越来越常见的检测方式。不同的客户端(Chrome、Firefox、Python-requests、Go http)在 TLS 握手时的特征是不同的。网站通过 JA3/JA4 指纹就能判断你的请求来自什么客户端。
即使你完美模拟了 HTTP 请求头,如果 TLS 指纹暴露了你是 Python-requests 而不是 Chrome,依然会被识别。
解决方式:使用 TLS 指纹伪装库(如 curl_cffi、tls-client),或者直接用浏览器自动化工具。
第四层:浏览器指纹
对于使用 Selenium/Playwright 等自动化工具的爬虫,网站会通过 JavaScript 检测:
- Canvas 指纹
- WebGL 渲染特征
- 字体列表
- navigator.webdriver 标记
- 鼠标/键盘行为模式
解决方式:使用反检测浏览器(如 undetected-chromedriver)或 Playwright 的 stealth 插件。
第五层:行为分析
最高级的反爬会分析你的操作行为:
- 页面停留时间(爬虫通常是 0 秒)
- 滚动行为(爬虫一般不滚动)
- 鼠标轨迹(爬虫没有鼠标)
- 请求时序(爬虫的请求间隔过于规律)
解决方式:在自动化脚本中加入随机延迟、模拟滚动和鼠标移动。
实战:从被封到稳定的完整方案
下面以一个典型的电商数据采集场景为例,展示完整的解决方案。
第一步:解决 IP 问题
选择隧道代理而不是自建 IP 池。原因很简单:
自建 IP 池 = 找代理源 + 验证有效性 + 管理轮换 + 处理失效
隧道代理 = 配一个地址,后端自动搞定一切
以 Python + requests 为例:
import requests
# 隧道代理配置(以悟空代理为例)
proxy_url = "http://tunnel.wukongdaili.com:端口"
proxies = {
"http": proxy_url,
"https": proxy_url
}
response = requests.get(
"https://目标网站/商品页面",
proxies=proxies,
timeout=10
)
隧道代理的核心优势:每次请求自动分配不同的出口 IP,无需手动管理。
第二步:处理 TLS 指纹
如果目标网站使用 Cloudflare、Akamai 等 WAF,普通的 requests 可能直接返回 403。这时候需要用 curl_cffi 来伪装 TLS 指纹:
from curl_cffi import requests
# 模拟 Chrome 120 的 TLS 指纹
response = requests.get(
"https://目标网站/商品页面",
proxies=proxies,
impersonate="chrome120", # 关键:伪装 TLS 指纹
timeout=10
)
impersonate 参数会自动处理 TLS 握手、HTTP2 设置、请求头排序等所有细节,让你的请求在 TLS 层面和真实 Chrome 浏览器无法区分。
第三步:控制请求节奏
即使 IP 和指纹都处理好了,请求频率过高依然会触发风控。建议:
import time
import random
def crawl_with_delay(url, max_retries=3):
for attempt in range(max_retries):
try:
# 随机延迟 2-8 秒,模拟人类浏览节奏
time.sleep(random.uniform(2, 8))
response = requests.get(url, proxies=proxies, timeout=10)
if response.status_code == 200:
return response
elif response.status_code == 429:
# 触发限流,等待更长时间
wait = random.uniform(10, 30)
print(f"触发限流,等待 {wait:.1f} 秒")
time.sleep(wait)
else:
print(f"请求异常: {response.status_code}")
except Exception as e:
print(f"请求失败: {e}")
return None
几个关键的节奏控制原则:
- 单次请求间隔:2-8 秒(随机)
- 同 IP 同站点请求间隔:建议 10 秒以上
- 遇到 429 限流:等待 10-30 秒再重试
- 连续失败 3 次:暂停 1-5 分钟
第四步:处理动态渲染
现在很多网站的内容是 JavaScript 动态加载的,直接请求 HTML 拿不到数据。这种情况有两种方案:
方案 A:逆向 API(推荐) 用浏览器开发者工具找到实际的数据接口,直接请求 API 而不是渲染页面。速度快、资源消耗低。
方案 B:浏览器自动化 当 API 难以逆向时,用 Playwright 控制真实浏览器:
from playwright.sync_api import sync_playwright
def crawl_with_browser(url):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(
proxy={"server": "http://tunnel.wukongdaili.com:端口"},
user_agent="Mozilla/5.0 ..."
)
page = context.new_page()
# 模拟人类行为
page.goto(url, wait_until="networkidle")
page.mouse.move(100, 200)
page.wait_for_timeout(random.uniform(1000, 3000))
content = page.content()
browser.close()
return content
排查清单:被封了怎么办?
当你的爬虫突然被封,按以下清单逐一排查:
- 当前 IP 是否被目标网站封禁?(换一个 IP 测试)
- User-Agent 是否合理?(不能是默认的 python-requests)
- 是否缺少关键请求头?(Accept、Accept-Language、Referer 等)
- TLS 指纹是否暴露?(用 curl_cffi 测试)
- 请求频率是否过高?(加入随机延迟)
- 是否触发了行为检测?(模拟鼠标移动和页面停留)
- 代理 IP 本身是否已被标记?(换一个代理服务商测试)
总结
2026 年的反爬技术已经从"封 IP"进化到"多维度识别",解决方案需要覆盖 IP 层、请求特征层、TLS 指纹层、浏览器指纹层和行为层。
建议的做法是:先用隧道代理解决 IP 问题,再用 curl_cffi 处理 TLS 指纹,最后配合合理的请求节奏。这套组合拳能解决 90% 以上的封禁问题。
如果你的场景更复杂(比如需要处理复杂的验证码、或者目标网站的反爬特别激进),可以留言讨论,我会在评论区补充具体方案。