4. 文本预处理

在法规文本订阅与影响识别系统中,原始文本往往来自政府公报、监管网站、PDF 附件、邮件推送或第三方数据接口。这些文本在进入分类模型和影响识别引擎之前,必须经过一套稳定的预处理流水线。预处理的质量直接决定了后续订阅匹配的准确率、影响识别的召回率以及元数据检索的可用性。本节围绕清洗、分段、标题识别、元数据抽取与标准化五个核心环节展开,帮助学员建立可落地的工程化处理思路。

一、核心原理:预处理是“信息保真”与“结构重建”的平衡

法规文本具有三个显著特征:第一,格式噪声多,PDF 转换后常出现页眉页脚、断行、乱码、全角半角混排;第二,结构层级强,章、节、条、款、项、目构成严格的逻辑树;第三,元数据价值高,发文机关、文号、发布日期、生效日期、失效日期、效力级别等字段是订阅分类的关键特征。预处理的核心原理可以概括为:在去除噪声的同时,尽可能保留法规的语义边界和结构信号,并将非结构化文本转化为带标签的结构化对象。

如果清洗过度,可能删除“第X条”等关键结构标记;如果清洗不足,页眉页脚会污染标题识别和分段结果。因此,预处理不是简单的“文本替换”,而是一个带有领域规则的解析过程。

二、方法流程:五步流水线

建议将预处理设计为可配置的流水线,每一步输出中间结果,便于调试和回溯。典型流程如下:

  1. 清洗:去除页眉页脚、页码、水印、多余空白、控制字符;统一全角/半角、繁简、换行符;修复 PDF 断行导致的词语断裂。
  2. 分段:依据法规结构标记(章、节、条、款、项)和自然段边界,将长文本切分为语义完整的段落单元。
  3. 标题识别:识别文档标题、章标题、节标题、条标题,并建立层级关系。
  4. 元数据抽取:从正文和属性区提取发文机关、文号、发布日期、生效日期、效力级别、主题词等字段。
  5. 标准化:对日期、机关名称、文号、效力级别进行归一化,形成统一编码和可比较字段。

工程建议:每一步都保留原始偏移量(offset),这样当后续分类或影响识别出现问题时,可以快速定位到原文位置,避免“黑箱”处理。

三、关键环节详解

1. 清洗:规则与正则的组合

清洗阶段最常见的噪声包括:页眉页脚重复行、页码、PDF 转换产生的孤立字符、HTML 标签残留、全角空格、连续空行。可以使用正则表达式和行频统计相结合的方式。例如,统计每行在文档中出现的次数,若某行重复出现且长度较短,很可能是页眉页脚。

import re

def clean_text(raw: str) -> str:
    # 去除 HTML 标签
    text = re.sub(r'<[^>]+>', '', raw)
    # 统一换行符
    text = text.replace('\r\n', '\n').replace('\r', '\n')
    # 全角转半角(保留中文标点)
    text = text.replace(' ', ' ')
    # 去除连续空行
    text = re.sub(r'\n{3,}', '\n\n', text)
    # 去除行首行尾空白
    lines = [line.strip() for line in text.split('\n')]
    return '\n'.join(lines)

2. 分段:以“条”为最小语义单元

法规文本的分段不能简单按句号或换行切分。推荐以“第X条”作为最小语义单元,因为“条”是法规中最稳定的引用单位。对于“款”和“项”,可以在“条”内部继续切分。分段时需要处理跨页断行、条款编号缺失、以及“第X条之一”等特殊形式。

import re

ARTICLE_PATTERN = re.compile(r'^第[一二三四五六七八九十百零〇\d]+条')

def split_articles(text: str):
    lines = text.split('\n')
    articles = []
    current = []
    for line in lines:
        if ARTICLE_PATTERN.match(line.strip()):
            if current:
                articles.append('\n'.join(current))
            current = [line.strip()]
        else:
            if current:
                current.append(line.strip())
    if current:
        articles.append('\n'.join(current))
    return articles

3. 标题识别:层级与模式并重

标题识别需要同时处理文档标题、章标题、节标题和条标题。文档标题通常位于正文开头,字体较大或独立成行;章标题以“第X章”开头;节标题以“第X节”开头;条标题以“第X条”开头。对于没有显式“章/节”标记的规范性文件,可以通过编号模式(如“一、”“(一)”)推断层级。

4. 元数据抽取:规则优先,模型辅助

元数据抽取建议采用“规则优先、模型辅助”的策略。发文机关、文号、日期等字段具有强模式,适合用正则和词典匹配;主题词、效力级别等字段可以结合分类模型。常见字段及抽取方式如下表所示:

字段 典型模式 抽取方式 标准化目标
发文机关 “国务院”“中国人民银行” 词典匹配 + 位置规则 统一机构编码
文号 “国发〔2024〕1号” 正则 机关简称+年份+序号
发布日期 “2024年3月15日” 正则 + 日期解析 ISO 8601
生效日期 “自2024年5月1日起施行” 正则 + 语义规则 ISO 8601
效力级别 “行政法规”“部门规章” 规则 + 分类模型 枚举编码

5. 标准化:让字段可比较、可检索

标准化是预处理的最后一公里。日期统一为 YYYY-MM-DD;机关名称统一为规范全称并映射到机构编码;文号统一为“机关简称〔年份〕序号”;效力级别统一为枚举值。标准化后的元数据可以直接用于订阅规则匹配和影响识别特征工程。

提示:标准化不是一次性工作,建议建立“机关名称别名词典”和“效力级别映射表”,并支持人工审核和增量更新。

四、案例场景:一条法规从原始文本到结构化对象

假设我们收到一份 PDF 转换后的文本,内容包含页眉“国务院公报”、页码“- 3 -”、正文标题“国务院关于修改《XX条例》的决定”、文号“国发〔2024〕5号”、发布日期“2024年2月1日”、生效日期“自2024年4月1日起施行”。经过清洗后,页眉页码被去除;分段后得到标题段、文号段、正文条款段;标题识别标记出文档标题和“第一条”“第二条”;元数据抽取得到发文机关“国务院”、文号“国发〔2024〕5号”、发布日期“2024-02-01”、生效日期“2024-04-01”、效力级别“行政法规”。最终输出一个 JSON 对象,供后续订阅分类和影响识别使用。

法规文本预处理五步流水线 原始文本 PDF/HTML/邮件 清洗 去噪/统一格式 分段 条/款/项切分 标题识别 层级结构重建 元数据 抽取+标准化 结构化输出:标题树 + 条款列表 + 元数据 JSON doc_title / chapter / article / agency / doc_no / publish_date / effective_date / level 订阅分类 主题/机关/效力 影响识别 条款级匹配 检索与审计 元数据过滤

五、误区与复盘

常见误区一:清洗过度。把“第X条”中的“第”字或括号当作噪声删除,导致分段失败。建议清洗规则先白名单后黑名单,保留法规结构标记。

常见误区二:分段只按换行。PDF 转换后换行位置不可靠,必须结合条款编号和语义边界。建议以“条”为主锚点,辅以段落长度和标点特征。

常见误区三:元数据只抽不标准化。“国务院”和“中华人民共和国国务院”如果不统一,订阅规则会漏匹配。建议建立别名词典和编码映射。

常见误区四:忽略中间结果留存。预处理一旦出错,无法回溯到原文位置。建议每一步输出带偏移量的中间文件,并记录处理日志。

复盘时,可以用一组已知法规作为回归测试集,检查清洗后是否丢失条款、分段数量是否与人工标注一致、标题层级是否正确、元数据字段是否完整。只有预处理稳定,后续的订阅分类和影响识别才有可靠的基础。