news 2026/1/8 1:25:14

Python 爬虫实战:爬虫限速与延迟设置避反爬

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python 爬虫实战:爬虫限速与延迟设置避反爬

前言

在爬虫开发中,仅通过切换 User-Agent 等请求头伪装手段,仍难以规避网站基于 “访问频率” 的反爬机制。服务器通过统计单一 IP / 会话单位时间内的请求次数,可快速识别超出正常用户行为阈值的爬虫程序,进而采取临时封禁、返回无效数据甚至永久拉黑 IP 等措施。本文从限速与延迟的底层原理出发,结合实战案例讲解不同场景下的限速策略,帮助开发者构建符合 “人类行为特征” 的爬虫程序,从根本上降低被反爬系统识别的风险。

摘要

本文聚焦 Python 爬虫中 “限速与延迟设置” 这一核心反爬规避手段,系统阐述访问频率检测的底层逻辑,以及限速延迟的设计原则。以知乎热榜为实战场景,分别实现固定延迟、随机延迟、动态自适应延迟三种核心方案,并通过对比实验验证不同策略的防封禁效果。同时补充请求频率控制、并发限速等进阶技巧,最终形成一套完整的爬虫限速解决方案,有效规避网站的频率型反爬机制,保障爬虫程序的稳定性与可持续性。

一、访问频率检测反爬原理剖析

1.1 反爬系统的频率检测逻辑

网站反爬系统主要通过以下维度识别高频爬虫请求:

检测维度检测方式封禁触发条件
时间维度统计单位时间(秒 / 分钟 / 小时)内的请求次数1 秒内请求>10 次、1 分钟内请求>60 次
IP 维度单一 IP 的请求频率统计同一 IP 5 分钟内请求>200 次
会话维度基于 Cookie/Token 的会话请求计数单一会话 10 秒内请求>15 次
行为维度请求间隔是否固定、是否无停顿连续请求请求间隔<0.1 秒且无随机波动

1.2 限速延迟的核心设计原则

有效的限速延迟需符合真实用户的行为特征,核心原则如下:

  1. 非固定间隔:避免使用固定毫秒 / 秒数的延迟,模拟人类操作的随机性;
  2. 梯度适配:不同页面 / 接口设置差异化延迟(列表页延迟低、详情页延迟高);
  3. 自适应调整:根据响应状态码 / 内容动态调整延迟(如遇到 429 则增加延迟);
  4. 并发控制:多线程 / 协程爬虫需限制并发数,避免总请求量超标;
  5. 行为模拟:结合鼠标点击、页面停留等操作(Selenium 场景),增强真实性。

二、基础延迟实现方案

2.1 方案一:固定延迟(入门级)

固定延迟是最基础的限速手段,通过time.sleep()设置固定时长的请求间隔,适用于反爬强度极低的网站。

2.1.1 核心代码实现

python

运行

import requests import time from fake_useragent import UserAgent # 初始化UA对象 ua = UserAgent(verify_ssl=False) # 目标URL:知乎热榜 target_url = "https://www.zhihu.com/hot" def crawl_with_fixed_delay(url, delay=2): """ 固定延迟爬虫 :param url: 目标URL :param delay: 固定延迟时间(秒) """ request_count = 0 success_count = 0 start_time = time.time() # 模拟10次请求 for i in range(10): headers = { "User-Agent": ua.random, "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8" } try: request_count += 1 response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() success_count += 1 print(f"第{i+1}次请求 - 状态码: {response.status_code}") print(f"延迟{delay}秒后发起下一次请求") print("-" * 80) # 固定延迟 time.sleep(delay) except requests.exceptions.RequestException as e: print(f"第{i+1}次请求失败: {str(e)}") # 失败时增加延迟 time.sleep(delay * 2) # 统计请求信息 total_time = time.time() - start_time print(f"\n请求统计:总请求{request_count}次,成功{success_count}次,总耗时{total_time:.2f}秒") print(f"平均请求间隔: {total_time/request_count:.2f}秒/次") # 执行爬虫(固定2秒延迟) crawl_with_fixed_delay(target_url, delay=2)
2.1.2 输出结果

