杰瑞科技汇

Python requests如何设置延迟请求?

在 Python 中使用 requests 库实现延迟(即请求之间的时间间隔)非常简单,主要有以下几种方法,适用于不同的场景。

Python requests如何设置延迟请求?-图1
(图片来源网络,侵删)

使用 time.sleep() (最常用、最简单)

这是最直接的方法,即在两次请求之间调用 time.sleep() 函数,让程序暂停指定的秒数。

适用场景:当你需要精确控制每次请求之间的间隔时间时,例如爬取网站时避免请求过快。

示例代码:

import requests
import time
# 目标 URL
url = 'https://httpbin.org/delay/1' # 这个 URL 会故意延迟1秒再响应
# 循环发送3次请求
for i in range(3):
    print(f"正在发送第 {i+1} 次请求...")
    try:
        # 发送 GET 请求
        response = requests.get(url)
        # 检查请求是否成功
        response.raise_for_status() # 如果状态码不是 200,则会抛出异常
        print(f"第 {i+1} 次请求成功,状态码: {response.status_code}")
        # print(response.json()) # 打印响应内容
    except requests.exceptions.RequestException as e:
        print(f"第 {i+1} 次请求失败: {e}")
    # 在两次请求之间暂停 2 秒
    # 如果是最后一次请求,则不需要 sleep
    if i < 2:
        print(f"等待 2 秒...")
        time.sleep(2) # 暂停 2 秒
print("所有请求已完成。")

优点

  • 简单直观,易于理解和使用。
  • 可以精确控制每次请求之间的间隔。

缺点

  • 同步的,time.sleep() 会阻塞整个程序的执行,在等待期间无法做其他事情。

使用 requests.Session() + time.sleep() (更高效)

如果你需要对同一个域名进行多次请求,使用 Session 对象是更好的选择。Session 对象会保持一个连接(TCP连接池),可以复用连接,提高效率,你仍然可以在 Session 的请求循环中使用 time.sleep()

适用场景:需要对同一网站的多个 URL 进行多次请求,既要延迟,又要高效。

示例代码:

import requests
import time
# 创建一个 Session 对象
session = requests.Session()
# 目标 URL 列表
urls = [
    'https://httpbin.org/get',
    'https://httpbin.org/user-agent',
    'https://httpbin.org/headers'
]
for i, url in enumerate(urls):
    print(f"正在使用 Session 发送第 {i+1} 次请求到 {url}...")
    try:
        # 使用 Session 发送请求
        response = session.get(url)
        response.raise_for_status()
        print(f"第 {i+1} 次请求成功,状态码: {response.status_code}")
    except requests.exceptions.RequestException as e:
        print(f"第 {i+1} 次请求失败: {e}")
    # 在请求之间暂停 1.5 秒
    if i < len(urls) - 1:
        print(f"等待 1.5 秒...")
        time.sleep(1.5)
# 关闭 Session,释放连接
session.close()
print("所有请求已完成。")

优点

  • 结合了 Session 的高效(连接复用)和 time.sleep() 的简单可控性。
  • 适合需要爬取同一网站多个页面的场景。

使用 requests.adaptersHTTPAdapter (高级用法)

requests 底层使用 urllib3 库,你可以通过配置 urllib3HTTPAdapter 来设置全局的延迟,pool_connectionspool_blocksize,但这更像是控制连接池的参数,而不是请求之间的时间间隔。

注意:这种方法不直接提供“请求之间等待 N 秒”的功能,而是通过控制连接池的获取超时时间来间接影响请求速率,它更适合管理连接资源。

