外观
AI辅助正则生成实战:杂乱爬虫文本抽取避坑实录
问题背景:爬虫文本不是标准 JSON,正则仍有价值
很多爬虫拿到的并不是干净接口,而是详情页片段、脚本残片、PDF 转文本、客服消息或混杂 HTML 的字符串。字段可能散落在自然语言里,例如“券后价 ¥ 1,299.00 元,库存仅剩 35 件,订单号:SO-20240518-9981”。这类文本结构不稳定,但又存在明显模式:金额符号、编号前缀、日期分隔符、单位后缀。
完全依赖大模型直接抽取字段,优点是快,缺点是不可复现:同一个提示词可能今天返回 JSON,明天把“1,299.00”识别成整数,也可能在批量任务中产生不可预期的解释。传统解析器面对半结构化网页更可靠,但对杂乱文本的适配成本高。正则表达式位于两者之间:模式显式、执行确定、成本低,适合抽取局部强规则字段。
AI 辅助正则生成的关键,不是把一句“帮我提取价格”扔给模型,而是建立“样本归纳 → 表达式生成 → 可运行校验 → 边界回归 → 性能检查”的闭环。
方案对比:直接抽取、手写正则与 AI 生成正则
第一种方案是让大模型直接从文本中输出结构化 JSON。它适合字段语义复杂、上下文依赖强的任务,例如判断商品是否参加促销。但如果要处理百万级爬虫文本,直接调用模型的成本、延迟和稳定性都可能成为问题。
第二种是工程师手写正则。它确定性最好,性能可控,但面对多种页面模板时需要反复观察样本。例如价格可能写作“¥199”“人民币 199.00 元”“市场价:1,299 元”,编号可能包含字母、短横线和不定长数字。
第三种是 AI 生成正则,再由程序自动校验。它适合“局部字段有规律、页面噪声多、样例持续增加”的爬虫文本抽取场景。大模型负责归纳候选模式,测试代码负责判断能不能上线。
提示词设计:让大模型输出可测试的正则方案
不要只给模型单条文本,建议同时提供正例、反例、抽取字段、输出格式和运行环境。下面这段提示词可以作为模板:
text
你是正则表达式工程师。请为 Python 3 的 re 模块生成正则。
任务:从杂乱爬虫文本中抽取订单金额,返回数值字符串。
正例:
1. “券后价 ¥ 1,299.00 元” -> 1299.00
2. “支付价:人民币 89 元” -> 89
3. “Price: $19.99” -> 19.99
反例:
1. “客服 QQ:1299.00” 不应匹配
2. “2024-05-18 12:30:00” 不应匹配
要求:
1. 只输出 JSON,字段为 pattern、groups、why、risks;
2. pattern 必须能被 Python re.compile 编译;
3. 说明每个片段含义,并列出至少 3 个风险;
4. 不要使用灾难性回溯的嵌套量词。这种约束有两个好处:一是模型必须区分“出现数字”和“金额上下文”,二是返回的 risks 可以直接转成测试用例。实际项目中,建议把页面样本去敏后再发给模型,并保留模型版本、提示词版本和生成时间,便于追踪表达式来源。
实操步骤:价格、编号与日期抽取
下面示例可以直接运行。目标是从杂乱文本中抽取金额、订单号和日期,并验证误匹配情况。
python
import re
from datetime import datetime
# 金额:要求前面出现货币符号或“价/金额”等上下文,数字主体支持千分位和小数
amount_pattern = re.compile(
r"(?:¥|¥|\$|人民币|券后价|支付价|金额|Price)\s*"
r"(?P<amount>\d{1,3}(?:,\d{3})+(?:\.\d{1,2})?|\d+(?:\.\d{1,2})?)"
r"\s*(?:元|块|RMB|CNY|USD)?",
re.IGNORECASE,
)
# 订单号:2到8位字母前缀 + 日期或短横线数字,避免把普通日期误识别为编号
order_pattern = re.compile(
r"(?P<order_no>[A-Z]{2,8}-\d{8}-\d{3,8}|[A-Z]{2,8}\d{8,16})",
re.IGNORECASE,
)
# 日期:限定四位年份、两位月日,捕获后再用程序校验真实日期
date_pattern = re.compile(
r"(?P<date>\d{4}[-/.]\d{1,2}[-/.]\d{1,2})"
)
def normalize_amount(raw: str) -> float:
"""去掉千分位逗号并转成浮点数。"""
return float(raw.replace(",", ""))
def parse_valid_date(raw: str):
"""正则只负责形态,datetime 负责判断 2024-99-99 这类非法日期。"""
text = raw.replace("/", "-").replace(".", "-")
try:
return datetime.strptime(text, "%Y-%m-%d").date().isoformat()
except ValueError:
return None
def extract(text: str) -> dict:
amount_match = amount_pattern.search(text)
order_match = order_pattern.search(text)
valid_date = None
for match in date_pattern.finditer(text):
valid_date = parse_valid_date(match.group("date"))
if valid_date:
break
return {
"amount": normalize_amount(amount_match.group("amount")) if amount_match else None,
"order_no": order_match.group("order_no").upper() if order_match else None,
"date": valid_date,
}
samples = [
"券后价 ¥ 1,299.00 元,订单号:SO-20240518-9981,下单时间 2024-05-18。",
"客服 QQ:1299.00;支付价:人民币 89 元,SO202405180099。",
"Price: $19.99, date 2024/02/29, order AB-20240301-12."
]
for item in samples:
print(extract(item))正则逐段解析
金额表达式中的 (?:¥|¥|\$|人民币|券后价|支付价|金额|Price) 是“非捕获组”,只要求金额前面出现货币符号或价格语义;\s* 允许符号和数字之间存在空格;命名组 (?P<amount>...) 负责捕获真正数值。数值部分分成两支:\d{1,3}(?:,\d{3})+(?:\.\d{1,2})? 匹配 1,299.00 这类千分位数字;\d+(?:\.\d{1,2})? 匹配普通整数或两位小数。最后的 (?:元|块|RMB|CNY|USD)? 是可选单位,避免没有单位的美元价格漏匹配。
订单号表达式要求字母前缀和数字段同时存在。[A-Z]{2,8}-\d{8}-\d{3,8} 匹配 SO-20240518-9981,[A-Z]{2,8}\d{8,16} 匹配 SO202405180099。它故意不匹配单独的 20240518,因为日期、手机号片段也可能是 8 到 11 位数字。
日期表达式只做形态约束:\d{4} 匹配年份,[-/.] 允许三种分隔符,\d{1,2} 兼容单位数月份。是否为真实日期交给 datetime.strptime,这样可以拒绝 2024-02-29、2024-99-01 等形态正确但语义非法的结果。
自动校验:不要相信模型,只相信回归结果
AI 生成的候选正则必须经过正反样本测试。可以维护一个 YAML 或 JSON 测试集,每次模型生成新表达式后自动运行:
python
cases = [
{
"text": "券后价 ¥ 1,299.00 元",
"want": {"amount": 1299.0, "order_no": None, "date": None},
},
{
"text": "客服 QQ:1299.00",
"want": {"amount": None, "order_no": None, "date": None},
},
{
"text": "SO-20240518-9981 下单于 2024.05.18",
"want": {"amount": None, "order_no": "SO-20240518-9981", "date": "2024-05-18"},
},
]
for case in cases:
got = extract(case["text"])
assert got == case["want"], f"抽取失败:{case['text']},得到 {got}"
print("全部回归用例通过")上线前还要做三类检查。第一是编译检查,捕获未转义括号、不支持的语法和命名组冲突。第二是结果检查,比较字段值而不是只看是否匹配,因为 search() 命中错误位置也算通过。第三是耗时检查,用真实长文本和接近失败的文本压测,避免正则在特定输入上卡住。
避坑总结:AI 正则生成的高频问题
错误现象:价格把 QQ 号、时间戳、库存数字也提取出来。 修正方法是增加左侧语义锚点,如货币符号、“券后价”“支付价”“金额”,并把“客服”“QQ”“库存”等词加入反例。不要使用裸的
\d+(?:\.\d+)?在整页文本里搜索金额。错误现象:模型给出
(.*)+、(\s*.*)*这类表达式,短文本正常,长文本超时。 修正方法是禁止嵌套量词,优先使用非捕获组、字符类和明确次数。对 HTML 或脚本片段,应先用解析器缩小范围,再在字段附近运行正则,而不是让一个表达式吞掉整页。错误现象:日期能匹配
2024-99-99,编号把手机号或订单时间误判为单号。 修正方法是坚持“正则校验形态,程序校验语义”。日期交给日期库,订单号要求业务前缀、分隔符和固定段位,并用真实反例做回归。错误现象:同一表达式在 JavaScript 中可用,在 Python 中报错,或 Unicode 数字、全角符号处理不一致。 修正方法是提示词中明确运行时;Python 使用
\d时要注意 Unicode 匹配,必要时改成[0-9];全角空格、全角冒号、全角¥都应放入样本。错误现象:为了提升召回率不断加分支,最后表达式无人能维护。 修正方法是把字段拆成多个小表达式,例如金额符号、数值、单位分别处理;复杂清洗放到普通代码中。正则不是编程语言,越想让它承担上下文推理,越容易脆弱。
最终建议是:让大模型做“候选模式生成器”和“边界用例助手”,让自动化测试做裁判。AI 可以显著缩短观察样本和编写初版正则的时间,但只有通过真实爬虫文本、反例集、性能压测和版本化回归,正则自动生成才能真正进入生产环境。
免责声明
本工具与文章内容仅用于学习测试场景,优化建议仅供参考,请勿直接用于生产环境,由此产生的风险自行承担。