直接用requests.post发送异常堆栈不行,因traceback.format_exc()返回纯字符串,缺乏时间、位置等结构化信息,且易超2048字符限制导致414错误;须拆解为markdown格式,含标题、代码块和补充字段,并做截断与安全防护。

为什么直接用 requests.post 发送异常堆栈不行?
因为企业微信机器人要求的 Content-Type 必须是 application/json,且消息体结构固定;而 Python 的 traceback.format_exc() 返回的是纯字符串,直接塞进去会丢失关键上下文(如发生时间、触发位置、模块名),也不方便后续按关键词过滤或告警分级。
实操建议:
- 用
logging.exception()或手动捕获sys.exc_info()获取结构化异常信息,再拼装为字典 - 必须对异常消息做长度截断(企业微信单条消息上限 2048 字符),否则请求会返回
414 Request-URI Too Large或静默失败 - 别用
json.dumps(exc_str)直接发——这会把整个堆栈当一个字段塞进 JSON,机器人解析后显示为乱码或空白
怎么构造符合企业微信格式的异常消息体?
核心是把异常拆成「标题 + 代码块 + 补充字段」三部分,用 markdown 类型发送。企业微信支持有限的 markdown 渲染,``` 包裹的堆栈能保持缩进和换行。
实操建议:
- 标题固定为
"[爬虫异常] {spider_name}",便于在群内快速识别来源 - 堆栈内容用
traceback.format_exception(*exc_info)获取标准列表,再''.join()拼接,避免format_exc()丢掉最外层异常类型 - 补充字段推荐加:
time(ISO 格式)、url(当前请求 URL,如有)、level(如"ERROR")——这些写在正文末尾用**key**:value格式即可,不额外建字段
示例片段:
{
"msgtype": "markdown",
"markdown": {
"content": "[爬虫异常] douban_movie\n> ```\nTraceback (most recent call last):\n File \"spider.py\", line 42, in parse\n data = json.loads(response.text)\njson.JSONDecodeError: Expecting value: line 1 column 1 (char 0)\n```\n**time**:2024-06-15T14:22:03Z\n**url**:https://movie.douban.com/subject/1292052/\n**level**:ERROR"
}
}
如何在 Scrapy 中全局捕获并推送异常?
Scrapy 的异常多数发生在 parse 回调或中间件里,不能只靠 try/except 包每个函数——太散且漏掉 start_requests 失败等场景。真正可靠的是重写 Spider.handle_httpstatus_list + 自定义 DownloaderMiddleware.process_exception。
实操建议:
- 在中间件中监听
process_exception(request, exception, spider),这里能捕获 DNS 错误、连接超时、回调抛出的任意异常 - 用
spider.logger记录原始日志,再调用统一推送函数,避免重复发送 - 加个开关配置(如
spider.crawler.settings.getbool('PUSH_WECHAT_ON_ERROR')),开发环境默认关,上线再开 - 推送前检查
spider.name是否在白名单里(比如只推prod_*开头的爬虫),防止测试爬虫刷屏
推送失败了怎么办?别让告警本身变成故障点
企业微信机器人地址可能失效、网络抖动、或被限流(每分钟最多 20 条)。如果推送逻辑卡住或重试太久,会拖慢整个爬虫调度。
实操建议:
- 用
requests.post(..., timeout=(3, 7)),第一个数是连接超时,第二个是读取超时,避免 hang 死 - 失败时不重试,只打一条本地日志(含
response.status_code和response.text),否则可能形成雪崩 - 把推送逻辑放到线程里执行(
threading.Thread(target=send_to_wechat, args=(payload,)).start()),主流程绝不阻塞 - 注意:企业微信返回
400通常是 JSON 格式错(比如多了逗号)、403是 webhook 地址过期、413是消息体超长——这些都要在本地日志里明确标出
最常被忽略的一点:机器人链接里的 key=xxx 参数不能硬编码在代码里,必须从环境变量或配置中心读取,否则密钥泄漏风险极高。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











