
本文详解如何通过设置关键请求头(user-agent 和 accept-language)并配合流式下载,成功获取 adgm 等严格防护网站上的 pdf 文件,避免文件损坏或 403/406 错误。
在使用 requests 库下载 pdf 文件时,看似简单的 GET 请求常因目标网站的反爬机制而失败——表现为文件可保存但无法打开(提示“已损坏”或“不是有效的 PDF”)。根本原因往往不是网络问题,而是服务器根据请求头(如 Accept-Language、User-Agent)进行内容协商或访问控制。以阿布扎比全球市场(ADGM)官网为例,其 PDF 资源明确要求同时提供 User-Agent 和 Accept-Language 头,缺一不可;仅设 User-Agent 仍会返回空响应或 html 错误页,导致二进制内容错乱。
以下为推荐的健壮下载方案,采用流式读取(stream=True)、分块写入和异常校验,确保大文件稳定下载且完整性可控:
import requests PDF_FILENAME = "alpha-development-middle-east-ltd-penalty-notice-redacted.pdf" BASE_URL = "https://www.adgm.com/documents/operating-in-adgm/ongoing-obligation/enforcement/" # 构建完整 URL(参数分离,提升可读性与可维护性) url = f"{BASE_URL}{PDF_FILENAME}" params = { "la": "en", "hash": "5EA2DA7D1492D105375580EEF2FB088F" } headers = { "User-Agent": "Mozilla/5.0 (macintosh; Intel Mac OS X 14_3_1) appleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 safari/605.1.15", "Accept-Language": "en-GB,en;q=0.9,en-US;q=0.8,pt;q=0.7" } chunk_size = 32 * 1024 # 32KB 每次读取,平衡内存与 I/O 效率 with requests.get(url, headers=headers, params=params, stream=True) as response: response.raise_for_status() # 自动抛出 HTTPError(如 403、404、500) with open(PDF_FILENAME, "wb") as f: for chunk in response.iter_content(chunk_size=chunk_size): if chunk: # 过滤空 chunk(如 keep-alive 短连接) f.write(chunk)
关键要点说明:
✅ 必须包含 Accept-Language:ADGM 等政府/监管类网站常依据该头判断用户区域与语言偏好,缺失将触发服务端拒绝响应(返回 406 Not Acceptable 或伪造 HTML)。
✅ stream=True + iter_content() 是安全实践:避免将整个 PDF 加载进内存,尤其对百 MB 级文件至关重要;同时支持断点续传逻辑扩展。
✅ response.raise_for_status() 不可省略:它能立即捕获 HTTP 错误状态码,防止静默写入无效响应体(例如返回的 403 页面 HTML 被当成 PDF 写入,造成“损坏”假象)。
⚠️ 注意 User-Agent 的真实性:避免使用过于陈旧或明显爬虫特征的 UA(如 python-requests/2.x),建议模拟主流浏览器最新版本,并保持 UA 与 Accept-Language 语义一致(如 en-GB 对应英国区 Safari)。
若仍失败,可进一步检查:
- 使用浏览器开发者工具 → Network 面板,复制真实下载请求的完整 Headers(含 cookie、Referer 等);
- 添加 timeout=(3.05, 27) 防止卡死(连接超时 3.05s,读取超时 27s);
- 对于需登录或 Token 的资源,优先考虑 requests.session() 维持会话状态。
此方法已在 ADGM、FCA、MAS 等多个监管机构官网验证有效,兼顾兼容性与鲁棒性,是生产环境 PDF 下载的推荐范式。
立即学习“Python免费学习笔记(深入)”;