3. 订阅机制入门

法规文本订阅不是“把网页加到收藏夹”那么简单。它要解决四个连续问题:从哪里取、多久取一次、每次取多少、取回来如何判断新旧。这四个问题分别对应订阅源配置、更新频率、增量抓取与去重策略。任何一环设计不当,都会导致漏采、重复入库、解析失败或存储成本失控。本节从工程视角拆解这套机制,并给出可直接落地的配置与判断逻辑。

一、核心原理:订阅是一条“状态化”的数据管道

订阅的本质,是把外部法规站点的“变化”转化为本地可识别的“事件”。它依赖三个关键状态:源状态(站点是否可达、结构是否变化)、抓取状态(上次抓取时间、游标、分页位置)、内容状态(正文指纹、标题、文号、生效日期)。只有把这三类状态持久化,增量与去重才有依据。

订阅源配置决定了管道的入口。一个法规订阅源通常包含:源名称、入口 URL、列表页规则、详情页规则、编码、请求头、限速、更新频率、增量标识字段。配置不是写死在代码里,而应外置为可版本管理的 YAML 或 JSON,便于审计与回滚。

source:
  name: "国务院政策文件库"
  entry: "https://www.gov.cn/zhengce/zuixin.htm"
  list_selector: "ul.list li a"
  detail_selector: "div.pages_content"
  encoding: "utf-8"
  headers:
    User-Agent: "RegWatch/1.0 (+contact@example.com)"
  rate_limit: "1 req / 3s"
  frequency: "every 6h"
  incremental_key: "publish_date"
  dedup_key: ["title", "doc_number"]
订阅源配置的核心原则:入口稳定、规则可测、频率可调、标识可判。入口频繁改版时,应优先选择 RSS、站点地图或开放 API,而不是硬爬 HTML。

二、更新频率:在时效性与站点压力之间取平衡

更新频率不是越密越好。频率过高会触发反爬、浪费带宽,并放大解析错误;频率过低则可能错过法规的征求意见窗口或生效节点。实践中可按“法规类型 + 发布规律”分层设置:

  • 高频源:政策发布厅、征求意见稿栏目,建议 2~6 小时一次。
  • 中频源:地方性法规、部门规章,建议 12~24 小时一次。
  • 低频源:年度汇编、标准全文库,建议每日或每周一次。
  • 事件驱动:对已订阅的详情页,可在生效日前 7 天提高检查频率。

频率配置还应支持“退避策略”:连续失败时自动拉长间隔,恢复后回归正常。这比固定轮询更稳健。

三、增量抓取:用游标与指纹减少无效请求

增量抓取的目标是“只取新增或变更的部分”。常见做法有三种:时间游标(记录上次最大发布时间)、分页游标(记录已抓页码或偏移量)、内容指纹(对标题+正文做哈希)。时间游标适合列表按时间倒序的站点;分页游标适合有稳定分页的栏目;内容指纹适合详情页可能被修订的场景。

一个可用的增量判断流程如下:先请求列表页,提取候选链接与发布时间;与本地游标比较,过滤掉早于游标且指纹未变的条目;对剩余条目请求详情页,计算正文指纹;若指纹已存在则跳过入库,否则写入并更新游标。整个过程应记录日志,便于复盘漏采原因。

def incremental_fetch(source, cursor):
    items = parse_list(source.entry)
    new_items = []
    for it in items:
        if it.publish_date <= cursor.last_publish:
            continue
        detail = fetch_detail(it.url)
        fp = sha256(detail.title + detail.body)
        if fp in cursor.fingerprints:
            continue
        new_items.append((it, detail, fp))
    return new_items

四、去重策略:多键组合比单一标题更可靠

法规文本的重复来源很多:同一文件被多个栏目转载、标题略有差异、正文含附件、文号相同但版本不同。仅靠标题去重会误杀,仅靠 URL 去重会漏掉转载。推荐使用多键组合 + 指纹的分层去重:

去重层级判断键适用场景风险
URL 级规范化 URL同一页面重复抓取无法识别转载
文号级发文机关 + 文号正式文件唯一标识部分文件无文号
标题级标题归一化快速初筛标题相似但内容不同
指纹级正文 SHA-256识别修订与转载附件变化需单独处理
建议入库时保留“首次发现时间”和“最近更新时间”两个字段。这样即使指纹相同,也能区分“新发布”与“旧文重发”,对法规影响识别非常关键。

五、案例场景:一次漏采与一次重复入库的复盘

场景一:漏采。某地方规章在周五 18:00 发布,订阅频率为每日 9:00。结果周一才抓到,错过了周末的征求意见截止提醒。复盘发现:频率未按栏目发布规律调整,且未对“征求意见”类源单独提频。改进:对征求意见栏目设置 4 小时频率,并增加“截止日期临近”告警。

场景二:重复入库。同一文件被“政策文件”和“政策解读”两个栏目转载,标题分别为“XX办法”和“XX办法(全文)”。仅按标题去重失败,导致两条记录。复盘发现:未使用文号与正文指纹组合。改进:以文号为主键,正文指纹为辅,标题仅作展示。

去重不是“删数据”,而是“合并数据”。被判定为重复的记录应保留来源链接与抓取时间,便于追溯与审计,而不是直接物理删除。

六、常见误区与检查清单

  • 误区一:频率越高越好。高频会触发封禁,且增加解析噪声。
  • 误区二:只存正文不存元数据。缺少发布时间、文号、来源,后续影响识别无法回溯。
  • 误区三:去重只做一次。应在入库前、入库后、影响识别前分阶段校验。
  • 误区四:忽略编码与附件。GBK 页面乱码、PDF 附件未解析,都会导致指纹失真。

落地时可用以下清单自检:源配置是否外置并可版本管理;频率是否按栏目分层;增量游标是否持久化;指纹是否覆盖标题与正文;去重是否多键组合;失败是否有退避与告警;日志是否记录候选数、新增数、跳过数。把这七项做扎实,订阅机制才算真正入门。

法规订阅机制:从源配置到去重入库 订阅源配置 入口 / 规则 / 限速 频率 / 增量键 更新频率调度 高频 / 中频 / 低频 失败退避 增量抓取 时间游标 / 分页游标 正文指纹 去重入库 多键组合 指纹校验 状态持久化:源状态 · 抓取游标 · 内容指纹 · 首次/最近发现时间 没有状态,就没有真正的增量与去重 漏采风险 频率过低 / 游标未更新 编码或附件解析失败 重复入库风险 仅标题去重 / 转载未识别 指纹未覆盖正文 改进方向 分层频率 + 退避告警 文号 + 指纹组合去重 订阅机制 = 可配置的源 + 可调度的频率 + 可判断的增量 + 可追溯的去重