使用Python Requests测量网页下载速度:问题与改进方案
问题分析
你的代码主要问题出在时间范围的计算错误,以及单次测量的偶然性:
response.elapsed统计的是从发起请求到收到完整响应的总耗时,包含DNS解析、TCP三次握手、服务器处理请求、网络传输数据的全部时间,并非仅数据下载的时间。服务器处理和握手等环节会占用大量时间,导致计算出的速度远低于实际下载速率。- 单次测量结果受网络波动、服务器负载影响极大,无法反映真实的平均速度。
- 若存在重定向,
elapsed还会包含所有重定向请求的耗时,进一步拉低计算值。
改进方案
针对网页HTML的测量需求,我们可以通过精准记录数据下载阶段的时间,并多次测量取平均来优化:
方法1:利用requests流式响应捕获下载时间(适配网页场景)
这里的流式只是用来捕获开始下载的时间点,不需要分块处理大文件,完全适配网页测量需求:
import requests import time def measure_download_speed(url, num_trials=5): total_speed = 0.0 session = requests.Session() for _ in range(num_trials): start_time = None content_length = 0 def track_download(response, *args, **kwargs): nonlocal start_time, content_length # 首次收到响应数据时记录开始时间 if start_time is None: start_time = time.perf_counter() # 累加收到的字节数 content_length += len(response.raw.read(1024*1024)) try: response = session.get(url, stream=True, hooks={'response': track_download}) response.raise_for_status() # 确保所有数据都被读取 while response.raw.read(1024*1024): pass if start_time is not None: elapsed_time = time.perf_counter() - start_time if elapsed_time > 0: # 字节转KB/s(1KB=1024字节) speed = (content_length / 1024) / elapsed_time total_speed += speed except Exception as e: print(f"请求失败: {e}") continue if num_trials > 0: avg_speed = total_speed / num_trials print(f"平均下载速度: {avg_speed:.2f} KB/s") return avg_speed else: return 0 # 测试 measure_download_speed("https://stackoverflow.com", num_trials=5)
方法2:通过响应头分离下载时间(更简洁)
如果不想用流式,也可以通过对比总耗时和服务器处理时间,近似分离出下载阶段的耗时:
import requests from datetime import datetime def get_download_speed(url, num_trials=5): total_speed = 0.0 session = requests.Session() for _ in range(num_trials): try: response = session.get(url, allow_redirects=True) response.raise_for_status() # 总耗时(秒) total_elapsed = response.elapsed.total_seconds() # 计算服务器处理时间:从本地请求发送到服务器返回响应头的时间 server_date = datetime.strptime(response.headers['Date'], '%a, %d %b %Y %H:%M:%S %Z') local_request_time = datetime.utcnow() - response.elapsed server_processing_time = (server_date - local_request_time).total_seconds() # 近似下载时间 = 总耗时 - 服务器处理时间(避免负数,设最小阈值) download_time = max(total_elapsed - server_processing_time, 0.001) content_size_kb = len(response.content) / 1024 speed = content_size_kb / download_time total_speed += speed except Exception as e: print(f"请求失败: {e}") continue avg_speed = total_speed / num_trials print(f"平均下载速度: {avg_speed:.2f} KB/s") return avg_speed get_download_speed("https://stackoverflow.com", num_trials=5)
关键优化点
- 精准计时:只统计数据传输阶段的时间,排除握手、服务器处理等非下载耗时。
- 多次取平均:减少网络波动、服务器负载带来的误差。
- 异常处理:避免单次请求失败导致整个测量失效。
内容的提问来源于stack exchange,提问作者Kodeeo
相关产品推荐
相关产品推荐

