You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用requests批量请求站点时如何获取所有去重的具体错误

requests批量站点检测的错误去重统计方案

问题原因

e.errno返回None是正常现象:requests和底层urllib3的异常都是链式封装的,顶层RequestException本身不直接绑定底层系统/SSL错误的错误号,具体错误诱因都存在异常链的末端,直接取顶层异常的属性拿不到根错误信息。
要区分同个顶层异常下的不同诱因,核心是先拿到异常链最底层的原始错误,再提取可去重的错误标识,过滤掉主机名、内存地址这类动态内容,就能生成不重复的错误清单。

可直接复用的实现代码

1. 依赖导入和统计容器初始化

import re
import requests
from collections import defaultdict
from urllib.parse import urlparse

# 错误统计容器:key为唯一错误标识,value存储错误出现次数、对应URL列表
error_stats = defaultdict(lambda: {"count": 0, "urls": []})

2. 工具函数

def is_valid_url(url):
    """提前过滤无效URL,避免空值、无协议地址触发无意义错误"""
    if not isinstance(url, str) or not url.strip():
        return False
    parse_res = urlparse(url.strip())
    return parse_res.scheme in ("http", "https") and parse_res.netloc

def get_root_error(e):
    """递归遍历异常链,拿到最底层的根错误"""
    current_err = e
    while current_err.__cause__ is not None:
        current_err = current_err.__cause__
    return current_err

def get_error_dedup_key(root_err):
    """生成可去重的错误标识,自动替换主机名、内存地址等动态内容"""
    # 提取根错误类型
    err_type = type(root_err).__name__
    # 提取错误码,兼容系统errno、Windows winerror两种标识
    err_code = getattr(root_err, "errno", None)
    if err_code is None:
        err_code = getattr(root_err, "winerror", None)
    # 处理错误信息,替换动态字段
    err_msg = str(root_err)
    err_msg = re.sub(r"0x[0-9a-fA-F]+", "<MEM_ADDR>", err_msg)  # 替换对象内存地址
    err_msg = re.sub(r"host='[^']+'", "host='<HOST>'", err_msg)  # 替换主机名
    err_msg = re.sub(r"connection to [^ ]+ timed out", "connection to <HOST> timed out", err_msg)  # 替换超时提示里的主机名
    return f"{err_type}|{err_code}|{err_msg.strip()}"

3. 改造检测逻辑

# 你的检测循环
for count, url in enumerate(your_url_list, 1):
    # 先过滤无效URL
    if not is_valid_url(url):
        err_key = "INVALID_URL|None|非法URL格式(空值/无协议/无域名)"
        error_stats[err_key]["count"] += 1
        error_stats[err_key]["urls"].append(url)
        print(f"{count} {url}: 不可访问,URL格式非法")
        continue
    
    try:
        resp = requests.get(
            url,
            allow_redirects=True,
            timeout=10, # 原代码timeout=1过短,公网站点TLS握手很容易超过1s被误判,建议设为5-10
            verify=True,
            headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36"}
        )
        if resp.status_code == 200:
            print(f"{count} {url}: 可正常访问,状态码: {resp.status_code}")
        else:
            # HTTP状态码类异常也纳入统计
            err_key = f"HTTP_STATUS|{resp.status_code}"
            error_stats[err_key]["count"] += 1
            error_stats[err_key]["urls"].append(url)
            print(f"{count} {url}: 不可访问,状态码: {resp.status_code}")
    except requests.exceptions.RequestException as e:
        root_err = get_root_error(e)
        err_key = get_error_dedup_key(root_err)
        error_stats[err_key]["count"] += 1
        error_stats[err_key]["urls"].append(url)
        print(f"{count} {url}: 不可访问\n错误详情: {e}")

4. 检测完成后输出唯一错误清单

print("\n===== 全量唯一错误类型汇总 =====")
for idx, (err_key, stat) in enumerate(error_stats.items(), 1):
    print(f"\n{idx}. 错误唯一标识: {err_key}")
    print(f"   累计出现次数: {stat['count']}")
    print(f"   故障站点示例: {stat['urls'][:3]}") # 最多展示3个示例站点

效果说明

这套逻辑会自动把同根因的错误归为一类,你遇到的SSL EOF错误、TLS名称不识别、握手失败、连接超时、DNS解析失败、连接被重置、端口拒绝连接等问题,都会生成独立的唯一标识,不会因为主机名、对象内存地址不同生成重复条目,跑完批量检测后直接看汇总清单就能拿到所有出现过的错误类型,不用手动整理日志。

内容的提问来源于stack exchange,提问作者stke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 03:06:23