示例代码:

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# 创建一个 Session 对象
session = requests.Session()
# 定义重试策略
retry_strategy = Retry(
    total=3, # 总共重试3次
    backoff_factor=1, # 指数退避因子,第一次重试等待 1 * 2^0 秒,第二次 1 * 2^1 秒...
    status_forcelist=[500, 502, 503, 504] # 遇到这些状态码时重试
)
# 创建一个 HTTPAdapter 并应用重试策略
adapter = HTTPAdapter(max_retries=retry_strategy)
# 将 adapter 挂载到 Session 的 http 和 https 上
session.mount("http://", adapter)
session.mount("https://", adapter)
# --- 现在你仍然需要用 time.sleep 来控制请求间隔 ---
url = 'https://httpbin.org/get'
for i in range(3):
    print(f"正在发送第 {i+1} 次请求...")
    try:
        response = session.get(url)
        response.raise_for_status()
        print(f"第 {i+1} 次请求成功")
    except requests.exceptions.RequestException as e:
        print(f"第 {i+1} 次请求失败: {e}")
    if i < 2:
        time.sleep(3) # 仍然需要手动添加延迟
session.close()

优点

  • 非常强大,可以处理网络不稳定、服务器临时错误等情况(通过重试机制)。
  • 可以自定义连接池大小等底层参数。

缺点

  • 配置相对复杂。
  • 不能替代 time.sleep() 来实现请求间隔,它主要用于连接管理和错误恢复。

使用 concurrent.futures (异步并发)

如果你不想因为 time.sleep() 而阻塞整个程序,可以使用 concurrent.futures 来异步地发送请求,并通过 as_completed 结合 time.sleep 来管理整体速率。

适用场景:需要高效地发送多个请求,但又要控制整体的请求速率(每秒最多 N 个请求)。

示例代码:

import requests
import time
import concurrent.futures
def fetch_url(url, delay):
    """一个请求函数,包含延迟逻辑"""
    print(f"准备请求 {url}...")
    time.sleep(delay) # 在请求前等待
    try:
        response = requests.get(url, timeout=5)
        response.raise_for_status()
        print(f"成功请求 {url}, 状态码: {response.status_code}")
        return response.text
    except requests.exceptions.RequestException as e:
        print(f"请求 {url} 失败: {e}")
        return None
urls = [
    'https://httpbin.org/get',
    'https://httpbin.org/delay/1',
    'https://httpbin.org/delay/2',
    'https://httpbin.org/delay/3'
]
# 设置总的请求延迟(每次请求间隔1秒)
request_delay = 1
# 使用 ThreadPoolExecutor 创建一个线程池
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    # 使用列表推导式提交任务,并为每个任务添加延迟
    # 注意:这里的延迟是在任务提交前等待,而不是在任务执行期间
    future_to_url = {
        executor.submit(fetch_url, url, request_delay): url
        for url in urls
    }
    # as_completed 会按照完成的顺序返回 future 对象
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
            # 在这里处理返回的数据
            # print(f"从 {url} 获取到的数据: {data[:100]}...")
        except Exception as e:
            print(f"处理 {url} 时发生错误: {e}")
print("所有异步请求已完成。")

注意:在这个异步例子中,time.sleep(delay) 是在任务提交到线程池之前执行的,这会使得主线程在提交任务时产生延迟,从而间接控制了任务提交的速率,如果想在任务内部执行时延迟逻辑,则需要将 time.sleep 放在 fetch_url 函数内部。


总结与最佳实践

方法 优点 缺点 适用场景
time.sleep() 简单、直接、精确 同步阻塞 简单脚本,需要精确控制间隔
Session + sleep 高效+可控 仍是同步阻塞 对同一网站多次请求,需要延迟
HTTPAdapter 强大,处理重试和连接池 配置复杂,不直接控制间隔 需要高可用性、稳定性的爬虫
concurrent.futures 异步并发,高效 代码稍复杂,需要理解异步模型 需要高并发且控制整体请求速率

对于绝大多数情况,尤其是初学者和日常脚本,方法一 (time.sleep())方法二 (Session + time.sleep()) 是最常用和推荐的选择。

重要提醒: 在进行网络请求(尤其是爬虫)时,添加延迟 (time.sleep) 是一种礼貌且必要的行为,它可以:

  1. 避免对目标服务器造成过大压力
  2. 降低被 IP 封禁的风险
  3. 防止触发网站的反爬虫机制

请根据你的具体需求选择最合适的方法。

分享:
扫描分享到社交APP
上一篇
下一篇