网页采集请求频率设置的直接做法是:先将单域名并发数设为 1,请求间隔设为 2~5 秒,再根据目标网站公布的限制、HTTP 429 比例、P95 响应时间、5xx 错误率和超时率逐步调节。如果网站或官方 API 已规定请求配额,应以其规定为准;如果出现持续 429、403、验证码或响应明显变慢,应立即降速或暂停。

网页采集请求频率设置教程:从限速配置到效果验证

一套可落地的配置不能只有 sleep。实际任务还应包含按域名限速、随机抖动、并发控制、连接与读取超时、有限重试、指数退避、缓存、增量采集和运行监控。

网页采集请求频率的定义

网页采集请求频率,是采集程序在一定时间内向同一目标网站或接口发送请求的数量。它通常用每秒请求数(RPS)、每分钟请求数或相邻请求间隔表示。例如,每 2 秒发送 1 次请求约等于 0.5 RPS,每 5 秒发送 1 次约等于 0.2 RPS。

设置请求频率时,需要同时区分以下三个参数:

  • 请求间隔:向同一域名发出相邻两次请求之间的等待时间。
  • 并发数:同一时刻正在执行的请求数量。
  • 超时时间:单个请求等待建立连接或读取响应的最长时间。

三者不能相互替代。例如,单个任务即使设置了 3 秒间隔,10 个工作线程仍可能在短时间内集中发出请求。因此,网页采集请求频率设置必须同时约束请求速率和并发数。

步骤一:确认采集权限与频率限制

1. 检查公开规则

访问目标网站前,先查看 robots.txt、服务条款、API 文档、版权声明和数据授权范围。robots.txt 通常位于网站根目录,例如:

步骤一:确认采集权限与频率限制
https://example.com/robots.txt

robots.txt 是网站对自动化访问规则的公开说明之一,但不能替代服务条款、合同授权或适用法律。若站点明确禁止访问某一路径,应停止对应采集。对于登录后内容、个人信息、付费内容或受访问控制保护的数据,还应确认是否已经取得必要授权。

2. 优先使用官方 API

网站提供官方 API 时,应优先使用 API。官方接口通常会明确标注每秒或每分钟配额、分页规则、剩余配额响应头以及超限后的处理方式,稳定性和可验证性通常优于直接采集网页。

3. 记录站点级配置

不要为所有网站使用同一套参数。建议为每个域名单独记录允许路径、请求间隔、并发上限、超时时间、重试次数和联系人,后续调整也应以域名为单位进行。

步骤二:配置初始请求间隔

目标站点没有公布明确限制时,可先使用以下保守参数:

  • 单域名并发数:1
  • 相邻请求间隔:2~5 秒
  • 连接超时:5~10 秒
  • 读取超时:15~30 秒
  • 单个请求最大重试次数:2~3 次

如果固定采用 3 秒间隔,理论上约为 0.33 RPS,每小时最多约 1200 次请求。实际请求量还会受到页面响应时间、退避等待、任务暂停和失败重试影响。

根据响应时间估算并发

可使用下面的近似关系评估维持目标速率所需的并发:

并发数 ≈ 目标 RPS × 平均响应时间(秒)

例如,平均响应时间为 2 秒,目标速率为 0.5 RPS,估算并发数约为 1。该公式用于容量估算,不代表站点允许相应压力。响应变慢时,应先检查限流、网络和页面大小,而不是直接增加并发。

步骤三:按域名限速并加入随机抖动

可以在基础间隔上增加少量随机抖动,例如每次等待 2~4 秒,以降低多个任务在同一时间点集中请求的概率。随机等待只是辅助措施,不能代替明确的 RPS 和并发上限。

Python 串行限速示例

import random
import time
from urllib.parse import urlparse

import requests

session = requests.Session()
session.headers.update({
    "User-Agent": "ExampleCollector/1.0 (contact: admin@example.com)"
})

MIN_INTERVAL = 2.0
MAX_INTERVAL = 4.0


def fetch(url):
    time.sleep(random.uniform(MIN_INTERVAL, MAX_INTERVAL))
    response = session.get(url, timeout=(8, 20))
    response.raise_for_status()
    return response.text


urls = [
    "https://example.com/page/1",
    "https://example.com/page/2",
]

for url in urls:
    html = fetch(url)
    print(urlparse(url).path, len(html))

User-Agent 应真实标识采集程序。在适合公开联系方式的业务场景中,可以提供运维联系地址,但不应伪装成其他客户端或使用虚假身份规避站点控制。

多域名和多节点分别处理

