使用requests爬取cardmarket网站持续返回403响应的问题求助
原因分析
- Cloudflare防护机制:cardmarket采用的Cloudflare防护不仅校验请求头,还会验证客户端是否具备真实浏览器的核心特征——比如能否执行JavaScript、生成Canvas指纹、匹配标准TLS握手参数等。
requests是纯HTTP请求库,无法模拟这些浏览器行为,因此直接被拦截返回403。 - 代理有效性不足:你用
httpbin.org验证代理仅能确认代理可正常转发请求,但Cloudflare会检测代理IP的信誉度——免费代理池中的IP大多已被Cloudflare标记为恶意爬虫IP,即便能通过httpbin,也无法绕过Cloudflare的验证。 - 代码逻辑错误:请求cardmarket后尝试调用
response.json(),但该网站返回的是HTML页面而非JSON格式,这会直接触发异常,导致你无法看到实际的响应内容(比如Cloudflare的验证页面)。
解决方案
1. 用支持JS渲染的工具替代requests
使用Playwright或Selenium模拟真实浏览器,它们能执行Cloudflare要求的JavaScript验证,自动完成人机校验并获取有效Cookie与页面内容。
示例(Playwright):
from playwright.sync_api import sync_playwright with sync_playwright() as p: # 先禁用无头模式,直观查看Cloudflare验证过程 browser = p.chromium.launch(headless=False) page = browser.new_page() # 模拟浏览器导航行为,触发Cloudflare验证 page.goto("https://www.cardmarket.com/en/Magic") # 等待页面加载完成,确保验证通过 page.wait_for_load_state("networkidle") # 获取完整页面HTML html_content = page.content() print(html_content) browser.close()
2. 优化请求头与Cookie管理(仅适用于Cloudflare验证较松的场景)
如果坚持使用requests,需做到:
- 从真实浏览器中复制完整请求头(包括
Accept、Accept-Encoding、Accept-Language、Referer、User-Agent等),确保与浏览器请求头完全一致; - 手动在浏览器中完成Cloudflare验证,然后复制浏览器的Cookie(如
__cf_bm、cf_clearance)到请求中:
import requests headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", # 补充其他从浏览器复制的请求头字段 } cookies = { # 从浏览器开发者工具的Application->Cookies中复制Cloudflare相关Cookie "__cf_bm": "xxx", "cf_clearance": "xxx" } response = requests.get("https://www.cardmarket.com/en/Magic", headers=headers, cookies=cookies) print(response.status_code) print(response.text[:1000])
3. 更换高质量代理
放弃免费代理,选择住宅代理(Residential Proxy)——这类IP来自真实用户设备,IP信誉度高,更容易通过Cloudflare检测。使用时确保代理支持HTTPS,且每次请求更换IP。
4. 修正代码逻辑错误
请求cardmarket后,不要直接调用response.json(),先检查响应状态码并打印部分内容排查问题:
try: response = requests.get(url, headers=headers, proxies=proxies, timeout=2) print(f"状态码: {response.status_code}") # 打印响应前1000字符,判断是否为Cloudflare验证页面 print(response.text[:1000]) except Exception as e: print(f"代理{proxy_url}失败: {str(e)}")
内容的提问来源于stack exchange,提问作者ahoone
相关产品推荐
相关产品推荐

