简介:这份资源面向法律科技方向的开发者、Python 学习者与智能问答项目实践者,围绕一个基于中文预训练模型的法律问答应用展开,可用于搭建智能法律咨询原型、练习文本相似匹配与 Web 服务集成。压缩包共 25 个文件,约 371.77MB,以 Python 脚本、HTML 模板、JS/CSS 静态资源、PNG 图片及 CSV 数据集为主,另含 Visio 流程图、模型配置与词表文件,覆盖数据处理、模型推理与前端展示的完整链路。其中 40000 条法律问答数据集可直接用于检索或训练,句子相似匹配脚本与中文 BERT 模型配合,能实现用户提问与法条答案的语义对齐;数据分析脚本负责清洗与探索,主程序与启动脚本则串联起后端逻辑和页面路由,模板与静态目录提供问答输入与结果展示界面。目前已有 139 人学习,适合想了解法律问答系统从数据到部署全流程的读者参考。
1. 法律.rar 里到底装了什么:从文件名到落地场景的一次拆解
第一次看到「法律.rar」这个文件名,我下意识以为是某位法务同事发来的合同归档包。解压之后才发现,它更像一份被压缩过的法规资料合集——里面通常是按法律门类分目录的文本文件、PDF 或 Word 文档,覆盖民法典、劳动法、合同法、公司法等常用领域。这类资源在工程圈其实有稳定需求:做合同管理系统的要拿它当测试语料,做法律问答机器人的要拿它当知识库底料,写合规检查脚本的要拿它当关键词来源。它解决的不是「学法律」的问题,而是「手头没有一份结构清晰的法规文本可用」的问题。适合谁?做 NLP 语料预处理的后端、需要本地法规检索的产品经理、以及想搭一个离线法律查询工具的个人开发者。如果你只是偶尔查一条法条,浏览器搜索更快;但如果你要批量处理、离线检索或喂给模型,这份压缩包就值得拆开看看。
2. 解压后先别急着读:目录结构与编码排查
拿到压缩包,第一件事不是打开文档,而是看清楚它怎么组织的。法律类资料集常见的坑集中在两处:目录层级混乱和文本编码不统一。这两个问题不解决,后面写脚本批量处理时全是玄学报错。
2.1 用命令行先摸清目录骨架
我一般不会直接双击解压,而是先用命令行看一眼压缩包内部结构,避免解压出一堆乱码文件名。
# 查看压缩包内文件列表,不实际解压 unrar l 法律.rar # 如果系统没有 unrar,用 7z 替代 7z l 法律.rar # 解压到指定目录,保持目录结构 unrar x 法律.rar ./law_data/逻辑说明:l参数只列出内容,适合先判断文件数量和层级;x参数按完整路径解压,保留原始目录树。参数上,./law_data/是我习惯的落地目录,避免解压到当前目录后和项目文件混在一起。如果列表里出现大量__MACOSX或.DS_Store,那是打包时混入的系统文件,解压后直接删掉,不要让它污染后续遍历。
2.2 编码检测:GBK 和 UTF-8 混用是常态
法律文本文件最常见的翻车现场是:用 Python 默认utf-8读取时报UnicodeDecodeError,因为不少早期整理的 txt 是 GBK 编码。我一般先用chardet批量探测,再决定统一转码策略。
import chardet from pathlib import Path # 遍历目录下所有 txt,探测编码 for p in Path("./law_data").rglob("*.txt"): raw = p.read_bytes()[:2000] # 只取前 2KB 探测,加快速度 result = chardet.detect(raw) print(f"{p.name} -> {result['encoding']} (置信度 {result['confidence']:.2f})")逻辑说明:read_bytes()[:2000]只读文件头部,避免大文件全量加载;chardet.detect返回编码和置信度。参数上,置信度低于 0.7 的建议人工抽查,不要盲目批量转码。确认编码后,统一转成 UTF-8 再入库,后续检索和模型训练都省心。
提示:如果目录里同时有 PDF 和 txt,先处理 txt,PDF 单独走解析流程,不要混在一个脚本里。
3. 把法规文本变成可检索数据:切分、清洗与入库
解压和编码只是第一步。真正让这份资源产生价值的,是把它变成结构化、可查询的数据。法律文本有很强的层级特征——编、章、节、条、款、项,利用这个结构做切分,比按固定字数硬切准确得多。
3.1 按「条」切分:正则匹配与边界处理
法律条文的核心单位是「第X条」。我一般用正则先定位条文起始位置,再按位置切片。
import re def split_by_article(text): # 匹配「第X条」开头,X 支持中文数字和阿拉伯数字 pattern = re.compile(r'(第[一二三四五六七八九十百千\d]+条)') parts = pattern.split(text) articles = [] # parts[0] 是第一条之前的序言,从 1 开始两两组合 for i in range(1, len(parts), 2): title = parts[i] body = parts[i + 1] if i + 1 < len(parts) else "" articles.append({"title": title, "content": body.strip()}) return articles逻辑说明:pattern.split会把文本按「第X条」切成列表,奇数位是条号,偶数位是正文。参数上,正则里的中文数字范围覆盖了常见写法,如果遇到「第 一 条」这种带空格的,需要先在清洗阶段去掉条号内部空格。切分后建议检查一下有没有空 content,那通常是条号被误匹配或原文本身缺失。
3.2 清洗规则:去掉页眉页脚和多余空白
从 PDF 转来的文本经常夹带页码、页眉、换行符混乱。我一般写一个清洗函数,把常见噪声一次性处理掉。
def clean_text(text): # 去掉纯数字行(页码) text = re.sub(r'^\s*\d+\s*$', '', text, flags=re.MULTILINE) # 去掉常见页眉关键词行 text = re.sub(r'^\s*(第\s*\d+\s*页|共\s*\d+\s*页)\s*$', '', text, flags=re.MULTILINE) # 合并被硬换行拆断的句子:中文行尾无标点则与下一行拼接 text = re.sub(r'([^\n。;:!?])\n([^\n])', r'\1\2', text) # 压缩连续空白 text = re.sub(r'[ \t]+', ' ', text) return text.strip()逻辑说明:前两条正则清理页码和页眉;第三条处理 PDF 转文本时常见的「一句话被换行拆开」问题,判断依据是行尾没有中文标点;最后压缩空格。参数上,flags=re.MULTILINE让^和$匹配每行首尾,不加的话只能匹配整个字符串的开头结尾。
3.3 入库:SQLite 做本地全文检索够用了
切分清洗完,落到 SQLite 里最省事,不用起服务,单文件携带方便。建表时把条号、正文、来源文件都存上,方便回溯。
import sqlite3 conn = sqlite3.connect("law.db") conn.execute(""" CREATE TABLE IF NOT EXISTS articles ( id INTEGER PRIMARY KEY AUTOINCREMENT, source_file TEXT, article_no TEXT, content TEXT ) """) conn.execute("CREATE INDEX IF NOT EXISTS idx_article_no ON articles(article_no)") conn.commit()逻辑说明:source_file记录条文来自哪个文件,排查问题时能定位;article_no建索引,按条号查询快。参数上,如果后续要做全文检索,可以再建 FTS5 虚拟表,但对几千条法规来说,LIKE查询已经够快,不必过度设计。
注意:入库前先去重。同一部法律可能在多个文件里重复出现,按
article_no + content做唯一性判断,避免检索结果里全是重复条目。
4. 避坑与排查:法律.rar 处理中最容易翻车的五件事
这类资源看起来只是文本,但实际处理时坑很集中。下面五条是我和身边人踩过的,按「现象 → 原因 → 解决」整理,遇到问题可以直接对号入座。
4.1 解压后文件名乱码
现象:解压出来的文件名显示为µÂÂÉ之类乱码。原因:压缩包在 Windows 下用 GBK 打包,Linux 或 macOS 默认按 UTF-8 解析文件名。解决:用unrar x -p指定密码(如果有),或在7z里加-mcp=936指定代码页;更稳妥的做法是在 Windows 环境下解压后再拷贝到目标系统。
4.2 条文切分后内容错位
现象:切出来的「第X条」正文里混入了下一条的条号。原因:正则匹配到了正文中引用的「依据第X条」这类交叉引用。解决:把匹配锚定到行首,用re.MULTILINE配合^\s*第[一二三四五六七八九十百千\d]+条,只匹配独立成行的条号,不匹配行内引用。
4.3 PDF 转文本后段落全乱
现象:转出来的文本每行都很短,句子被拆得七零八落。原因:PDF 按版面坐标输出文本,换行位置和语义无关。解决:优先找原始 txt 或 Word 版本;只有 PDF 时,用pdfplumber按文本框提取,再走 3.2 的合并逻辑,不要直接用pdftotext的默认输出。
4.4 编码转换后出现问号
现象:GBK 转 UTF-8 后,部分生僻字变成?。原因:原文件在 GBK 里就用了替换字符,信息已经丢失。解决:这种属于不可逆损坏,只能回到原始文件重新导出;批量转码前先备份,转完抽查生僻字段落。
4.5 检索结果排序不合理
现象:搜「合同」时,短条文排在长条文后面,重点不突出。原因:默认按主键排序,没有相关性权重。解决:如果上了 FTS5,用bm25()排序;没上 FTS5 时,可以按「条号出现位置 + 内容长度」做简单加权,把核心条款往前排。
5. 进阶用法:把法规库接进本地问答流程的一个具体技巧
数据入库只是起点。如果你想让这份资源真正好用,可以把它接进一个本地检索增强的问答流程:用户提问 → 检索相关条文 → 把条文作为上下文交给模型 → 生成回答。这里不展开模型部署,只讲一个我反复用到的技巧:用条号做检索锚点,而不是纯语义相似度。
法律问答有个特点——用户问「试用期最长多久」,语义检索可能召回一堆劳动法条文,但真正相关的是《劳动合同法》第十九条。纯向量检索容易把「试用期」相关的多条都拉出来,排序不稳定。我的做法是:先用关键词匹配定位到可能的条号范围,再用条号去数据库精确取条文,最后才用语义相似度做二次排序。
def retrieve_articles(query, conn, top_k=5): # 第一步:关键词粗筛,缩小条号范围 keywords = ["试用期", "劳动合同", "期限"] like_clause = " OR ".join(["content LIKE ?" for _ in keywords]) params = [f"%{k}%" for k in keywords] rows = conn.execute( f"SELECT article_no, content FROM articles WHERE {like_clause}", params ).fetchall() # 第二步:按条号排序,优先取条号靠前的核心条款 rows.sort(key=lambda r: r[0]) return rows[:top_k]逻辑说明:LIKE粗筛负责召回,条号排序负责把核心条款提前。参数上,top_k控制返回条数,一般 3 到 5 条足够,太多会稀释上下文。这个方案不依赖向量库,单机跑起来很快,适合个人工具或内部系统。如果条文量级到几万条以上,再考虑上 FTS5 或轻量向量索引。
还有一个习惯:每次更新法规库后,我会随机抽 10 条,手动比对原文和入库内容,确认切分和清洗没有引入错误。这个动作花不了几分钟,但能挡住大部分静默数据损坏。从那以后我每次处理这类文本资源,都强制走一遍「抽样比对」再交付。希望帮到你。
本文还有配套的精品资源,点击获取