同时采集多个网站时,应为每个域名单独维护下一次允许请求的时间、RPS 和并发上限。分布式采集还需要通过 Redis 等共享存储实现全局令牌桶或统一计数,否则每个节点都按本地上限运行后,汇总流量仍可能超过限制。

步骤四:限制并发数和连接池

多线程或异步程序应使用信号量限制同一域名的在途请求数量,并使用令牌桶或漏桶控制单位时间内的请求总数。只在任务开始前等待一次,无法约束后续并发产生的瞬时流量。

按单变量逐步调整

  1. 将单域名并发保持为 1,先运行 20~50 个 URL。
  2. 确认没有持续出现 429、403、验证码和异常延迟。
  3. 将请求间隔缩短 10%~20%,例如从 5 秒调整到 4 秒。
  4. 至少观察一个完整小批次,并对比调整前后的指标。
  5. 只有在站点规则允许且串行处理不能满足要求时,才将并发从 1 增加到 2。
  6. 错误率或响应延迟明显上升时,恢复上一组稳定参数。

连接池可通过复用 TCP 连接降低握手开销,但不会提高站点允许的访问频率。连接池最大连接数通常不应高于程序实际允许的并发数。

步骤五:配置超时、重试和指数退避

服务器返回 429 Too Many Requests 时,应暂停该域名的请求,并优先遵守 Retry-After 响应头。该响应头可能是等待秒数,也可能是 HTTP 日期。

带 Retry-After 的重试示例

import random
import time
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime

import requests

RETRYABLE_STATUS = {429, 500, 502, 503, 504}


def retry_after_seconds(value):
    if not value:
        return None
    try:
        return max(0, int(value))
    except ValueError:
        try:
            target = parsedate_to_datetime(value)
            if target.tzinfo is None:
                target = target.replace(tzinfo=timezone.utc)
            delay = target - datetime.now(timezone.utc)
            return max(0, delay.total_seconds())
        except (TypeError, ValueError, OverflowError):
            return None


def fetch_with_retry(session, url, max_retries=3):
    for attempt in range(max_retries + 1):
        response = session.get(url, timeout=(8, 20))

        if response.status_code not in RETRYABLE_STATUS:
            response.raise_for_status()
            return response

        if attempt == max_retries:
            response.raise_for_status()

        server_wait = retry_after_seconds(
            response.headers.get("Retry-After")
        )
        backoff = min(60, 2 ** attempt) + random.uniform(0, 1)
        time.sleep(server_wait if server_wait is not None else backoff)

    raise RuntimeError("request failed")

没有 Retry-After 时,可采用近似 1、2、4、8 秒的指数退避,并加入随机抖动。必须设置最大等待时间和最大重试次数,避免故障期间无限重试。

不同状态码的处理方式

  • 401:检查认证信息和访问权限,不应密集重试。
  • 403:停止对应访问并确认规则,不应通过更换身份绕过限制。
  • 404:通常表示资源不存在,一般无需立即重试。
  • 429:暂停或降低该域名频率,并遵守 Retry-After
  • 500/502/503/504:允许有限重试,但应使用指数退避。

POST 等可能产生副作用的请求不能默认重试。只有确认操作具备幂等性,或服务端支持幂等键时,才应自动重发。

步骤六:通过缓存和增量采集减少请求

降低服务器压力的有效方式不只是延长间隔,还包括减少没有必要的访问。对于已采集页面,可以保存 ETagLast-Modified,下次发送条件请求:

headers = {
    "If-None-Match": saved_etag,
    "If-Modified-Since": saved_last_modified,
}
response = session.get(url, headers=headers, timeout=(8, 20))

if response.status_code == 304:
    print("内容未变化,继续使用本地缓存")

还可实施以下优化:

  • 只采集新增页面或更新时间已变化的页面。
  • 规范化 URL 并去重,避免重复请求同一资源。
  • 保存任务进度,失败后从断点继续。
  • 根据内容更新周期安排任务,降低低频页面的采集次数。
  • 缓存原始响应,解析失败时优先离线重新处理。

步骤七:记录指标并验证频率

记录可用于判断的指标

  • 每分钟请求数及单域名 RPS。
  • 平均响应时间、P95 和 P99 响应时间。
  • HTTP 2xx、3xx、4xx、5xx 的数量和比例。
  • 429、403、超时和连接失败次数。
  • 重试次数、退避总时长和最终失败率。
  • 单位时间内成功采集的有效页面数。