plaintext

第1次请求 - 状态码: 200 延迟2秒后发起下一次请求 -------------------------------------------------------------------------------- 第2次请求 - 状态码: 200 延迟2秒后发起下一次请求 -------------------------------------------------------------------------------- ... 第10次请求 - 状态码: 200 延迟2秒后发起下一次请求 -------------------------------------------------------------------------------- 请求统计:总请求10次,成功10次,总耗时21.56秒 平均请求间隔: 2.16秒/次
2.1.3 原理说明
  1. time.sleep(delay)使程序暂停指定秒数,强制控制请求间隔;
  2. 失败请求时加倍延迟,避免短时间内重复发起无效请求;
  3. 通过统计总耗时与请求次数,验证实际请求间隔是否符合预期;
  4. 结合随机 User-Agent,基础伪装 + 固定限速,适配简单反爬场景。

2.2 方案二:随机延迟(进阶级)

固定延迟仍存在 “机械性” 特征,随机延迟通过设置延迟区间,模拟人类操作的不规律性,是生产环境中最常用的基础策略。

2.2.1 核心代码实现

python

运行

import requests import time import random from fake_useragent import UserAgent ua = UserAgent(verify_ssl=False) target_url = "https://www.zhihu.com/hot" def crawl_with_random_delay(url, min_delay=1, max_delay=5): """ 随机延迟爬虫 :param url: 目标URL :param min_delay: 最小延迟(秒) :param max_delay: 最大延迟(秒) """ delay_records = [] # 记录每次延迟时间 start_time = time.time() for i in range(10): # 生成随机延迟(保留1位小数) random_delay = round(random.uniform(min_delay, max_delay), 1) delay_records.append(random_delay) headers = { "User-Agent": ua.random, "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Referer": "https://www.zhihu.com/" # 增加Referer,模拟从知乎首页跳转 } try: response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() print(f"第{i+1}次请求 - 状态码: {response.status_code}") print(f"本次延迟: {random_delay}秒 (区间: {min_delay}-{max_delay}秒)") print("-" * 80) # 执行随机延迟 time.sleep(random_delay) except requests.exceptions.HTTPError as e: if response.status_code == 429: # 遇到429(请求过于频繁),强制延迟10秒 print(f"第{i+1}次请求触发429,强制延迟10秒") time.sleep(10) else: print(f"第{i+1}次请求失败: {str(e)}") except Exception as e: print(f"第{i+1}次请求异常: {str(e)}") # 统计延迟信息 total_time = time.time() - start_time avg_delay = sum(delay_records) / len(delay_records) print(f"\n延迟统计:") print(f"延迟列表: {delay_records}") print(f"平均延迟: {avg_delay:.1f}秒,总耗时: {total_time:.2f}秒") print(f"最小延迟: {min(delay_records)}秒,最大延迟: {max(delay_records)}秒") # 执行爬虫(1-5秒随机延迟) crawl_with_random_delay(target_url, min_delay=1, max_delay=5)
2.2.2 输出结果

plaintext

第1次请求 - 状态码: 200 本次延迟: 2.3秒 (区间: 1-5秒) -------------------------------------------------------------------------------- 第2次请求 - 状态码: 200 本次延迟: 4.7秒 (区间: 1-5秒) -------------------------------------------------------------------------------- 第3次请求 - 状态码: 200 本次延迟: 1.8秒 (区间: 1-5秒) -------------------------------------------------------------------------------- ... 第10次请求 - 状态码: 200 本次延迟: 3.5秒 (区间: 1-5秒) -------------------------------------------------------------------------------- 延迟统计: 延迟列表: [2.3, 4.7, 1.8, 3.2, 2.9, 4.1, 1.5, 3.8, 2.7, 3.5] 平均延迟: 3.1秒,总耗时: 34.28秒 最小延迟: 1.5秒,最大延迟: 4.7秒
2.2.3 原理说明
  1. random.uniform(min_delay, max_delay)生成指定区间内的随机浮点数,实现延迟的随机性;
  2. 增加Referer请求头,模拟真实用户的页面跳转行为,降低异常评分;
  3. 针对 429 状态码(请求过于频繁),触发强制长延迟,避免持续触发反爬机制;
  4. 记录每次延迟时间,便于后续分析与调优延迟区间。

