前阵子帮团队爬一个技术文档网站,刚发第一个请求就被拦下——页面显示“Checking your browser before accessing xxx.com”,5秒后自动刷新,接着返回403。一看响应头有“Server: cloudflare”,就知道撞上了Cloudflare的反爬盾。
试了常规操作:换User-Agent、加Cookie、用代理,全没用;换成Selenium,刚启动浏览器就被识别为自动化工具;甚至用了Playwright默认配置,依然卡在5秒盾。最后花了一周研究Cloudflare的检测逻辑,才搞明白:它不仅看IP和Cookie,更会深度检测TLS握手指纹和浏览器环境特征,普通爬虫的“伪装”在它面前就像没穿衣服。
这篇就把实战方案拆解出来:从Cloudflare的检测原理,到用Python模拟真实浏览器的TLS指纹和环境特征,每一步都附代码和调试技巧,亲测能突破80%的Cloudflare反爬场景。
一、先搞懂:Cloudflare凭什么认出你是爬虫?
Cloudflare的反爬(尤其是“5秒盾”)是目前最严的反爬机制之一,它的核心逻辑不是“一刀切”,而是通过多层检测判断你是“真人浏览器”还是“爬虫”:
| 检测层 | 检测内容 | 普通爬虫的破绽 |
|---|---|---|
| TLS握手层 | 检测客户端的TLS协议版本、加密套件、扩展字段等(即TLS指纹) | requests/urllib的TLS配置固定,和真实浏览器差异大(比如加密套件顺序不同) |
| JS挑战层 | 执行一段复杂JS,检测是否能正常运行(含DOM API、定时器、Canvas指纹等) | 纯requests无法执行JS;Selenium/Playwright默认配置有自动化标记(如navigator.webdriver) |
| 行为模式层 | 检测请求频率、页面交互(如鼠标移动)、Cookie变化等 | 爬虫请求间隔均匀、无交互、Cookie固定不变 |
最狠的是,这三层检测是“与”的关系——只要有一层不通过,就会被拦。比如很多人以为换个代理就能过,其实TLS指纹不对,用住宅代理也白搭。
二、核心破局点:TLS指纹模拟(比换IP更重要)
TLS指纹是Cloudflare的“第一道防线”,也是最容易被忽略的点。普通Python爬虫用的requests库,其TLS握手参数和真实浏览器差异极大,Cloudflare一眼就能认出。
1. 为什么requests永远过不了Cloudflare?
打开Wireshark抓包对比就会发现:
真实Chrome的TLS握手会发送特定的加密套件顺序(比如优先ECDHE-ECDSA-AES128-GCM-SHA256)、扩展字段(如ALPN协商http/1.1和h2);而requests用的urllib3库,TLS配置是固定的(比如加密套件顺序不同,扩展字段少),形成了独特的“爬虫指纹”。
Cloudflare的服务器端有一个“浏览器TLS指纹库”,一旦你的指纹不在库中,直接拦截,连JS挑战都不给你发。
2. 用curl_cffi模拟浏览器TLS指纹(亲测有效)
要绕过TLS检测,必须让爬虫的TLS握手参数和真实浏览器完全一致。推荐用库——它基于libcurl,能模拟Chrome/Firefox等浏览器的TLS指纹,比requests更像“真实浏览器”。
curl_cffi
安装curl_cffi
pip install curl_cffi # 支持Python 3.7+
核心代码:模拟Chrome的TLS指纹发送请求
from curl_cffi import requests # 注意:不是普通的requests
# 目标网站(被Cloudflare保护)
url = "https://被Cloudflare保护的网站.com"
# 模拟Chrome 112浏览器的TLS指纹和请求头
response = requests.get(
url,
impersonate="chrome112", # 关键!指定模拟的浏览器版本
headers={
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Upgrade-Insecure-Requests": "1",
# 不用手动加User-Agent,impersonate会自动生成对应浏览器的UA
},
timeout=30
)
print("状态码:", response.status_code)
print("页面内容:", response.text[:500]) # 打印前500字符
为什么这样能过?
会让curl_cffi:
impersonate="chrome112"
使用Chrome 112的TLS协议版本(TLS 1.3);发送和Chrome完全一致的加密套件顺序、扩展字段;自动生成对应版本的User-Agent(比如)。
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
实测对大多数Cloudflare保护的网站(非I’m Under Attack Mode),这样就能直接绕过TLS检测,返回200。
三、突破JS挑战:用Playwright深度模拟浏览器环境
如果网站启用了Cloudflare的“I’m Under Attack Mode”(最严模式),仅过TLS层还不够,会触发JS挑战——页面会执行一段复杂JS,检测你的浏览器是否“真实可用”。
此时需要能执行JS且无自动化标记的浏览器环境,Playwright比Selenium更合适(它的自动化特征更少,且能深度修改浏览器属性)。
1. JS挑战检测什么?(避坑关键)
Cloudflare的JS挑战会检测:
是否为
navigator.webdriver(Selenium默认会暴露);
true等浏览器内置对象是否完整;Canvas指纹、WebGL指纹是否符合真实浏览器;JS执行速度(爬虫的JS引擎可能比真实浏览器慢)。
window.chrome
2. Playwright配置:隐藏自动化特征,通过JS挑战
核心代码:修改浏览器属性,模拟真实用户
from playwright.sync_api import sync_playwright
import time
def bypass_cloudflare(url):
with sync_playwright() as p:
# 1. 配置Chrome,隐藏自动化特征
browser = p.chromium.launch(
headless=False, # 调试时用False,生产可用True(但部分网站会检测无头模式)
args=[
"--disable-blink-features=AutomationControlled", # 关键:禁用自动化标记
"--no-sandbox",
"--disable-setuid-sandbox",
# 模拟真实屏幕分辨率(避免默认小窗口被识别)
"--window-size=1280,720"
]
)
# 2. 创建新页面,进一步隐藏特征
page = browser.new_page(
viewport={"width": 1280, "height": 720}, # 匹配窗口大小
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
)
# 3. 加载前注入JS,覆盖自动化相关属性
page.add_init_script("""
// 覆盖navigator.webdriver
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
// 补全window.chrome对象(部分网站检测)
window.chrome = {
runtime: {},
// 模拟真实Chrome的属性
loadTimes: () => ({ connectionInfo: 'h2' })
};
// 移除playwright的全局变量
delete window.__playwright_evaluate_function__;
""")
# 4. 访问目标URL,处理5秒盾
page.goto(url)
# 5. 等待JS挑战完成(最多等30秒)
try:
# 检测是否出现"验证成功"的标志(如标题变化)
page.wait_for_url("**", timeout=30000) # 等待页面跳转或刷新完成
print("JS挑战通过!")
# 模拟人类行为(可选,进一步降低风险)
page.mouse.move(100, 200) # 移动鼠标
time.sleep(1)
page.mouse.click(100, 200) # 点击页面
# 获取通过验证后的页面内容
content = page.content()
return content
except Exception as e:
print("JS挑战失败:", e)
return None
finally:
browser.close()
# 测试:访问被Cloudflare严格保护的网站
if __name__ == "__main__":
page_content = bypass_cloudflare("https://被严格保护的网站.com")
if page_content:
print("页面内容前500字符:", page_content[:500])
关键配置解析(缺一不可)
1.** 禁用AutomationControlled **:
Chrome的参数会关闭自动化检测开关,让
--disable-blink-features=AutomationControlled返回
navigator.webdriver(默认会返回
undefined)。
true
2.** 补全浏览器对象 **:
真实Chrome有等内置对象,Playwright默认没有,需要手动注入JS补全,否则Cloudflare的JS会检测到“浏览器不完整”。
window.chrome
3.** 避免无头模式 **:
部分网站会检测是否包含“HeadlessChrome”,调试时用
navigator.userAgent,生产环境如需无头,可修改User-Agent去掉“Headless”字样。
headless=False
4.** 模拟人类行为**:
加鼠标移动、点击等操作,让行为模式更像真人——Cloudflare会分析页面交互,完全无交互的请求容易被标记为爬虫。
四、组合拳:TLS指纹+浏览器模拟+优质代理
对付严格的Cloudflare,单靠某一项技术不够,需要三者结合:
TLS指纹一致:用curl_cffi或Playwright确保TLS握手和目标浏览器一致;浏览器环境真实:用Playwright修改自动化特征,通过JS挑战;IP质量高:用住宅代理(而非数据中心代理),避免IP在Cloudflare的黑名单中。
实战代码:代理+Playwright+TLS模拟
from playwright.sync_api import sync_playwright
def with_proxy_and_fingerprint(url, proxy):
"""
结合代理、TLS指纹和浏览器模拟
:param url: 目标URL
:param proxy: 代理配置,格式"http://user:pass@host:port"
"""
with sync_playwright() as p:
# 配置浏览器和代理
browser = p.chromium.launch(
headless=False,
args=[
"--disable-blink-features=AutomationControlled",
f"--proxy-server={proxy}" # 设置代理
]
)
# 创建页面,配置指纹
page = browser.new_page(
viewport={"width": 1280, "height": 720},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
)
# 注入JS隐藏特征
page.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
window.chrome = {runtime: {}};
""")
# 访问并等待挑战完成
page.goto(url)
page.wait_for_timeout(5000) # 等5秒盾刷新
print("页面标题:", page.title())
browser.close()
# 测试:使用住宅代理(替换成你的代理)
if __name__ == "__main__":
proxy = "http://用户名:密码@代理IP:端口" # 推荐用BrightData、Soax等住宅代理
with_proxy_and_fingerprint("https://严格保护的网站.com", proxy)
五、避坑指南:90%的人会踩这些坑
TLS指纹和UA不一致
比如用却手动设置Firefox的User-Agent,会被Cloudflare检测到“指纹矛盾”,直接拦。解决:让TLS模拟版本和UA完全匹配(curl_cffi会自动生成对应UA,别手动改)。
impersonate="chrome112"
代理IP被污染
很多免费代理或低价数据中心代理,早已被Cloudflare拉黑,即使指纹正确也会被拦。解决:用付费住宅代理(如BrightData),它们的IP更像真实用户IP。
请求频率太快
即使通过了挑战,短时间内发大量请求(比如1秒10次),Cloudflare会触发“行为异常”检测,重新拦截。解决:加随机延迟(3-5秒),模拟人类浏览节奏。
无头模式被识别
用时,Chrome的User-Agent会包含“HeadlessChrome”,部分网站会针对性拦截。解决:手动修改UA,去掉“Headless”,比如:
headless=True
page.set_extra_http_headers({"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"})
JS执行环境不完整
Cloudflare的JS挑战会调用、
document.createElement等API,Playwright默认环境可能缺失部分API。解决:用最新版本的Playwright(定期
canvas.toDataURL),确保JS引擎完整。
playwright install --update
六、合规提醒:突破Cloudflare≠合法爬取
最后必须强调:
Cloudflare的反爬机制受法律保护,未经允许突破其保护爬取数据,可能违反《计算机信息网络国际联网安全保护管理办法》;商用场景下,应通过网站提供的API接口获取数据,或联系网站方获得授权;个人学习测试时,需控制爬取频率,不影响网站正常运行。
总结:Cloudflare反爬的本质是“验证真实性”
对抗Cloudflare的核心不是“破解”,而是“证明你是真实浏览器”——TLS指纹要像,浏览器环境要真,行为模式要自然。新手不用一开始追求完美,先用curl_cffi过简单网站,再用Playwright+代理挑战严格模式,多调试抓包对比真实浏览器的请求特征,慢慢就能找到规律。
如果遇到更复杂的情况(比如Cloudflare的Turnstile验证码),可以结合打码平台(如2Captcha)处理,但成本会更高。评论区可以交流你遇到的Cloudflare反爬场景,咱们一起拆解~


















暂无评论内容