如何在Python中获取网络请求的详细时序属性(如Blocked、DNS解析等)?
获取类似Firefox开发者工具的请求时序细节
嘿,这个问题我之前折腾过好一阵子!requests库的elapsed.total_seconds()确实只能给你一个模糊的总耗时,和Firefox开发者工具里拆分的Blocked、DNS解析、Connecting这些阶段时间差得挺多——毕竟前者只算了从请求发起开始到响应拿到手的总时长,还包含了requests自身的一些初始化开销,而Firefox是把整个HTTP请求的生命周期拆得明明白白的。
想要拿到和Firefox一样的细分时序,你可以试试下面这两个方案:
方案一:用PyCurl(底层精确控制)
PyCurl是libcurl的Python绑定,能直接获取到HTTP请求各个阶段的精确时间戳,完全对应Firefox里的各个时序项。
首先先安装它:
pip install pycurl
然后用这段代码就能拿到所有细节:
import pycurl from io import BytesIO def get_detailed_timings(url): response_buffer = BytesIO() curl = pycurl.Curl() curl.setopt(curl.URL, url) curl.setopt(curl.WRITEDATA, response_buffer) # 开启时序统计,定义回调函数收集时间数据 timings = {} def track_progress(_, __, ___, ____, total_time, namelookup_time, connect_time, pretransfer_time, starttransfer_time): timings['总耗时'] = total_time timings['DNS解析耗时'] = namelookup_time timings['到TCP连接完成耗时'] = connect_time timings['到开始发送请求前耗时'] = pretransfer_time timings['到第一个响应字节到达耗时'] = starttransfer_time curl.setopt(curl.XFERINFOFUNCTION, track_progress) curl.setopt(curl.PROGRESSFUNCTION, lambda *args: None) # 兼容旧版本libcurl try: curl.perform() except Exception as e: print(f"请求失败:{str(e)}") return None finally: curl.close() # 计算各个阶段的单独耗时,对应Firefox的展示项 timings['Blocked(近似)'] = timings['DNS解析耗时'] timings['TCP连接耗时'] = timings['到TCP连接完成耗时'] - timings['DNS解析耗时'] timings['TLS握手耗时'] = timings['到开始发送请求前耗时'] - timings['到TCP连接完成耗时'] timings['Waiting(服务器响应等待)'] = timings['到第一个响应字节到达耗时'] - timings['到开始发送请求前耗时'] timings['响应接收耗时'] = timings['总耗时'] - timings['到第一个响应字节到达耗时'] return timings # 测试用例 if __name__ == "__main__": url = "https://example.com" results = get_detailed_timings(url) if results: print("请求时序详情(对应Firefox开发者工具):") for stage, duration in results.items(): print(f"{stage}: {duration:.4f} 秒")
这里的各个字段完全对应Firefox里的项:
DNS解析耗时→ Firefox的DNS解析时间TCP连接耗时→ Connecting阶段的TCP连接部分TLS握手耗时→ (HTTPS请求)SSL/TLS握手时间Waiting(服务器响应等待)→ 就是你看到的~650ms那个等待时间Blocked(近似)→ 近似Firefox的Blocked时间(pycurl没有直接提供请求排队等待的精确值,用DNS开始前的时间近似)
方案二:用HTTPX(更简洁的现代库)
如果你觉得PyCurl的代码有点繁琐,HTTPX这个现代HTTP库也支持直接获取细分时序,而且API更友好,还支持异步。
安装HTTPX:
pip install httpx
示例代码:
import httpx import asyncio async def get_httpx_timings(url): async with httpx.AsyncClient() as client: response = await client.get(url) # 从响应扩展字段里拿到时序数据 timing_data = response.extensions['http_version']['timings'] print("请求时序详情:") print(f"Blocked: {timing_data['blocked']:.4f} 秒") print(f"DNS解析: {timing_data['dns']:.4f} 秒") print(f"TCP连接: {timing_data['connect']:.4f} 秒") print(f"TLS握手: {timing_data['tls']:.4f} 秒") print(f"请求发送: {timing_data['send']:.4f} 秒") print(f"Waiting(等待响应): {timing_data['wait']:.4f} 秒") print(f"响应接收: {timing_data['receive']:.4f} 秒") print(f"总耗时: {sum(timing_data.values()):.4f} 秒") # 运行异步函数 asyncio.run(get_httpx_timings("https://example.com"))
HTTPX的timings字段直接映射了Firefox里的所有阶段,不用自己计算,用起来特别省心。
为什么requests的elapsed和Firefox有差异?
简单来说:
- requests的
elapsed是从你调用requests.get()方法开始计时,到响应完全拿到手的总时间,包含了requests库自身的初始化、参数处理等开销。 - Firefox的时间是从浏览器真正发起网络请求的那一刻开始算的,而且拆分了各个阶段,没有包含浏览器自身的准备开销,所以两者会有100ms左右的差异,这完全正常。
内容的提问来源于stack exchange,提问作者onon99buynoodle
相关产品推荐
相关产品推荐

