4、Python法律文本处理基础:正则表达式在法条提取中的应用、jieba分词与法律词典定制、停用词过滤与文本清洗
做法律大模型,第一步不是调模型,而是搞定文本。
法律文本有多「脏」,做过的人都知道。条款编号不统一、标点符号乱飞、各种「的」「地」「得」混着用。我刚开始做法律NLP项目时,光清洗数据就花了两周。嗯,今天就把这些坑一次性讲清楚。
4.1 正则表达式:法条提取的瑞士军刀
正则表达式,说白了就是一套「文本匹配规则」。你告诉它你要找什么模式,它帮你从海量文本里捞出来。
核心场景:从裁判文书、法律法规中提取条款编号、案号、日期等结构化信息。
4.1.1 法条编号提取
法律文本里最常见的模式是什么?「第X条」「第X章」「第X节」。我见过最离谱的写法是「第 一 条」中间带空格的。正则表达式怎么处理?
import re
text = "根据《中华人民共和国合同法》第一百零七条、第二百零一条的规定..."
# 提取所有"第X条"模式
pattern = r'第[一二三四五六七八九十百千零]+条'
matches = re.findall(pattern, text)
print(matches) # ['第一百零七条', '第二百零一条']
# 更通用的写法:支持阿拉伯数字和中文数字
pattern2 = r'第[\d一二三四五六七八九十百千零]+条'
matches2 = re.findall(pattern2, text)
print(matches2) # ['第一百零七条', '第二百零一条']
我的经验:中文数字转阿拉伯数字是个大坑。我曾经写过一个转换函数,把「一百零七」转成107,结果「二百零一」转成了201。后来发现「零」的处理要特别小心——「一百零七」是107,「一百一十」是110。建议直接用字典映射,别自己写逻辑。
4.1.2 案号提取
案号格式五花八门:「(2023)京01民初123号」「(2022)沪0115刑初456号」。括号有全角半角,年份有四位两位。
text = "本院于2023年5月10日立案受理了(2023)京01民初123号案件"
pattern = r'[((]\d{4}[))][\u4e00-\u9fa5]{2,4}\d{1,4}[民刑行]初?\d+号'
match = re.search(pattern, text)
if match:
print(match.group()) # (2023)京01民初123号
注意:不同法院的案号格式差异很大。基层法院用「民初」,中级法院用「民初」或「民终」,高级法院还有「民再」。我建议你建一个案号格式库,把常见模式都列进去。
4.1.3 日期提取
法律文书里的日期格式:「2023年5月10日」「2023-05-10」「2023/05/10」。还有更坑的:「二〇二三年五月十日」。
text = "判决日期:二〇二三年五月十日"
# 中文日期转数字
chinese_num = {'〇': '0', '一': '1', '二': '2', '三': '3', '四': '4',
'五': '5', '六': '6', '七': '7', '八': '8', '九': '9'}
for cn, num in chinese_num.items():
text = text.replace(cn, num)
print(text) # 判决日期:2023年5月10日
4.2 jieba分词与法律词典定制
分词是中文NLP的基础。但通用分词器对法律文本效果很差——「原告」「被告」「诉讼请求」这些词,jieba默认可能给你切成「原」「告」「诉讼」「请求」。
4.2.1 为什么需要法律词典?
你想想看,法律文本里有大量专有名词:
- 法律术语:「不可抗力」「违约责任」「诉讼时效」
- 机构名称:「最高人民法院」「北京市第一中级人民法院」
- 案由:「民间借贷纠纷」「买卖合同纠纷」
jieba默认词典里没有这些词,分出来就是一堆碎片。
4.2.2 定制法律词典
import jieba
# 加载自定义词典
jieba.load_userdict('legal_dict.txt')
# legal_dict.txt 内容示例:
# 不可抗力 5 n
# 违约责任 5 n
# 诉讼时效 5 n
# 最高人民法院 5 nt
# 民间借贷纠纷 5 nz
text = "因不可抗力导致合同无法履行,不承担违约责任"
words = jieba.lcut(text)
print(words)
# 输出:['因', '不可抗力', '导致', '合同', '无法', '履行', ',', '不', '承担', '违约责任']
我的习惯:词典文件每行三个字段:词、词频、词性。词频建议设5-10,太高会影响其他词的分词效果。词性标注用n(名词)、nt(机构名)、nz(其他专名)。
4.2.3 动态添加与删除
# 动态添加
jieba.add_word("诉讼请求")
jieba.add_word("事实与理由")
# 动态删除(防止错误分词)
jieba.del_word("诉讼")
jieba.del_word("请求")
避坑指南:我曾经在项目中动态添加了1000多个法律词,结果分词速度从每秒10万字降到了2万字。后来发现是词典太大导致的。建议:只添加高频且确实需要合并的词,别一股脑全加进去。
4.3 停用词过滤与文本清洗
法律文本里充斥着大量「的」「了」「在」「是」这类停用词。它们对语义理解没什么帮助,反而会增加噪声。
4.3.1 构建法律停用词表
通用停用词表对法律文本不够用。我建议你专门建一个法律停用词表:
# legal_stopwords.txt 示例
的
了
在
是
为
所
被
由
于
以
及
等
其
该
之
与
或
但
而
且
虽
然
因为
所以
因此
据此
为此
鉴于
关于
对于
按照
依照
根据
def load_stopwords(filepath):
with open(filepath, 'r', encoding='utf-8') as f:
stopwords = set([line.strip() for line in f])
return stopwords
stopwords = load_stopwords('legal_stopwords.txt')
def filter_stopwords(words, stopwords):
return [w for w in words if w not in stopwords and len(w) > 1]
text = "根据《中华人民共和国合同法》的规定,当事人应当按照约定履行自己的义务"
words = jieba.lcut(text)
filtered = filter_stopwords(words, stopwords)
print(filtered)
# 输出:['中华人民共和国合同法', '规定', '当事人', '按照', '约定', '履行', '自己', '义务']
注意:「自己」在法律文本中有时有特殊含义(如「自己代理」),要不要过滤取决于你的任务。我一般保留长度大于1的词,因为单字词在法律文本中信息量很低。
4.3.2 文本清洗全流程
一个完整的法律文本清洗流程,我总结为五步:
- 去除无关字符:HTML标签、特殊符号、多余空格
- 统一格式:全角转半角、繁体转简体
- 正则提取:提取法条、案号、日期等结构化信息
- 分词与词典匹配:加载法律词典,提高分词准确率
- 停用词过滤:去除无意义词,保留关键信息
import re
import jieba
def clean_legal_text(text):
# 1. 去除HTML标签
text = re.sub(r'<[^>]+>', '', text)
# 2. 全角转半角
text = text.replace(',', ',').replace('。', '.').replace(';', ';')
text = text.replace('(', '(').replace(')', ')').replace('《', '<').replace('》', '>')
# 3. 去除多余空格和换行
text = re.sub(r'\s+', ' ', text)
# 4. 提取法条(示例)
articles = re.findall(r'第[\d一二三四五六七八九十百千零]+条', text)
# 5. 分词
words = jieba.lcut(text)
# 6. 停用词过滤
stopwords = load_stopwords('legal_stopwords.txt')
filtered = [w for w in words if w not in stopwords and len(w) > 1]
return filtered, articles
text = "<p>根据《合同法》第107条,当事人一方不履行合同义务的,应当承担违约责任。</p>"
words, articles = clean_legal_text(text)
print("分词结果:", words)
print("提取法条:", articles)
4.4 本章知识体系
下面这张图展示了本章的核心逻辑:从原始法律文本到结构化数据的完整流程。
说白了,这三步是法律NLP的「基本功」。正则负责「捞」结构化信息,分词负责「切」出语义单元,清洗负责「去」掉噪声。每一步都有坑,但踩过之后,你会发现法律文本其实没那么可怕。
总结:法律文本处理的核心不是技术有多炫,而是对法律文本模式的理解有多深。正则表达式、分词、清洗,这三板斧用好了,80%的法律NLP问题都能解决。