解析一个老网站:GBK 编码、页面结构漂移与限流退避
我维护着一个每天自动跑十几轮的采集任务数据源是一个十几年前的老站。它有个很要命的特点页面只保留最近三个批次过时就没了——漏一轮那批数据就永久缺失没有补档的地方。跑了几个月真正咬人的不是怎么写解析而是三个沉默的错编码猜错 → 抓回来一堆乱码但程序不报错页面改版 → 解析出 0 条程序开心地写下今天没有数据被限流 → 当成这个页面不存在然后那一批就永远缺了。这三个错的共同点它们都不抛异常。下面是我最后怎么把它们一个个变成会响的警报。一、GBK先搞清楚乱码是从哪来的老站大概率是 GBK/GB18030。乱码通常有三种来源处理方式完全不同。第一种信了响应头。很多 HTTP 客户端比如 requests在resp.text时会按响应头的Content-Type: ...; charset来解码老站经常不写 charset或者写了但和实际字节不一致。这时候resp.text是错的而resp.content永远是原始字节——别用.text除非你已经确认过编码。第二种页面上写了 meta但字节不是那个编码。这种最坑因为它看起来有声明。第三种只有一部分是坏的。比如正文正常、某个字段里混进了另一个编码的内容。整页解码会把\ufffd替换字符混进来你不检查就写进库了。我的做法很朴素defdecode(raw:bytes,hint:str|NoneNone)-str:按 声明 → 探测 → 兜底 的顺序解码解不干净就报警绝不静默写库。forencinfilter(None,(hint,gb18030,utf-8)):try:textraw.decode(enc)exceptUnicodeDecodeError:continueif\ufffdnotintext:# 没有替换字符 这次解码是可信的returntextraiseValueError(解码失败候选编码全部出现替换字符别猜先看原始字节)关键在最后那句解不干净就抛异常。我宁可这一轮的任务红着脸失败也不要往库里写一行看起来正常的问号。顺带一个经验gb18030是gbk的超集直接上gb18030基本能覆盖老站不用纠结到底是 GBK 还是 GB2312。二、结构漂移最危险的不是改版是改版后你还在写老站的 class 名会变今天叫list-item下周可能叫new-list-item但结构往往不动一条记录仍然是一行、仍然由同样几个格子组成。所以锚点我尽量选结构不选样式# 差依赖样式类名改版必挂forliinsoup.select(div.list-item):...# 好靠结构定位行 格子改版时更可能还活着ROW_REre.compile(rtr[^]*data-id([^])[^]*(.*?)/tr,re.S)但真正救命的是断言。解析器必须能区分这个页面今天确实没有记录和我的解析器已经不认识了rowsparse(html)ifnotrows:raiseRuntimeError(解析到 0 条 —— 先确认是页面真空了还是改版了别写空数据)forrinrows:iflen(r)5:raiseRuntimeError(字段数不对期望 5 个格子实际 %d —— 结构变了%len(r))这一段看起来像废话但它救过我一次某天页面换了版式解析器返回 0 条如果当时没断言程序会安安静静地把今天没有比赛写进记录然后那一整批就这么丢了而日志里连个警告都没有。三、限流与退避429、418、521 是三件事采集里最常见的谎话是重试就行。其实要先分类状态含义该怎么办429标准限流退避后重试指数 随机抖动418/403常见于站点前面的 WAF“请求疑似攻击行为”重试基本没用换节奏/换出口并且要报警521/ 502源站抖了退避重试通常几次就过域名解析失败本机网络问题不算作该批不存在下次跑要自动补重试要带抖动否则多个任务会在同一秒一起回来把限流撞得更狠defretry(fn,tries3,base0.5):lastNoneforiinrange(tries):try:returnfn()exceptRateLimitedasexc:lastexcifitries-1:time.sleep(base*(2**i)random.uniform(0,base))# 指数 抖动raiselast最要紧的一条失败必须落进待补绝不能和空混在一起。我的状态文件分两块{done:{p1:3},pending:{p3:FileNotFoundError: 页面不存在p3}}done只记已经拿到的pending记这轮没拿到的。下一轮开始先看pending能补就补。这样断网和这批真的没有就能分开了——采集任务最容易犯的错就是把失败静默成空。四、让任务自己说话每轮一行结论最后一件小事但收益最大每轮结束只打印一行结论。结论: 页面 3 个成功 2 / 失败 1/ 读到 5 行 / 新增 0 条 / 补空 1 个 / 跳过 4 条 → records.csv 累计 5 条待补 1 项下次自动补以前我也写详细日志结果是出问题时没人愿意翻几千行。现在判断任务状态只需要看两类东西结论行本轮干了什么、有没有失败、待补几项启动/结束两个标记是否配对只有启动没有结束就是被中途杀了而不是跑完了没事。配套还有两条纪律幂等只补空、不覆盖已有值。重跑、补跑永远安全所以把它再跑一遍是一个廉价动作而不是一次冒险。状态原子落盘先写.tmp再os.replace。进程被强杀也不会留下半个 JSON。五、小结老站采集的难点不在解析在**“错了但不说”**。就三条判据幂等重跑不会写坏数据失败可区分限流 / 改版 / 断网各走各的路失败落待补不静默成空可判读每轮一行结论人看结论不看日志。对着这三条把一个采集任务放三个月不管它基本还是稳的。