执行分阶段验证

  1. 小样本验证:运行 20~50 个 URL,确认解析、超时、状态码分类和重试逻辑正确。
  2. 低频试运行:使用 2~5 秒间隔和并发 1 完成一个小批次。
  3. 建立基线:记录正常情况下的成功率、P95 延迟、429 数量和每分钟有效页面数。
  4. 单变量调节:每次只缩短 10%~20% 的间隔,或者只增加 1 个并发。
  5. 覆盖完整周期:观察目标网站高峰期以及一个完整采集周期内的表现。
  6. 执行回退:指标超过阈值时,自动恢复上一组稳定参数或暂停任务。

设置自动降速条件

以下条件可作为保守的起始阈值,正式值应结合目标站点规则和业务基线确定:

  • 连续出现 2 次 429:暂停该域名,并按照 Retry-After 恢复。
  • 5 分钟内 5xx 比例超过 5%:将请求频率降低 50%。
  • P95 响应时间较基线增加 100%:降低并发并延长间隔。
  • 连续出现 403 或验证页面:暂停采集并人工确认访问权限。
  • 超时率持续超过 3%:检查网络和服务状态,不立即增加重试次数。

这些数值是启动参考,不是通用安全线。网站明确规定的限制应始终优先。

不同采集场景的起始参数

场景初始间隔单域名并发操作重点
小型公开网站5~10 秒1保持低频,必要时联系站点确认
普通内容网站2~5 秒1使用随机抖动、缓存和增量更新
官方 API按照文档配额按照文档限制读取限流头并监控剩余配额
自有或已授权系统按照压测结果逐级增加结合服务端 CPU、延迟和错误率设置上限
分布式采集按域名全局计算全局控制使用共享令牌桶,避免各节点分别超发

上线前检查清单

  • 已经确认 robots.txt、服务条款、API 配额和数据授权范围。
  • 已经按域名设置请求间隔、RPS 和并发上限。
  • 已经配置连接超时、读取超时和最大重试次数。
  • 能够解析并遵守 429 响应中的 Retry-After。
  • 对可重试的 5xx 使用带随机抖动的指数退避。
  • 不会对 401、403 和 404 进行密集重试。
  • 已经启用 URL 去重、缓存、断点续采和增量更新。
  • 已经记录 RPS、响应延迟、状态码、重试和失败率。
  • 已经配置自动降速、暂停任务和人工告警条件。

常见问题

网页采集请求间隔设置多少合适?

目标站点没有公布限制时,可从单域名每 2~5 秒请求 1 次、并发数 1 开始。小型网站可放宽到 5~10 秒。运行后根据 429、5xx、超时率和 P95 响应时间逐步调整,网站或 API 公布的限制始终优先。

请求频率、请求间隔和并发数有什么区别?

请求频率表示单位时间内发送多少请求,请求间隔表示相邻两次请求之间等待多久,并发数表示同一时刻有多少请求正在执行。多个线程可能同时发送请求,因此设置间隔后仍需单独限制并发和总体 RPS。

遇到 HTTP 429 应该怎么处理?

立即暂停或降低该域名的请求频率,并优先遵守 Retry-After 响应头。没有 Retry-After 时,使用带随机抖动的指数退避,同时限制最大等待时间和最大重试次数。不要通过增加代理或更换身份绕过限流。

固定 sleep 可以完成请求限速吗?

固定 sleep 只适用于简单的串行任务。多线程、异步或分布式任务还需要按域名限制 RPS 和并发,正确处理 Retry-After,并在多个工作节点之间实施全局限速。

怎样判断当前网页采集请求频率过高?

常见信号包括 429 或 403 增加、出现验证页面、P95 响应时间明显上升、5xx 和超时率持续增长,以及提高请求量后有效页面数没有同步增加。出现这些信号时应自动降速或暂停。

如何验证调整后的频率是否合适?

先用 20~50 个 URL 建立低频基线,再一次只调整请求间隔或并发中的一个参数。对比调整前后的 RPS、P95 延迟、状态码比例、超时率、重试次数和有效页面数,并至少观察一个完整采集周期。

如何在不提高请求频率的情况下加快采集?

优先使用官方 API,并采用增量采集、URL 去重、ETag 和 Last-Modified 条件请求、断点续采、原始响应缓存及离线解析。这些措施可以减少无效请求,通常比直接增加并发更稳定。

总结

网页采集请求频率是采集程序在一定时间内向同一网站或接口发送请求的数量。实操时应先确认站点规则,再从单域名并发 1、请求间隔 2~5 秒开始,同时配置按域名限速、随机抖动、超时、有限重试和指数退避。通过 RPS、P95 延迟、429、403、5xx、超时率和有效页面数验证配置,每次只修改一个参数,并在异常指标达到阈值时自动降速或暂停。