豆瓣的封禁页也返回 200
拿 6341 个真实页面校准判定器,以及豆瓣把「我的广播」改名成「我的动态」那天。
一个抓取器最危险的失败方式不是报错,是把错的东西当成对的存下来。 豆瓣把限流页、验证码页、掉登录后的引导页全部用 HTTP 200 返回。 如果只看状态码,你会得到一份「成功抓取了 1157 个页面」的档案, 里面装的是 1157 份「请先登录」。
每一个响应都要有一个判定
所以每个响应都要过一遍分类,产出六种判定之一:
| 判定 | 意思 | 该怎么办 |
|---|---|---|
ok | 正常页面 | 存下来,继续 |
blocked | 被限流/封禁 | 停机,不重试 |
challenge | 要过验证码 | 暂停,等人 |
login | 会话过期 | 停机(不是可重试的错误) |
gone | 条目已删除 | 记下来,这也是信息 |
soft404 | 返回了 200 的「没这个页面」 | 按失败处理 |
判定依据是内容:导航栏里有没有你的用户名、有没有跳到
sec.douban.com、响应体积相对于滚动分布是不是异常、
该有的结构锚点在不在。判不出来的一律算失败——
没有「大概是对的」这个档位。
封禁页本身也照样写进 WARC。这样以后要重新校准分类器, 不用再抓一次豆瓣,翻旧档案就行。
6341 个页面
作品详情页那条路线的判定描述,是拿一份真实档案里的 6341 个页面校准出来的。 这件事没法在设计阶段做——十五年跨度的豆瓣页面结构差异很大, 坐在屋里想出来的锚点,碰上 2011 年的页面就碎了。
豆瓣把「我的广播」改名了
同一天撞上的:广播页的标题从「我的广播」变成了「我的动态」, 而它正好是那条路线的结构锚点之一。于是整条最高优先级的路线一夜之间全部判成失败。
这个事故本身是设计生效的证明——判定是「失败」而不是「抓到了空数据」, 所以它响了,而不是静静地产出一份空档案。但它也说明锚点的选择要挑 结构性的东西,别挑文案。文案是产品经理的,随时会变。
个人主页不能当事实来源
还有一个同类的:一开始判定和「豆瓣声称有多少条」都是从个人主页上读的。 问题是个人主页是用户可自定义的——分类区块可以隐藏、可以调顺序, 某个类别可能根本不显示。这意味着两件事:判定不能依赖那些区块的存在, 声称数量也不能从那里取。
改成从每一个列表页自己的标题读,比如
<h1>我看过的影视(1333)</h1>。
这个数字必须在抓的当下就记下来,因为它事后不可重建——
当然,它能证明的东西比看上去少,那是
另一篇的话题。