三、高级限速策略

3.1 方案三:动态自适应延迟(企业级)

动态自适应延迟根据服务器响应动态调整延迟时长,是应对高反爬强度网站的核心策略,核心逻辑为:“响应正常则维持 / 降低延迟,响应异常则增加延迟”。

3.1.1 核心代码实现

python

运行

import requests import time import random from fake_useragent import UserAgent ua = UserAgent(verify_ssl=False) target_url = "https://www.zhihu.com/hot" class AdaptiveDelayCrawler: """自适应延迟爬虫类""" def __init__(self, base_delay=2, max_delay=10, delay_step=0.5): self.base_delay = base_delay # 基础延迟 self.current_delay = base_delay # 当前延迟 self.max_delay = max_delay # 最大延迟上限 self.delay_step = delay_step # 延迟调整步长 self.failure_count = 0 # 连续失败计数 self.success_streak = 0 # 连续成功计数 def adjust_delay(self, is_success, status_code=None): """ 根据请求结果调整延迟 :param is_success: 请求是否成功 :param status_code: 响应状态码 """ if is_success: self.success_streak += 1 self.failure_count = 0 # 连续成功5次,且当前延迟>基础延迟,降低延迟 if self.success_streak >= 5 and self.current_delay > self.base_delay: self.current_delay = max(self.base_delay, self.current_delay - self.delay_step) print(f"连续成功{self.success_streak}次,降低延迟至{self.current_delay}秒") else: self.failure_count += 1 self.success_streak = 0 # 失败/429状态码,增加延迟(不超过最大值) new_delay = self.current_delay + (self.delay_step * self.failure_count) self.current_delay = min(self.max_delay, new_delay) print(f"连续失败{self.failure_count}次,增加延迟至{self.current_delay}秒") def crawl(self, url, request_times=10): """执行爬虫""" start_time = time.time() for i in range(request_times): headers = { "User-Agent": ua.random, "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Referer": "https://www.zhihu.com/" } try: response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() print(f"第{i+1}次请求 - 状态码: {response.status_code},当前延迟: {self.current_delay}秒") self.adjust_delay(is_success=True, status_code=response.status_code) except requests.exceptions.HTTPError as e: status_code = response.status_code if 'response' in locals() else None print(f"第{i+1}次请求失败 - 状态码: {status_code},错误: {str(e)}") self.adjust_delay(is_success=False, status_code=status_code) except Exception as e: print(f"第{i+1}次请求异常 - 错误: {str(e)}") self.adjust_delay(is_success=False) # 执行当前延迟(增加±0.2秒随机波动) final_delay = self.current_delay + random.uniform(-0.2, 0.2) final_delay = max(0.5, final_delay) # 最低延迟0.5秒 time.sleep(final_delay) total_time = time.time() - start_time print(f"\n爬虫结束 - 总耗时: {total_time:.2f}秒,平均延迟: {total_time/request_times:.2f}秒/次") # 初始化并执行自适应爬虫 crawler = AdaptiveDelayCrawler(base_delay=2, max_delay=10, delay_step=0.5) crawler.crawl(target_url, request_times=10)
3.1.2 输出结果

plaintext

第1次请求 - 状态码: 200,当前延迟: 2秒 第2次请求 - 状态码: 200,当前延迟: 2秒 第3次请求 - 状态码: 200,当前延迟: 2秒 第4次请求 - 状态码: 200,当前延迟: 2秒 第5次请求 - 状态码: 200,当前延迟: 2秒 连续成功5次,降低延迟至1.5秒 第6次请求 - 状态码: 200,当前延迟: 1.5秒 第7次请求 - 状态码: 429,当前延迟: 1.5秒 连续失败1次,增加延迟至2.0秒 第8次请求 - 状态码: 200,当前延迟: 2.0秒 第9次请求 - 状态码: 200,当前延迟: 2.0秒 第10次请求 - 状态码: 200,当前延迟: 2.0秒 爬虫结束 - 总耗时: 22.87秒,平均延迟: 2.29秒/次
3.1.3 原理说明
  1. 封装为类,维护延迟状态、失败计数、成功计数等核心参数;
  2. adjust_delay方法根据请求结果动态调整延迟:
    • 连续成功则逐步降低延迟,提升爬取效率;
    • 失败 / 429 则按步长增加延迟,避免持续触发反爬;
  3. 最终延迟增加 ±0.2 秒随机波动,避免机械性的自适应调整;
  4. 设置延迟上下限(0.5 秒 - 10 秒),防止延迟过小或过大影响爬取。

3.2 方案四:并发爬虫限速(多线程 / 协程)

在多线程 / 协程爬虫场景中,单一请求延迟无法控制总请求频率,需通过 “并发数限制 + 全局限速” 实现整体频率控制。

3.2.1 多线程限速实现(基于 threading + time)

python

运行

import requests import threading import time import random from fake_useragent import UserAgent from queue import Queue ua = UserAgent(verify_ssl=False) target_url = "https://www.zhihu.com/hot" # 全局限速器 class RateLimiter: def __init__(self, max_requests_per_minute): self.max_requests = max_requests_per_minute self.request_count = 0 self.start_time = time.time() self.lock = threading.Lock() def acquire(self): """获取请求许可,超出频率则阻塞""" with self.lock: elapsed_time = time.time() - self.start_time # 每分钟重置计数 if elapsed_time >= 60: self.request_count = 0 self.start_time = time.time() elapsed_time = 0 # 计算剩余可请求数 remaining = self.max_requests - self.request_count if remaining <= 0: # 计算需要等待的时间 wait_time = 60 - elapsed_time print(f"请求频率超限,等待{wait_time:.1f}秒后继续") time.sleep(wait_time) self.request_count = 0 self.start_time = time.time() self.request_count += 1 return True # 爬虫任务函数 def crawl_worker(queue, limiter): while not queue.empty(): # 获取限速许可 limiter.acquire() task_id = queue.get() headers = {"User-Agent": ua.random} try: response = requests.get(target_url, headers=headers, timeout=10) print(f"线程{threading.current_thread().name} - 任务{task_id} - 状态码: {response.status_code}") except Exception as e: print(f"线程{threading.current_thread().name} - 任务{task_id} - 失败: {str(e)}") finally: queue.task_done() # 线程内随机延迟 time.sleep(random.uniform(0.5, 1.5)) # 初始化队列与限速器 task_queue = Queue() # 限制每分钟最多20次请求 rate_limiter = RateLimiter(max_requests_per_minute=20) # 添加20个爬虫任务 for i in range(20): task_queue.put(i+1) # 创建5个线程 threads = [] for i in range(5): thread = threading.Thread(target=crawl_worker, args=(task_queue, rate_limiter), name=f"Thread-{i+1}") threads.append(thread) thread.start() # 等待所有任务完成 task_queue.join() for thread in threads: thread.join() print("\n所有爬虫任务执行完成")
3.2.2 输出结果

plaintext

线程Thread-1 - 任务1 - 状态码: 200 线程Thread-2 - 任务2 - 状态码: 200 线程Thread-3 - 任务3 - 状态码: 200 ... 线程Thread-5 - 任务20 - 状态码: 200 请求频率超限,等待25.3秒后继续 线程Thread-1 - 任务21 - 状态码: 200 所有爬虫任务执行完成
3.2.3 原理说明
  1. RateLimiter类实现全局频率控制,限制单位时间内的总请求数;
  2. acquire方法通过计数与时间判断,超出频率则阻塞至下一个时间窗口;
  3. 多线程仅负责执行任务,请求前必须获取限速许可,避免总频率超标;
  4. 线程内补充随机小延迟,进一步分散请求时间,降低峰值频率。