相关新闻

SISSO稀疏符号回归构建可解释钙钛矿稳定性因子

SISSO稀疏符号回归构建可解释钙钛矿稳定性因子

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 1:28:29 阅读更多 →
Python高光谱遥感影像识别与分类:从数据读取到3D-CNN实战

Python高光谱遥感影像识别与分类:从数据读取到3D-CNN实战

简介:这份资源面向计算机、遥感与人工智能方向的本科生和研究生,适用于毕业设计、课程设计及项目开发场景,围绕高光谱遥感影像识别与分类这一课题提供完整实现方案。压缩包共24个文件,约6.01MB,以13个Python源码文件为…

2026/10/11 1:28:29 阅读更多 →
基于PI重复控制的并联型APF Matlab仿真与谐波抑制

基于PI重复控制的并联型APF Matlab仿真与谐波抑制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 1:28:29 阅读更多 →

最新新闻

基于Spring Boot + Vue的蘑菇百科系统设计与实现指南

基于Spring Boot + Vue的蘑菇百科系统设计与实现指南

毕设选题年年有人纠结,年年有人踩坑。如果你正盯着“XX管理系统”这类老掉牙的题目发愁,或者担心做纯网页展示类项目显得工作量不足,我强烈建议你认真看看“基于Spring Boot Vue的蘑菇百科系统”这个方向。它既有信息管理系统的完整业务链路…

2026/10/11 2:23:00 阅读更多 →
PJ85718DM与PIC18LF46K40在HVAC温控系统中的高可靠设计

PJ85718DM与PIC18LF46K40在HVAC温控系统中的高可靠设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 2:23:00 阅读更多 →
Kubernetes 内存超分与防爆死指南:基于 cgroup v2 memory.high 的平滑降级实操

Kubernetes 内存超分与防爆死指南:基于 cgroup v2 memory.high 的平滑降级实操

在 Kubernetes 生产集群的算力成本治理中,CPU 属于典型的“可压缩资源(Compressible Resource)”,当算力超卖或并发突刺时,CFS 调度器最多让应用稍微慢一点、延迟稍微抖动一下,进程本身并不会消亡。而内存则…

2026/10/11 2:23:00 阅读更多 →
Git误操作急救:reflog与fsck恢复丢失代码

Git误操作急救:reflog与fsck恢复丢失代码

git误操作这种事,凡是写过代码的人多多少少都遇到过。可能是午休回来手一抖,把分支 reset 到了一周前;也可能是合并的时候顺手 rebase 了一下,push 时才被远程仓库一口回绝;更常见的是git checkout -- .之后&#xff0…

2026/10/11 2:23:00 阅读更多 →
Linux进程控制实战:fork、exec、wait与僵尸进程排查指南

Linux进程控制实战:fork、exec、wait与僵尸进程排查指南

写Linux程序的人,几乎都要跟进程控制打交道。fork、exec、exit、wait这四个词,翻过书的人都能念出来,但真正把它们组合起来用对,才是区分“看过”和“会写”的分水岭。我见过不少开发者在多进程服务里栽跟头,要么子进程…

2026/10/11 2:23:00 阅读更多 →
Linux网络性能优化实战:从内核参数调整到tcpdump抓包排查

Linux网络性能优化实战:从内核参数调整到tcpdump抓包排查

某个深夜,线上接口的P99延迟从60ms一路冲到700ms,我第一时间就想做一轮Linux网络性能优化与监控,于是把网上那套内核参数调优脚本挨个灌进去,tcp_tw_reuse、tcp_max_syn_backlog全都改了。结果延迟没降,反而有机器连接…

2026/10/11 2:22:00 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →