Python正则表达式实战:中文房源信息结构化解析
发布时间:2026/9/3 21:53:32 锦皓数字建站

今天不讲大模型也不讲 ComfyUI。先看一条典型的社区房源信息碧桂园山湖城观澜1街1座29033室1厅1厨1卫2阳台3床1.8m1.5m1.2m可日租/周租/月租欢迎各位邻居咨询。这条文本看起来就是一个租房信息但放到数据开发视角它就是一个很标准的非结构化短文本。我们要做的是把小区、楼栋、房号、户型、床型、租期、备注从这一句话里拆出来输出成 JSON 或 Excel。整件事不需要 GPU、不需要高显存、不需要 API Key普通 CPU 就能跑而且支持批量任务。下面直接给一套可复制的实现方案感兴趣的同学可以跟着跑一遍。文章会按这个顺序展开先梳理核心能力和适用范围再讲环境准备和脚本编写然后用真实文本做功能测试和效果验证接着扩展成批量任务和 API 接口最后给出性能观察、常见问题排查和工程化建议。整个过程不用特定平台不依赖商业接口适合想自己动手做文本清洗、数据入库、信息归类开发的同学。1. 核心能力速览这里要说明一下下面的方案不是某个现成开源项目而是一套基于 Python 正则和规则引擎的通用文本结构化解析方案。这样写的好处是零依赖、可控、容易改也方便后续接入大模型兜底。能力项说明项目类型中文短文本结构化解析输入示例碧桂园山湖城观澜1街1座29033室1厅1厨1卫2阳台3床1.8m1.5m1.2m可日租/周租/月租主要功能提取地址、楼栋、房号、户型、床型尺寸、租期、备注运行环境Python 3.9Windows / macOS / Linux 均可硬件要求CPU 即可无显存要求依赖库标准库re、json批量场景可选pandas启动方式命令行脚本运行是否支持批量任务支持文本文件逐条解析也可接 CSV 导出是否支持 API可通过 FastAPI 封装成 HTTP 接口适合场景房源信息整理、爬虫数据清洗、物业台账、中介系统录入不适合场景房源真伪核验、实勘图片判断、合同法律审查这个表格里最值得关注的是“无显存要求”和“支持批量任务”。很多文本清洗需求根本不需要上大模型规则解析就能解决大部分问题成本低、速度快、结果可预期。如果遇到复杂文本也可以在规则解析的基础上加一个 LLM 兜底通道。2. 适用场景与使用边界先说适合谁。如果你是房东手里有多套房源想把户型、床型、租金周期整理成一个表格如果你是爬虫开发者抓了一堆社区帖子想从中抽取房源字段如果你是物业或中介需要把口播、群消息、广告文本转成结构化记录这套方案都适合。这个方案能解决的核心问题是把一段“人话”变成机器可读的数据。尤其是社区群里常见的半结构化文本格式不统一、数字和单位混杂靠人工复制进 Excel 耗时且容易出错。脚本化以后粘贴一行文本马上得到 JSON 或多行批量结果。但也要说清楚边界。它不能判断这条房源是否真实存在不能代替实勘不能核验产权更不能用来做违法或骚扰性用途。门牌号、手机号、社交账号这类信息属于个人敏感信息公开传播前需要确认是否有合法来源和授权。如果是从公开渠道采集也要注意平台条款不能批量采集后又原样公开。合规红线必须提前想清楚。另一个边界是准确率。规则解析对格式规范的文本很有效但对口语化、乱序、省略字段、多种单位混写的文本需要不断补充规则。如果文本里出现“三个房间”“主卧1米8”“长租短租都行”正则方案就要做额外适配。更稳妥的做法是规则先抽取抽不出来的字段标记为null再由人工或大模型补充。3. 环境准备与前置条件这套解析方案完全走 Python 标准库理论上不需要安装额外依赖。但为了批量处理和导出方便我建议装一个pandas。另外如果后面要封装 API还需要fastapi和uvicorn。先检查本机 Python 版本python --version如果不是 3.9 及以上建议先升级 Python 环境。然后创建项目目录mkdir house-info-parser cd house-info-parser在项目目录下创建一个虚拟环境python -m venv venvWindows 激活虚拟环境venv\Scripts\activatemacOS / Linux 激活虚拟环境source venv/bin/activate如果只需要基础解析可以不安装任何第三方库。如果要跑批量 CSV 导出再执行pip install pandas如果后面要封装 API执行pip install fastapi uvicorn安装完成后在项目目录下新建一个 Python 文件parse_house.py。这就是核心解析脚本。需要注意这里的路径和包名都是演示用的实际项目里可以根据自己的目录结构调整。4. 解析脚本编写与启动核心思路是先按标点切分文本再分别用正则提取地址块、户型、床型和租期。我们以开头那条文本为样例碧桂园山湖城观澜1街1座29033室1厅1厨1卫2阳台3床1.8m1.5m1.2m可日租/周租/月租欢迎各位邻居咨询。先切分出地址块也就是第一个逗号前的内容。然后从地址块里提取楼栋和房号从整段文本里提取户型和床型。完整代码如下import re import json RAW 碧桂园山湖城观澜1街1座29033室1厅1厨1卫2阳台3床1.8m1.5m1.2m可日租/周租/月租欢迎各位邻居咨询。 def parse_house(text: str) - dict: result {} # 地址块取第一个逗号前的内容通常是小区、楼栋、房号的组合 addr_part re.split(r[,;], text)[0].strip() result[address] addr_part # 楼栋和房号以 1街1座2903 这种格式为例 building_match re.search(r(\d)街(\d)座(\d), addr_part) if building_match: result[building] building_match.group(1) 街 building_match.group(2) 座 result[room_no] building_match.group(3) else: result[building] None result[room_no] None # 户型N室N厅N厨N卫N阳台 layout_match re.search(r(\d)室(\d)厅(\d)厨(\d)卫(\d)阳台, text) if layout_match: result[layout] { bedroom: int(layout_match.group(1)), living_room: int(layout_match.group(2)), kitchen: int(layout_match.group(3)), bathroom: int(layout_match.group(4)), balcony: int(layout_match.group(5)), } else: result[layout] None # 床型尺寸1.8m1.5m1.2m bed_part re.search(r(\d(?:\.\d)?m(?:\\d(?:\.\d)?m)), text, re.IGNORECASE) if bed_part: sizes re.findall(r\d(?:\.\d)?m, bed_part.group(1), re.IGNORECASE) result[beds] [s.lower() for s in sizes] else: result[beds] [] # 租期日租 / 周租 / 月租 / 年租 lease_match re.search(r(日租|周租|月租|年租)(?:/(日租|周租|月租|年租))*, text) if lease_match: result[lease_modes] re.findall(r日租|周租|月租|年租, lease_match.group(0)) else: result[lease_modes] [] result[note] text return result if __name__ __main__: parsed parse_house(RAW) print(json.dumps(parsed, ensure_asciiFalse, indent2))运行方式很简单python parse_house.py预期输出是一段带缩进的 JSON字段包括address、building、room_no、layout、beds、lease_modes和note。如果看到layout是带几个数字的字典beds是[1.8m, 1.5m, 1.2m]说明脚本已经跑通。这个脚本的核心不复杂就是正则。它把原始文本拆成几个容易定位的部分再各自用正则匹配。对于固定格式的文本准确率很高对于格式混乱的文本主要靠不断补充正则来优化。第一次写解析脚本建议先跑通单个样例再逐渐增加测试用例。5. 功能测试与效果验证单条文本跑通还不够要用多组测试用例验证。这样才能知道解析规则在哪些格式下可靠、在哪些格式下会翻车。下面给出几个典型测试场景。第一个是完整格式也就是我们一直用的样例碧桂园山湖城观澜1街1座29033室1厅1厨1卫2阳台3床1.8m1.5m1.2m可日租/周租/月租欢迎各位邻居咨询。预期结果地址为碧桂园山湖城观澜1街1座2903楼栋为1街1座房号为2903户型为3室1厅1厨1卫2阳台床型为三条尺寸租期为三种。第二个用例是缺少床型碧桂园山湖城观澜1街1座29022室1厅1厨1卫1阳台月租随时看房。预期结果beds为空列表lease_modes为[月租]。这时脚本不会报错只是在没有床型信息时返回空列表。第三个用例是只有地址和户型山湖城观澜2街3座10014室2厅2卫无家具。预期结果beds为空lease_modes为空layout正常提取。这时解析成功但表示信息不完整。第四个用例是床型尺寸用“米”而不是“m”山湖城观澜5街2座2013室2厅床1.8米1.5米可月租。这时原始正则匹配不到1.8米因为模式只支持1.8m。解决方法是把正则改成兼容“米”和“m”两种写法bed_part re.search(r(\d(?:\.\d)?[米m](?:\\d(?:\.\d)?[米m])), text, re.IGNORECASE)第五个用例是地址部分包含多个逗号例如碧桂园山湖城观澜1街1座29033室1厅可月租王女士电话13800000000。当前脚本会截取第一个逗号前的内容作为地址所以地址是对的。但note会保留整段原文如果要做手机号脱敏可以再补一条手机号正则phone_match re.search(r1[3-9]\d{9}, text)然后决定是否删除或打码。验证是否成功的标准很简单字段提取出来后和人工读出来的结果是否一致。建议每写一次规则就用不少于 10 条历史文本做回归测试避免改了新规则又弄坏旧格式。6. 批量任务与 API 接口扩展单条解析只是第一步。实际场景里你面对的往往是一整个文本文件或者一个爬虫导出的 CSV。这时需要批量解析能力。假设有一个input.txt每行一条房源信息碧桂园山湖城观澜1街1座29033室1厅1厨1卫2阳台3床1.8m1.5m1.2m可日租/周租/月租欢迎各位邻居咨询。 山湖城观澜2街3座10014室2厅2卫无家具。 观澜中心3栋15022室1厅1卫床1.5m1.2m周租月租均可。写一个批量解析脚本batch_parse.pyimport json from parse_house import parse_house input_file input.txt output_file output.jsonl with open(input_file, r, encodingutf-8) as f: lines [line.strip() for line in f if line.strip()] results [] for line in lines: results.append(parse_house(line)) with open(output_file, w, encodingutf-8) as f: for item in results: f.write(json.dumps(item, ensure_asciiFalse) \n) print(解析完成共, len(results), 条)运行python batch_parse.py生成output.jsonl每行一条 JSON方便后续导入 Elasticsearch、数据库或 Excel。如果要用 CSV 导出可以用 pandasimport pandas as pd df pd.DataFrame(results) df.to_csv(output.csv, indexFalse, encodingutf-8-sig) print(df.head())这里用utf-8-sig是为了在 Excel 里直接打开中文不乱码。如果需要 HTTP 接口可以封装一个 FastAPI 服务from fastapi import FastAPI from pydantic import BaseModel from parse_house import parse_house app FastAPI() class ParseRequest(BaseModel): text: str class ParseResponse(BaseModel): data: dict app.post(/parse, response_modelParseResponse) def parse_endpoint(req: ParseRequest): return ParseResponse(dataparse_house(req.text))启动 APIuvicorn api_server:app --host 127.0.0.1 --port 8000然后用 curl 测试curl -X POST http://127.0.0.1:8000/parse \ -H Content-Type: application/json \ -d {text: 碧桂园山湖城观澜1街1座29033室1厅1厨1卫2阳台3床1.8m1.5m1.2m可日租/周租/月租欢迎各位邻居咨询。}接口返回 JSON 后就可以接到自己的内部系统里。需要注意这个 API 没有做鉴权生产环境要加访问限制不能直接暴露在公网。7. 资源占用与性能观察由于这是纯 CPU 文本解析没有模型推理所以资源占用非常低。启动 Python 进程后内存占用通常在几十 MB 到一两百 MB 之间CPU 在做正则匹配时会有短暂波动但不会持续占用高负载。显存占用为 0因为整个流程不涉及 CUDA。性能方面单条文本的解析时间一般在微秒到毫秒级别。如果数据量达到十万条主要瓶颈通常不是正则本身而是文件读取、JSON 序列化和磁盘写入。建议批量场景里用io逐行读取不要一次性把整个文件readlines到内存尤其是当文本量很大时。这里给一个提升性能的思路。可以先把要用的正则编译好避免循环中反复编译import re BED_RE re.compile(r(\d(?:\.\d)?m(?:\\d(?:\.\d)?m)), re.IGNORECASE) LAYOUT_RE re.compile(r(\d)室(\d)厅(\d)厨(\d)卫(\d)阳台)在批量脚本里还可以用multiprocessing做进程池加速from multiprocessing import Pool def process_parallel(lines): with Pool(processes4) as pool: return pool.map(parse_house, lines)但要注意进程启动本身有开销如果数据量只有几百条单线程反而更快。先小批量测试再决定是否上并行。另外解析结果如果要做入库建议使用批量插入不要一条一条 insert否则数据库连接会成为累赘。端口方面如果使用 API 服务且端口被占用换成其他端口即可uvicorn api_server:app --host 127.0.0.1 --port 80018. 常见问题与排查方法实际运行中常见问题集中在文本格式、正则匹配、环境依赖三块。下面用表格列出排查思路。问题现象可能原因排查方式解决方案启动脚本报模块不存在Python 版本太低或第三方库未安装检查python --version和pip list升级 Python 或安装对应依赖地址字段为空文本第一个逗号前没有内容或地址被其他符号分隔打印原始文本确认分隔符是中文逗号还是英文逗号扩展分隔符正则如[,;。]户型提取不到文本里没有“室厅厨卫阳台”格式确认字段拼写是否存在“三室一厅”这种中文数字增加中文数字映射或使用兜底规则床型提取不到尺寸单位用的是“米”而不是“m”检查原始文本看尺寸写法正则兼容“米”和“m”租期提取为空文本写着“可短租”或“长租”没有“日租周租月租”检查源文本增加“短租”“长租”等别名映射批量解析输出行数不对文件有空行或换行符不一致打印读入行数看是否有空行被过滤用line.strip()过滤后再判断API 请求返回 422请求体没有按要求传text字段查看 FastAPI 文档页面调整 curl 或 Python requests 的请求体端口被占用已经有进程占用了 8000 端口查看系统端口占用换端口或停掉旧进程输出结果不稳定同一格式文本在不同时间表现不同检查文本中是否包含特殊字符统一做去除多余空格、全角半角转换的预处理遇到问题时最有效的办法不是直接改规则而是先把原始文本原样打印出来看。很多时候是隐藏空格、中文标点、数字全半角不一致导致匹配失败。加一个预处理函数去掉首尾空格把全角逗号、分号都统一处理能省很多麻烦。9. 最佳实践与使用建议第一批建议面向工程化落地。第一先小参数测试不要一上来就解析十万条。第二条文本能跑通再跑十条十条没问题再跑全量。第二保留一份最小可运行配置也就是parse_house.py和样例文本后续改规则时能快速回归。第三把模型文件、输入素材、输出结果分目录管理这里就是input.txt、output.jsonl、output.csv分开避免文件互相覆盖。第四批量任务一定要加日志和失败重试。文本解析虽然简单但遇到脏数据时可能会让整个脚本中途退出。建议捕获异常for line in lines: try: results.append(parse_house(line)) except Exception as e: print(解析失败, line, e) results.append({error: str(e), raw: line})这样即使某一条文本格式异常也不会中断整个任务。第五接口服务要限制访问范围。如果只是本机使用绑定127.0.0.1就够如果需要在局域网内使用再绑定0.0.0.0但一定要加访问控制或接口鉴权。不要直接把网关节点的 8000 端口暴露到公网。第六涉及人脸、声音、版权素材时必须确认授权。这条虽然主要用于文本解析但在处理社区群信息、个人发布的房源时同样要关注隐私授权。如果有人名、电话、门牌号建议脱敏后使用。不要在技术博客或测试代码里原样公开他人手机号等真实敏感信息。从数据质量角度看建议在解析结果里保留raw原文和parse_status字段。这样在后续人工复核时可以迅速定位哪些字段是机器识别出来的哪些字段是缺失的。对缺失字段可以设计一个补录界面或者接入大模型二次抽取。10. 总结与下一步这次拿“碧桂园山湖城观澜1街1座2903”这条典型文本完整走了一遍中文房源信息结构化解析流程。最值得尝试的点就是不依赖 GPU、不依赖大模型、不依赖商业 API用 Python 自带的正则就能把绝大多数规整文本拆成结构化数据。对开发同学来说这套思路可以直接迁移到电商商品信息解析、合同要素抽取、地址标准化等场景。如果自己上手试第一步应该先跑通单个样例验证输出 JSON 是否符合预期第二步再增加床型、户型、租期等不同字段的组合测试第三步写批量脚本处理真实文本最后才是考虑封装 API 或接大模型兜底。最容易踩的坑有两个。一个是中文标点、全角半角、空格导致的匹配失败这类问题先统一预处理文本。另一个是字段缺失时不加判断直接取分组导致脚本报错。建议在正则匹配后都加一个if match判断匹配不到就返回None或空列表这样解析脚本才更健壮。后续可以继续扩展的方向包括把规则解析结果和 LLM 解析结果做交叉校验加入 Pydantic 做字段 Schema 校验把批量任务改成消息队列消费模式或者直接做成一个内部工具页面。先把最核心的正则解析跑通后续扩展都是水到渠成的事情。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。