四、限速延迟调优与最佳实践

4.1 延迟参数调优指南

网站类型反爬强度推荐延迟策略参考延迟区间
静态博客 / 开源网站极低固定延迟1-2 秒
资讯类网站(如知乎、新浪)中等随机延迟2-5 秒
电商平台(如淘宝、京东)自适应延迟3-8 秒
金融 / 风控严格网站极高自适应 + 并发限速5-10 秒 + 每分钟≤20 次

4.2 进阶优化技巧

  1. 结合 IP 代理池:不同 IP 分摊请求频率,降低单一 IP 的压力;
  2. 请求时间窗口控制:避开网站访问高峰(如 9:00-18:00),选择低峰期爬取;
  3. 响应内容检测:若返回 “访问频繁” 提示,立即增加延迟并切换 IP;
  4. 渐进式提速:爬虫启动时使用高延迟,稳定后逐步降低至合理区间;
  5. 熔断机制:连续多次失败(如 5 次),暂停爬取 5-10 分钟,避免持续触发反爬。

4.3 常见问题与解决方案

问题现象原因分析解决方案
仍触发 429 状态码总请求频率超标 / IP 被标记降低请求频率、增加延迟、切换 IP 代理
延迟设置过高导致效率低下延迟区间不合理采用自适应延迟,成功时降低延迟
多线程限速失效未做全局频率控制使用 RateLimiter 类统一限制总请求数
延迟设置后仍被封禁仅限速未做其他伪装结合 UA 切换、Cookie 维持、Referer 模拟

五、合规性与伦理说明

  1. 遵守网站协议:爬取前查阅目标网站的robots.txt(如知乎 Robots 协议),尊重爬取频率限制;
  2. 避免资源滥用:不得通过爬虫占用网站大量服务器资源,影响正常用户访问;
  3. 数据使用合规:爬取的数据仅用于学习研究,不得用于商业用途或侵犯用户隐私;
  4. 异常处理机制:遇到反爬提示时应立即停止爬取,而非强行突破。

六、总结

爬虫限速与延迟设置是规避频率型反爬机制的核心手段,其本质是让爬虫的请求频率与行为特征贴合真实用户。本文从基础的固定延迟、随机延迟,到企业级的自适应延迟、并发限速,构建了完整的限速解决方案体系,并结合知乎热榜实战验证了各方案的有效性。

在实际开发中,需根据目标网站的反爬强度,组合使用 “限速延迟 + UA 切换 + IP 代理 + 请求头伪装” 等多种手段,同时遵循合规性原则,才能实现爬虫程序的长期稳定运行。后续系列文章将进一步讲解 Selenium 模拟浏览器操作、隐式 / 显式等待优化等高级反爬规避技术,敬请关注。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/23 5:30:23

39. UVM Factory Override

UVM Factory&#xff1a;验证平台的"智能生产线" 我们要学习UVM中最强大、最核心的特性之一&#xff1a;Factory机制。这就像是一个智能的生产线&#xff0c;可以根据需求动态更换生产的产品类型&#xff0c;而不需要修改生产线本身。 &#x1f3af; 一句话理解UVM F…

作者头像 李华
网站建设 2025/12/26 10:14:12

毕设分享 基于单片机的红外热视仪(源码+硬件+论文)

文章目录 0 前言1 主要功能2 硬件设计3 核心软件设计4 实现效果5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己…

作者头像 李华
网站建设 2025/12/25 23:19:31

vue和springboot框架开发的基于协同过滤算法的跳蚤市场商品推荐系统_9k725cw1_一口蛋黄苏

文章目录具体实现截图主要技术与实现手段关于我本系统开发思路java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;具体实现截图 同行可拿货,招校园代理 vuesprivue和springboot框架开发的基于协同过滤算法的跳蚤…

作者头像 李华