直接print或写本地文件不适用于生产爬虫日志,因其日志分散、无级别区分、无法追溯请求上下文(如url、spider、重试失败),且不支持实时告警、聚合分析;并发时多进程/线程写同一文件易导致错乱丢行。

为什么直接 print() 或写本地文件不适用于生产爬虫日志
因为日志分散、无级别区分、无法追溯请求上下文(比如哪个 url、哪个 spider、哪次重试失败),更没法做实时告警或聚合分析。一旦并发量上来,多个进程/线程往同一个文件 open(..., 'a') 写,还会出现内容错乱或丢行。
用 logging + QueueHandler 实现进程安全的日志转发
核心是把日志从各个爬虫工作线程/进程统一推到一个队列,再由单独的监听线程消费并投递到中心服务(如 Elasticsearch、Kafka 或 HTTP API)。避免了文件锁和写冲突。
-
QueueHandler和QueueListener是标准库内置组合,无需额外依赖 - 每个 worker 进程都配置自己的
QueueHandler,共用一个queue.Queue实例(注意:multiprocessing 需用multiprocessing.Manager().Queue()) - 监听线程拿到
LogRecord后,可补全字段:加spider_name、request_url、response_status等上下文信息(需在爬虫代码中通过logger.info("msg", extra={"url": url})传入) - 别忘了在
QueueListener的 handler 中设置formatter,否则输出是原始对象,不是可读字符串
如何把日志结构化地发给 Elasticsearch
直接用 elasticsearch-py 的 bulk() 批量写入比单条 index() 效率高得多,但要注意字段类型映射。比如 timestamp 字段必须是 date 类型,否则 Kibana 里时间筛选会失效。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 日志 record 的
asctime是字符串,入库前要转成 ISO8601 格式:datetime.fromtimestamp(record.created).isoformat() - 把
record.exc_info转成字符串存进exception字段,方便检索堆栈关键词 - 为防日志爆炸,对
response_body这类大字段做截断(例如只存前 500 字符),并在字段名后加_truncated标记 - 索引名建议带日期后缀,如
spider-logs-2024.06.15,便于按天 rollover 和清理
遇到 “UnicodeEncodeError: 'gbk' codec can't encode…” 怎么办
这是 Windows 控制台默认编码惹的祸,但问题常被误判为日志模块本身的问题。实际只要确保所有 handler 的 stream 或文件打开方式显式指定 encoding='utf-8' 即可,尤其 FileHandler 和自定义的 HTTP handler。
立即学习“Python免费学习笔记(深入)”;
- 不要依赖
sys.getdefaultencoding(),它返回的是 'utf-8',但 Windows 控制台sys.stdout.encoding很可能是 'gbk' - 如果用
RotatingFileHandler,必须传encoding='utf-8',否则中文日志写入时抛异常 - 在日志格式字符串里避免直接拼接未编码的响应体,先用
repr(body[:100])或body[:100].decode('utf-8', 'replace')
lineno、funcName 对分析爬取成功率没用,却占存储;而 retry_times、download_timeout 这类控制参数,漏掉就很难定位超时是否集中在某类请求上。

















