住宅代理是按吉字节计费的,所以在按 GB 的套餐上,你的带宽就是你的账单。问题在于,一个粗放的抓取器会浪费掉它所付费的大部分字节,下载它从不看的图片、字体、视频和跟踪脚本,重新抓取没有变化的页面,并在一个简单的 HTTP 请求就够用时去渲染完整的浏览器。好消息是:这些浪费几乎都很容易削减,一个调优良好的抓取器,对于同样的数据,其带宽用量通常还不到一个未调优抓取器的一半。
这是一份做到这一点的实用指南。九条策略,大致按影响力排序,让你通过你的住宅代理采集同样的数据,却只为其中一小部分字节付费。它是按带宽计费的住宅代理所讲的按 GB 转变在成本效率上的姊妹篇。
1. 非必要不渲染浏览器
这是单条最大的杠杆。一个无头浏览器会下载页面所引用的一切:图片、CSS、网页字体、视频、分析脚本、广告脚本。对于大多数抓取,这些你一样都不需要。如果你想要的数据在初始 HTML 里,或可通过 API 触达(下一节),就完全跳过浏览器,改用一个简单的 HTTP 客户端,requests、httpx 或 Scrapy。一个 HTTP GET 的字节,可以只是一次完整浏览器渲染所拉取字节的极小一部分。
把浏览器留给那些真正需要执行 JavaScript 才能产出数据的页面。拿不准时,先抓原始 HTML,检查你的目标值是否已经在里面。
2. 打 JSON API,而不是 HTML
许多站点是从一个后端 JSON 端点渲染其内容的,页面在加载后会去调用它。直接抓取那个端点,而不是完整的 HTML 页面及其资源,往往只是一小部分字节,还能给你干净、结构化、无需解析的数据。打开你浏览器的网络(Network)标签,找到返回数据的那个请求(通常是一个返回 JSON 的 XHR/fetch),然后经由代理直接调用它。一个精简的 JSON 响应,可以替代一个沉重的页面加上一次浏览器渲染。
3. 当你必须用浏览器时,拦掉重型资源类型
当一个页面确实需要渲染时,别让浏览器下载你不会用的东西。Playwright 和 Puppeteer 都让你可以拦截请求,在图片、媒体、字体、以及往往还有样式表触及代理之前把它们中止掉,这通常能把带宽削减掉远超一半。在 Playwright 中:
await context.route("**/*", (route) => { const type = route.request().resourceType(); if (["image", "media", "font"].includes(type)) { return route.abort(); // 从不下载,从不计费 } return route.continue();});保留 document、script、xhr 和 fetch;其余的丢掉。同样的模式以及 Chromium 的细节,见如何在 Playwright 中使用住宅代理。
4. 保持压缩开启
文本大约能压缩 5 到 10 倍,所以一个经 gzip 或 brotli 编码的 HTML 响应,只是原始大小的一小部分。大多数 HTTP 客户端默认会发送 Accept-Encoding: gzip, deflate, br 并透明地解压,但要确认你没有把它禁用,且永远不要把这个头去掉。如果你在手动设置头,把它加上:
headers = {"Accept-Encoding": "gzip, br", "User-Agent": "..."}穿过代理的字节是压缩后的那些,所以这是对你被计费量的一次直接、免费的削减。
5. 用条件请求,让未变化的页面几乎不花钱
对任何重复性的爬取,别盲目地重新下载页面。HTTP 条件请求让服务器可以用一个极小的 304 响应告诉你”什么都没变”,而不是重新发送整个响应体。存下每次抓取的 ETag 或 Last-Modified,并在下次回传:
# 重新抓取时,发回存下的校验符headers = {"If-None-Match": stored_etag} # 或 If-Modified-Sincer = client.get(url, headers=headers)if r.status_code == 304: pass # 未变化:几个字节,而不是整页相比一整页,一个 304 几乎不花钱,而在一个大多数页面很少变化的监测任务里,仅这一条就能大幅削减带宽。
6. 不要重新抓取没有变化的东西
相关,但比 HTTP 校验符更宽泛:追踪你已经采集过的东西,并跳过它。用 sitemap 的 lastmod 时间戳来找出变化的 URL,为每个页面保留一个内容哈希、跳过对相同内容的再处理,并对你的爬取前沿(crawl frontier)去重,这样一次运行里就不会两次抓取同一个 URL。最便宜的吉字节,是你从不下载的那个。
7. 只取你需要的字节
如果你只需要一个文件的头部或它的第一块(一次元数据检查、一次 content-type 嗅探),就别把整个东西下载下来。一个 HEAD 请求返回头部而无正文,而一个 Range 请求在服务器支持时只拉取你指定的字节:
client.head(url) # 只有头部,没有正文client.get(url, headers={"Range": "bytes=0-2047"}) # 只有前 2 KB当你的爬取触及大型媒体、PDF 或下载、而你只需要其中一小部分或只需要元数据时,这一点最要紧。
8. 让重试和地理定位变得高效
每一次失败的尝试仍然会花掉带宽,所以浪费性的重试会悄悄推高你的账单。做退避(backoff)并给重试设上限,而不是猛击一个失败的 URL;避免过紧的地理过滤(国家 + 城市 + ASN 一起上),当没有匹配的空闲 IP 时它会让请求失败并重试,改为放宽一个过滤条件。设置合理的超时,好让一个卡住的响应不至于拖累;并让你的会话策略与任务相匹配,这样你就不会不必要地重新建立流程。高效的请求就是便宜的请求。
9. 度量每条记录的字节
你无法削减你不度量的东西。追踪每个任务的带宽,更好的是,追踪每条成功提取的记录所用的字节,浪费就会自己显形:一个每行花掉数兆字节的页面,通常是在拉取你本可以拦掉的资源,或是在你本可以打 API 的地方做了渲染。把它记录下来,观察趋势,先优化最严重的那些。
关于负责任地行事的一点说明
削减带宽不只是对你更便宜,也对你所采集的站点更轻:更少、更小的请求,意味着对它们服务器更少的负载。这让成本效率与良好公民行为相一致。继续采集公开数据,遵守每个站点的条款和速率限制,并让效率来减小你的足迹。我们的可接受使用政策是 Shifter 上何为允许的权威依据。
常见问题
经由代理抓取时,什么最耗带宽? 完整的浏览器渲染,遥遥领先,因为它会下载页面上的每一张图片、字体、视频和脚本。削减渲染(或在你必须渲染时拦掉重型资源类型),是你能做的影响力最大的改动。
拦掉图片和字体会不会弄坏抓取? 通常不会,你很少需要它们来做数据提取。测试一下站点是否把目标内容懒加载在图片后面;对绝大多数任务,拦掉图片、媒体和字体请求是安全的,并能大幅削减带宽。
住宅代理会对失败或被封的请求计费吗? 穿过代理的字节都是可计费的,所以失败的尝试和重试仍然会花掉带宽。这正是为什么高效的重试、合理的超时,以及避免过紧的地理过滤(它会造成失败),对你的账单很要紧。
打一个站点的 JSON API 被允许吗? 调用页面自身所使用的一个公开端点是常见做法,也高效得多,但站点的条款和速率限制仍然适用。负责任地采集,并对任何不确定的事项获取法律意见。
我实际上能省多少? 因目标而异,但跳过不必要的渲染、拦掉重型资源、以及不重新抓取未变化的页面,对于同样提取出的数据,通常能把总带宽削减掉一半以上。
底线
在按 GB 的套餐上,带宽效率就是成本效率,而大多数抓取器都留下了很多可省之处:在本可以直接抓取时去渲染、下载它们从不读取的资源、以及重新拉取从未变化的页面。把这份清单做一遍,能不用浏览器就不用、去打 API、拦掉重型资源、保持压缩开启、用条件请求、不重新抓取未变化的页面、只取你需要的、让重试变高效、并去度量,于是同样的数据会以其中一小部分的字节回到你手里。
这份效率会与池的质量叠加:干净的 IP 意味着更少的封锁和重试,也就是更少浪费的字节,所以在调优你的客户端之余,值得去为抓取选对代理。在定价页面查看按 GB 的套餐,然后把一个调优良好的抓取器指向住宅 gateway,看着字节数往下掉。