网页抓取自定义请求头:2026实操指南
TL;DR: 自定义请求头通过内容协商、语言与地区一致性、会话维持和条件缓存,提高响应格式正确率、解析成功率并减少重复流量。最佳做法是按域名与资源类型维护最小模板,让请求头、Cookie、代理地区和客户端能力保持一致;排障时固定其他变量,依次检查最终 URL、状态码、Content-Type、重定向链和响应正文,而不是盲目复制浏览器字段。
自定义请求头解决什么问题
自定义请求头是抓取程序随 HTTP 请求发送的元数据,主要用于四类任务:
- 内容协商:用
Accept声明可解析的 HTML、JSON 或其他媒体类型; - 本地化:用
Accept-Language配合代理地区、地区 Cookie 和 URL 获取一致的语言或币种; - 会话与授权:通过
Cookie、Authorization维持已获许可的访问状态; - 增量更新:通过
If-None-Match或If-Modified-Since避免重复下载未变化资源。
请求头只表达客户端声明。修改 User-Agent 不会执行 JavaScript,也不会生成有效 Cookie 或获得账户权限;发送 Accept: application/json 也不能把只提供 HTML 的页面转换为 JSON。
自定义请求头如何提高抓取质量
减少格式错误和无效解析
公开接口同时支持 HTML 与 JSON 时,明确发送 Accept: application/json 可以减少内容协商歧义。程序仍需验证响应:如果状态码是 200,但 Content-Type 为 text/html,正文可能是登录页、错误页或限流提示,不能直接交给 JSON 解析器。
建议分别统计两项指标:
- HTTP 成功率:返回预期状态码的请求比例;
- 解析成功率:成功提取必需字段的响应比例。
例如,1,000 次请求中有 980 次返回 200,但只有 910 次包含商品编号、价格和币种,则 HTTP 成功率为 98%,有效解析率只有 91%。后一个数字更能反映抓取管道是否可用。
提高地区数据的一致性
价格、库存和税费页面通常同时受出口 IP、语言、Cookie 与 URL 路径影响。采集法国站点时,可以把法国代理、Accept-Language: fr-FR,fr;q=0.9、欧元 Cookie 和法语 URL 绑定为一个会话;如果代理位于法国,而 Cookie 仍保存美国地区设置,结果可能混合美元价格和法语文案。
应在每条结果中保存以下上下文:
- 代理国家或地区;
- 请求语言;
- 最终 URL;
- 页面显示的币种;
- 含税或未税标记;
- 会话编号和采集时间。
这些字段能区分真实价格变化与地区配置漂移。
降低重复传输成本
服务器返回 ETag 后,后续请求可发送 If-None-Match;资源未变化时,服务器通常返回 304 Not Modified,不再传输完整正文。没有 ETag 时,可以使用 Last-Modified 与 If-Modified-Since,但时间精度和服务器实现可能导致误判,因此仍应定期执行完整校验。
以 10,000 个平均 500 KB 的页面为例,完整下载约需 5 GB 流量。如果 70% 的页面未变化并返回 304,理论上可减少约 3.5 GB 正文传输;实际节省量还要扣除请求头、响应头和定期全量校验产生的流量。
建立最小且稳定的请求头模板
按“域名 + 资源类型”拆分模板,不要让 HTML 页面、JSON 接口和文件下载共用一套字段。
| 请求头 | 适用场景 | 推荐做法 | 常见错误 |
|---|---|---|---|
User-Agent | 大多数 HTTP 请求 | 使用稳定、可解释的客户端标识 | 每次请求随机更换 |
Accept | HTML 或 API 内容协商 | 只声明解析器支持的媒体类型 | 声明 JSON 后不验证响应 |
Accept-Language | 本地化页面 | 与代理地区、Cookie 和 URL 一致 | 同一会话频繁切换语言 |
Accept-Encoding | 压缩传输 | 只声明客户端可解码的格式 | 不支持 Brotli 却声明 br |
Authorization | 获得许可的 API | 从密钥管理系统动态注入 | 写入仓库或完整日志 |
Cookie | 登录或地区会话 | 在同一粘性会话内复用 | 同一 Cookie 数秒内跨国跳转 |
Referer | 站点确实依赖导航来源 | 按真实导航链设置 | 为所有请求伪造同一来源 |
If-None-Match | 基于 ETag 的增量抓取 | 与 URL 和资源版本绑定保存 | 把其他 URL 的值混用 |
不要复制浏览器中的全部请求头。浏览器请求可能包含短期令牌、设备提示、实验字段以及仅对当前导航有效的 Referer;复制后既可能泄露凭据,也可能形成相互矛盾的客户端声明。
三类模板示例
HTML_HEADERS = {
"User-Agent": "ResearchCollector/1.0 (+https://example.org/contact)",
"Accept": "text/html,application/xhtml+xml",
"Accept-Language": "zh-CN,zh;q=0.9",
}
JSON_HEADERS = {
"User-Agent": "ResearchCollector/1.0 (+https://example.org/contact)",
"Accept": "application/json",
}
FILE_HEADERS = {
"User-Agent": "ResearchCollector/1.0 (+https://example.org/contact)",
"Accept": "application/pdf,application/octet-stream;q=0.8",
}
没有请求正文的 GET 通常不需要 Content-Type。只有发送 JSON 正文的 POST、PUT 或 PATCH 请求,才应设置 Content-Type: application/json。
用响应证据验证配置
每次请求至少记录以下字段:
- 请求 URL、最终 URL和完整重定向链;
- 状态码、
Content-Type与Content-Encoding; - 响应字节数和耗时;
- 解析是否成功及缺失字段;
- 请求头模板版本;
- 代理地区、会话编号和重试次数。
诊断异常响应时,可以保存正文前 1 KB,快速识别登录页、验证码页、网关错误或限流提示。保存前必须清除令牌、邮箱、电话号码和查询参数中的敏感值。
import requests
session = requests.Session()
session.headers.update({
"User-Agent": "ResearchCollector/1.0 (+https://example.org/contact)",
"Accept": "text/html,application/xhtml+xml",
"Accept-Language": "zh-CN,zh;q=0.9",
})
url = "https://example.org/public-page"
first = session.get(
url,
timeout=(5, 20),
allow_redirects=True,
)
validator_headers = {}
if first.headers.get("ETag"):
validator_headers["If-None-Match"] = first.headers["ETag"]
elif first.headers.get("Last-Modified"):
validator_headers["If-Modified-Since"] = first.headers["Last-Modified"]
second = session.get(
url,
headers=validator_headers,
timeout=(5, 20),
allow_redirects=True,
)
if second.status_code == 304:
print("资源未变化,继续使用本地副本")
elif second.status_code == 200:
content_type = second.headers.get("Content-Type", "")
print(second.url, content_type, len(second.content))
else:
print(second.status_code, second.url)
服务器可以忽略条件请求,因此程序必须同时处理 200 和 304。ETag 与缓存验证的语义分别由 RFC 9110 和 RFC 9111 定义。
一次可复现的排障案例
某公开商品接口包含 120 个 URL。第一版采集器直接复制浏览器请求,其中混入过期 Cookie、导航专用字段和不再有效的会话标识;11 个请求被重定向到 HTML 会话页,但程序仍按 JSON 解析,最终产生 11 次异常。
排查过程分为四步:
- 固定 URL、代理出口和并发数;
- 记录最终 URL、重定向链与
Content-Type; - 删除过期 Cookie 和无关浏览器字段;
- 仅保留稳定的
User-Agent、Accept: application/json和必要授权。
调整后,120 个响应均返回预期媒体类型。第二轮复用首轮保存的 ETag,其中 83 个未变化资源返回 304;这组结果只代表该接口,但明确展示了最小模板如何同时改善可诊断性和增量传输效率。
请求头、代理与会话的边界
请求头控制应用层声明,代理决定网络出口,两者不能互相替代。无状态公开页面可以按请求轮换 IP;购物车、分页游标、地区 Cookie 或登录会话通常需要粘性出口,具体轮换粒度可参考网页抓取中的 IP 轮换实践指南。
同一登录 Cookie 不应在数秒内跨多个国家使用。更稳妥的做法是以“账户 + 国家 + Cookie 容器 + 代理会话”为绑定单元,并在会话结束后统一销毁,而不是只轮换 User-Agent。
EProxies 提供超过 7200 万个住宅 IP,覆盖 195+ 个国家和地区,支持 HTTP(S) 与 SOCKS5。平台运行时间指标为 98.2%,并由 99.9% 运行时间 SLA 支持;按量住宅代理 $0.25/GB 起,300 GB 阶梯档约 $0.73/GB,ISP SOCKS5 代理 $0.95/IP 起,无限流量方案 $79/月起。采购前应确认目标国家库存、并发上限、会话时长、计费口径和 SLA 适用范围。
按采集任务选择配置
新闻增量采集
保存文章列表页和正文页各自的 ETag 或 Last-Modified。收到 304 时沿用本地副本;收到 200 时更新正文、验证器、发布时间和采集时间,避免把缓存时间误当成文章发布时间。转载、聚合与内容权利问题可参考网页抓取如何重塑新闻分发。
公开 JSON 接口
先确认接口文档明确支持 JSON,再发送 Accept: application/json。批量任务还应记录分页游标、速率限制响应头和每页记录数;如果接口返回 200 但必需字段为空,应先检查权限、接口版本和查询参数,而不是继续增加请求头。批量接口编排可参考 API 驱动的行业报告网页抓取指南。
动态页面
初始 HTML 不含目标数据时,继续堆叠请求头通常无效。应先在浏览器网络面板中确认数据来自 XHR、Fetch、GraphQL、内嵌状态还是脚本执行;只有页面必须运行 JavaScript 才使用浏览器自动化,具体识别路径可参考使用人工智能抓取动态网站。
自定义请求头故障排查流程
先把问题归入四层:代理连接、HTTP 协议、会话授权或内容解析。每轮实验只改变一个变量,否则无法判断异常来自请求头、Cookie、代理出口还是目标站点更新。
| 现象 | 首要检查项 | 可执行动作 | 不应采取的做法 |
|---|---|---|---|
400 | 字段语法、重复字段、URL 编码、正文格式 | 导出实际发送的请求并逐字段删除 | 一次添加整套浏览器字段 |
401 | 凭据是否缺失、过期或作用域错误 | 刷新合法凭据并核对 API 权限 | 更换 User-Agent 代替认证 |
403 | 账户权限、条款、来源链和响应正文 | 停止重试并确认访问许可 | 随机请求头持续尝试 |
407 | 代理凭据、白名单和代理 URL | 单独测试代理认证 | 修改目标站点的 Accept |
429 | Retry-After、并发数和单域名频率 | 降低并发并执行退避 | 无等待立即重试 |
| HTML 代替 JSON | 最终 URL、登录状态、媒体类型 | 保存正文片段并检查重定向 | 直接交给 JSON 解析器 |
| 解压失败 | Content-Encoding 与解码器 | 删除不支持的压缩声明 | 声明客户端不支持的 br |
200 但字段为空 | JavaScript、地区 Cookie、接口版本 | 对照页面网络请求和字段模式 | 把 HTTP 成功当成数据成功 |
推荐按以下顺序执行:
- 用单个 URL、单线程和固定代理复现;
- 输出程序最终实际发送的请求头,而不是配置文件中的预期值;
- 检查最终 URL、重定向次数和响应媒体类型;
- 暂时移除 Cookie、
Referer和非必需字段; - 从最小模板开始,每次只恢复一个字段;
- 用 20 至 50 个稳定 URL 做回归测试,比较 HTTP 成功率、解析成功率和响应大小;
- 为新模板分配版本号,异常时立即回滚。
遇到 429 时优先遵循 Retry-After。没有该字段时,可使用带随机抖动的指数退避,例如约 1、2、4、8 秒,并限制最大重试次数;持续限流时应降低单域名并发,而不是只更换请求头。
安全与合规控制
API 密钥、Cookie 和授权令牌应从环境变量或密钥管理系统读取。日志只保留令牌哈希、末尾字符或内部凭据编号;共享抓包文件前,还要清除查询参数、请求正文和响应正文中的个人数据。
请求头模板可以纳入版本控制,但敏感值必须排除。每次变更应记录目标域名、字段差异、测试样本数、HTTP 成功率、解析成功率、流量变化和回滚版本。
公开可访问不等于允许无限制采集。上线前应检查站点条款、个人数据规则、数据库权利、访问频率和技术限制,并结合各国网页抓取合法性地图评估适用司法辖区。
常见问题
自定义请求头有哪些最佳实践?
按域名和资源类型使用最小、稳定且可解释的模板,只发送任务需要的字段。让请求头与 Cookie、代理地区、会话状态和本地解析能力一致,并同时监控状态码、Content-Type、重定向链与解析成功率。敏感字段应由密钥系统注入,不能写入代码或完整日志。
自定义请求头问题如何排查?
先用单个 URL、固定代理和固定 Cookie 复现,再检查程序实际发送的请求头、最终 URL、状态码、重定向链、Content-Type 和正文前 1 KB。移除 Cookie、Referer 与非必需字段,从最小模板开始每次只增加一个字段;对 429 遵循 Retry-After,对 401、403 则先核对凭据、权限和站点条款。最后用 20 至 50 个稳定 URL 回归测试,比较解析成功率而不只是 200 比例。
自定义请求头如何改善网页抓取?
它们可以请求正确的媒体类型和语言、维持合法会话,并通过 ETag 或修改时间减少重复下载。例如,将法国住宅代理、法语请求头和欧元 Cookie 绑定到同一会话,可降低语言、币种和地区库存相互冲突的概率。其效果应通过解析成功率、响应字节数、304 比例和地区字段一致性来衡量。
自定义请求头能保证抓取成功吗?
不能。请求头无法替代合法认证、JavaScript 执行、Cookie 管理、访问频率控制或稳定网络。即使返回 200,仍要检查媒体类型和必需字段是否存在。
是否应该复制浏览器中的全部请求头?
不应该。浏览器请求可能包含短期令牌、设备提示、实验字段和当前导航专用字段,复制后可能泄露凭据或造成字段冲突。先建立最小模板,再根据接口文档和响应证据逐项增加字段。
Accept 和 Content-Type 有什么区别?
Accept 声明客户端希望接收的响应媒体类型,Content-Type 描述当前消息正文的媒体类型。没有请求正文的 GET 通常不需要设置 Content-Type;发送 JSON 正文时才使用 Content-Type: application/json。
遇到 403 是否应该继续增加请求头?
不应该。先检查响应正文、账户权限、站点条款、真实导航链路和请求频率,并在确认访问许可前停止自动重试。随机追加字段不会获得授权,反而会掩盖真正的拒绝原因。
自定义请求头能替代住宅代理吗?
不能。请求头表达客户端偏好,住宅代理提供特定地区的网络出口。多地区采集需要共同管理代理、语言、Cookie 和会话;代理本身不会赋予账户权限,请求头也不能改变出口 IP。
本文由 EProxies 团队撰写,经内部质量标准核查与人工审核后发布。