
本文详解如何通过设置关键请求头(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免费学习笔记(深入)”;









