第四章:数据采集与清洗——爬虫基础、法律网站数据抓取、去重与格式清洗
做法律大模型,数据是命根子。但说实话,光有数据还不够,你得知道怎么把数据「请」回来,还得把它「洗」干净。这一章,我就带你走一遍从爬虫到清洗的全流程。都是我在项目里踩过的坑,你听听看。
4.1 爬虫基础:别把法律网站搞崩了
爬虫这东西,说白了就是个自动化下载器。你手动复制粘贴,它帮你自动干。但法律网站不是你家后院,爬得太猛,人家服务器会报警的。
我常用的工具是 Python 的 requests 和 BeautifulSoup。简单说,requests 负责发请求拿网页,BeautifulSoup 负责解析 HTML 提取内容。嗯,这里要注意:很多法律网站有反爬机制,你得加个 User-Agent 伪装成浏览器。
import requests
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
url = 'https://example.com/case/123'
resp = requests.get(url, headers=headers)
soup = BeautifulSoup(resp.text, 'html.parser')
title = soup.find('h1', class_='case-title').text.strip()
print(title)
你看,核心代码就这么几行。但实际项目中,你可能会遇到动态加载的页面。这时候 requests 就拿不到了,得用 Selenium 模拟浏览器。我曾经在一个地方法院的网站上折腾了两天,最后发现人家用的是 Vue 异步渲染……嗯,从那以后我学乖了,先看页面源码再动手。
4.2 法律网站数据抓取:实战案例
咱们拿「中国裁判文书网」举个例子。这个网站数据量大,但结构乱。我建议你先抓一个页面看看结构,再写循环批量抓取。
为什么会这样?因为法律网站的 HTML 结构经常变。你写死一个选择器,过两周可能就失效了。我的做法是:
- 先手动分析:打开浏览器开发者工具,找到目标数据的 CSS 选择器或 XPath。
- 写一个测试脚本:只抓一页,验证数据对不对。
- 加延时和重试:每抓一页等 1-3 秒,遇到 503 错误自动重试 3 次。
time.sleep(random.uniform(1, 3)) 随机延时,这样更像人类行为,不容易被封 IP。
下面是一个批量抓取裁判文书标题和案号的示例:
import time
import random
def fetch_case_list(page_num):
url = f'https://example.com/list?page={page_num}'
resp = requests.get(url, headers=headers)
soup = BeautifulSoup(resp.text, 'html.parser')
cases = []
for item in soup.select('div.case-item'):
title = item.find('a').text.strip()
case_id = item.find('span', class_='case-id').text.strip()
cases.append({'title': title, 'case_id': case_id})
return cases
for page in range(1, 101):
data = fetch_case_list(page)
# 保存到本地
with open(f'cases_page_{page}.json', 'w') as f:
json.dump(data, f, ensure_ascii=False)
time.sleep(random.uniform(1, 3))
你想想看,如果不用随机延时,100 页可能 5 分钟就抓完了,但你的 IP 大概率会被封。我刚开始做的时候不懂,一口气抓了 5000 页,结果第二天 IP 就被拉黑了……
4.3 去重:别让模型学重复的东西
法律数据里重复率特别高。同一个案子,可能在不同网站、不同时间被收录多次。如果不去重,模型会学到「同一个意思有不同说法」,反而降低准确率。
我的去重策略分三步:
- 精确去重:用 MD5 或 SHA256 对全文做哈希,哈希值一样的就是重复。
- 模糊去重:用 SimHash 或 MinHash,计算文本相似度,超过 90% 就视为重复。
- 业务去重:法律数据有唯一标识,比如案号、文书编号。同一个案号只保留一份。
下面是一个简单的 MD5 去重示例:
import hashlib
seen_hashes = set()
deduped_data = []
for item in raw_data:
text = item['content']
hash_val = hashlib.md5(text.encode()).hexdigest()
if hash_val not in seen_hashes:
seen_hashes.add(hash_val)
deduped_data.append(item)
嗯,这里要注意:MD5 去重对「只改了一个标点符号」的情况无效。所以我在项目中通常先用 MD5 去重,再用 SimHash 做第二轮过滤。
4.4 格式清洗:把数据变成模型能吃的样子
法律数据格式乱得让人头疼。有的用全角标点,有的用半角;有的段落之间空三行,有的连空格都没有。模型可不管这些,它只认干净的数据。
我总结了一套清洗流程,你直接拿去用:
| 步骤 | 操作 | 示例 |
|---|---|---|
| 1 | 统一编码 | 全部转成 UTF-8,避免乱码 |
| 2 | 去除空白字符 | 删除多余空格、换行、制表符 |
| 3 | 统一标点 | 全角逗号转半角,中文句号保留 |
| 4 | 去除噪声 | 删除页眉页脚、广告、版权声明 |
| 5 | 标准化日期 | 「2023年1月1日」统一为「2023-01-01」 |
下面是一个清洗函数的示例:
import re
def clean_text(text):
# 去除多余空白
text = re.sub(r'\s+', ' ', text)
# 统一标点(中文保留全角,英文用半角)
text = text.replace(',', ',').replace('。', '.')
# 去除页眉页脚(假设以「本院认为」开头)
if text.startswith('本院认为'):
text = text[4:]
# 标准化日期
text = re.sub(r'(\d{4})年(\d{1,2})月(\d{1,2})日', r'\1-\2-\3', text)
return text.strip()
我曾经遇到过一个案子,判决书里混了法官的批注和排版标记。清洗的时候没注意,结果模型把「【注:此处删除】」也当成了正文……从那以后,我每次清洗完都会随机抽 100 条人工检查一遍。
4.5 知识体系总览
下面这张图是我自己画的,把数据采集和清洗的整个流程串起来了。你一看就明白。
你看,整个流程分三个阶段:采集、清洗、输出。每个阶段都有对应的技术要点。我个人习惯在清洗阶段花最多时间,因为数据质量直接决定模型效果。你想想看,喂给模型一堆脏数据,它学出来的东西能靠谱吗?
好了,这一章的内容就这些。爬虫、去重、清洗,每一步都有坑,但只要你按流程走,基本不会出大问题。记住:法律数据是敏感数据,合规永远是第一位的。