快速回答
我可以通过住宅代理运行多少并发请求?
根据每个目标网站的承受能力:CrawlRelay 按住宅代理流量 GB 计费(50 GB 起US$0.50/GB),而非按线程或连接计费。使用 Little 定律计算并发请求数,飞行中的请求 = 每秒页面数 × 每页面秒数,然后为每个主机设定上限,初始值为2到4。遇到429或503响应时降低并发。支持加密货币支付,无需 KYC。
并发一览
| 计费方式 | 按流量 GB 计费;线程和连接不计费 |
|---|---|
| 规模规则 | 飞行中的请求 = 吞吐量 × 延迟(Little 定律) |
| 每主机起始值 | 2–4 并行请求;小型网站每请求加延迟 |
| 减速信号 | 429、503响应、响应时间上升、挑战页面 |
| 带宽计算 | 每秒页面数 × 每页面字节数 × 3,600 = 每小时字节数 |
| 住宅代理中位响应时间 | 代理网络 0.93 秒,真实目标 3.21 秒(Proxyway 2026) |
如何根据所需吞吐量确定并发规模?
使用 Little 定律:飞行中请求数等于目标速率乘以每个请求所需时间。Proxyway 2026 年代理市场研究显示,住宅代理访问真实目标的中位请求时间为 3.21 秒,因此规划时以 2 到 4 秒计算真实网站,而非仅考虑网络的亚秒级响应。
| 目标 | 每请求时间 | 飞行中请求数 |
|---|---|---|
| 每秒 5 页 | 2 秒 | 10 |
| 每秒 20 页 | 2 秒 | 40 |
| 每秒 20 页 | 4 秒 | 80 |
| 每秒 100 页 | 3 秒 | 300 |
然后将请求分布于不同主机。分布到 200 个站点上的 40 个飞行请求是温和的;40 个请求集中在一个小站点则是其未授权的负载测试。
该吞吐量使用多少带宽?
将每秒页面数乘以每页面字节数。以每秒 20 个 HTML 页面,每页 50 KB 计算,爬取速度为每秒 1 MB 即每小时 3.6 GB,约每日 86 GB。若每渲染页为 2 MB,则同速率带宽为每秒 40 MB。
用结果乘以计划中每 GB 价格,US$0.50/GB 起始 50 GB 为 CrawlRelay,即可算出每小时费用。带宽计算器指南 提供按页面类型的典型页面大小。
每主机限额应从多少开始?
| 目标 | 每主机并行请求数 | 请求间延迟 | 备注 |
|---|---|---|---|
| 小型网站(博客、本地商店) | 1 | 2–5 秒 | 若存在则遵守 Crawl-delay 中的 robots.txt |
| 中等网站 | 2–4 | 0.5–2 秒 | 监控响应时间;当响应时间翻倍时降低请求速度 |
| 拥有 CDN 的大型平台 | 4–16 | 自动节流或自适应 | 仅在 429 和 5xx 率保持平稳时增加速度 |
| 公共 API | 文档中说明的限制 | 如文档所述 | 使用服务商提供的 API 密钥和限制 |
如何自动调整并发数?
使用加法增加和乘法减少,这也是 TCP 处理拥塞的规则:在一系列正常响应后加一个槽位,遇到 429 或 503 时减半限制。以下限速器为每个主机维护一个限制,对该主机的每个请求暂停 Retry-After 延迟,重试一次,并且绝不超过您设置的上限。网络错误作为返回值出现,所以一次超时不会中断整个批次。
import asyncio
import random
from urllib.parse import urlsplit
import httpx
PROXY = "http://USERNAME-country-us:[email protected]:7777"
class AdaptiveLimit:
"""One per host: +1 slot after 20 clean responses, halve on 429/503, pause for Retry-After."""
def __init__(self, start=2, low=1, high=16):
self.limit, self.low, self.high = start, low, high
self.in_flight, self.clean = 0, 0
self.pause_until = 0.0
self.cond = asyncio.Condition()
async def __aenter__(self):
loop = asyncio.get_running_loop()
while True:
async with self.cond:
await self.cond.wait_for(lambda: self.in_flight < self.limit)
delay = self.pause_until - loop.time()
if delay <= 0:
self.in_flight += 1
return self
await asyncio.sleep(delay) # the host asked for a pause: wait outside the lock
async def __aexit__(self, *exc):
async with self.cond:
self.in_flight -= 1
self.cond.notify_all()
def record(self, status, retry_after=None):
if status in (429, 503):
self.limit, self.clean = max(self.low, self.limit // 2), 0
wait = int(retry_after) if retry_after and retry_after.isdigit() else 5 + random.random()
now = asyncio.get_running_loop().time()
self.pause_until = max(self.pause_until, now + wait)
elif status < 400:
self.clean += 1
if self.clean >= 20 and self.limit < self.high:
self.limit, self.clean = self.limit + 1, 0
gates = {}
def gate_for(url):
host = urlsplit(url).hostname
if host not in gates:
gates[host] = AdaptiveLimit()
return gates[host]
async def fetch(client, url, attempts=2):
gate = gate_for(url)
status = None
for _ in range(attempts):
try:
async with gate:
r = await client.get(url)
except httpx.HTTPError as exc:
return "error: %s" % type(exc).__name__
status = r.status_code
gate.record(status, r.headers.get("Retry-After"))
if status not in (429, 503):
break # the retry, if any, waits for the host's pause in __aenter__
return status
async def main(urls):
timeout = httpx.Timeout(30.0, connect=10.0)
async with httpx.AsyncClient(proxy=PROXY, timeout=timeout) as client:
print(await asyncio.gather(*(fetch(client, u) for u in urls)))
print({host: gate.limit for host, gate in gates.items()})
asyncio.run(main(["https://example.com/"] * 10))代理爬取除了目标主机外,什么会导致速度变慢?
- TLS 握手。 每次请求建立一个新连接,且每次请求都需要一个新 IP,这会增加与目标网站的握手次数。当 IP 多样性不重要时,对同一网站的多页请求保持粘性会话和长连接可以避免多次握手。
- 慢出口。 住宅代理和 4G 移动代理是真实的家庭和移动网络链接。使用超时设置并在新连接上重试,而不是等待。
- 范围太窄。 一个小城市或单个 ASN 的出口比整个国家少。仅在数据发生变化的区域精准投放。
- 页面渲染。 无头浏览器消耗的 CPU、内存和流量远高于 HTTP 客户端。仅渲染需要 JavaScript 的页面。
- DNS。 通过
socks5h://或 HTTP 端口,DNS 解析发生在出口处;无需为目标主机缓存本地解析器。
并发相关问题
CrawlRelay 是否限制线程数或连接数?
计费按流量 GB 计算,不按线程、连接或请求计费,发布的套餐未设线程数限制。实际限制取决于每个目标主机的容忍度和您自身设备性能。如果计划大规模爬取,请先写信给 [email protected],以便我们核查您的目标范围。
为什么我增加线程后爬取速度反而变慢?
通常是因为目标网站开始限速:响应时间延长,出现 429 或 503 返回码,重试增加负载。另一常见原因是自己设备 CPU 或连接资源不足,尤其是使用无头浏览器时。增加线程前请先测量每个主机的延迟和错误率。
每个请求一个连接会比长连接慢吗?
会的,每次请求增加一个 TCP 和 TLS 握手,大约延迟几百毫秒,尤其是通过住宅代理出口时。这是拥有每次请求新 IP 的代价。在 IP 多样性不重要的情况下,对同一网站的多页请求使用粘性会话和长连接更快且节省流量。
在并发爬虫中如何遵守 robots.txt 的 crawl-delay?
每个主机解析 robots.txt 一次,缓存最长 24 小时,符合 RFC 9309 建议。将 Crawl-delay 值转换为单线程请求之间的最小间隔。Python 的 urllib.robotparser 可通过 crawl_delay() 访问该数据。
按流量 GB 计费实现扩展
US$0.50/GB 从 50 GB 到 1 TB,流量永不过期。