Python文本信息抽取实战:金额识别、实体抽取与API批量处理

Python文本信息抽取实战:金额识别、实体抽取与API批量处理 产房外的“三块二”到底是哪个项目的核心数据如果把它当成一行业务数据你的第一反应是用 Excel 拉个透视表还是写个正则把它从文本里抠出来实际上这类“算不清的账”往往不是数学问题而是文本解析问题。人的语言里藏着金额、日期、人物和情绪机器要做的第一步不是替你做决定而是先把这些信息从非结构化文本里结构化出来。这次我们来看一套用 Python 实现的文本信息抽取方案金额识别、命名实体识别、情感判断、批量处理、API 封装全部跑通目标是让“产房外算不清的那笔账”变成一张可以筛选、可以统计、可以自动化对账的结构化数据表。这套方案的定位不是某个现成大厂 API而是本地可跑的轻量工具链。它的核心特点很明确纯 CPU 也能跑不需要显卡依赖包数量少Python 3.8 以上的环境基本都能装支持批量读取文本文件并导出 CSV可以通过 FastAPI 暴露成 HTTP 接口方便接进其他系统同时保留了自定义词典和正则规则的扩展入口适合处理中文口语表达。对于关注本地部署、批量任务和接口调用的读者来说这篇文章可以直接收藏。下面我会从能力速览开始逐步演示环境准备、代码实现、接口封装、性能观察和常见问题排查你可以直接照着复制运行。1. 核心能力速览能力项说明方案类型Python 文本信息抽取工具集非第三方商业 API输入数据中文/英文文本支持 .txt 批量文件目录核心功能金额提取、人名/地名/时间等实体识别、情感倾向判断、关键词统计硬件要求CPU 即可建议 2 核以上内存 4GB 以上GPU 需求不需要 GPU不使用 CUDA依赖环境Python 3.8jieba、pandas、fastapi、uvicorn 等启动方式命令行执行脚本或启动 FastAPI 服务接口能力支持 POST 接口输入文本返回 JSON批量任务支持遍历目录批量解析结果合并导出 CSV适用场景合同金额核对、客服工单分析、舆情评论分析、口语文本结构化实施难度中等有 Python 基础即可上手从能力项能看出来这不是“识别一张发票”这种垂直工具而是一套更通用的“文本账本”处理框架。你给它任意一段话它返回的是金额字段、实体字段、情感分数的结构化结果。这套思路在面对“产房外的三块二”这类口语化表达时尤其有用因为多数现成 API 对口语文本的金额识别并不可靠本地自定义规则反而更容易调节。2. 适用场景与使用边界这套方案的典型使用场景有三类。第一类是财务文本预处理从合同原文、聊天记录、报销说明中提取金额和日期用于对账前的数据清洗。第二类是用户反馈归类读取客服工单或评论内容识别用户提到金额、商品、地名并给出情感倾向便于自动化标记。第三类是研究工作流中的数据标注批量抽取事件要素生成 CSV 后交给人工复核。不适合的场景也要说清楚。这套工具不做语义推理不能判断“这笔账到底谁对谁错”只能提取表层信息它也不适合处理扫描件 PDF因为那不是文本抽取而是 OCR 的任务需要另外引入 OCR 模型如果面对的是海量长文档比如几百页的招股书建议先用段落切分再逐段分析否则内存和耗时都会明显增长。使用边界方面重点提醒一句文本数据里往往包含个人隐私无论是聊天记录还是客户工单处理前必须确认数据来源合法、用途明确。批量处理涉及个人信息时建议脱敏后再做保存。用于商业场景前对输出的实体和情感结果要做人工复核避免因为识别误差引发争议。所有本地处理的文本数据不要在未授权的情况下上传到第三方服务。3. 环境准备与前置条件先确认操作系统和 Python 版本。Windows 10/11、Ubuntu 20.04、macOS 12 都可以Python 建议 3.8 到 3.11 之间。下面这套脚本不依赖 GPU所以不需要安装 CUDA 或 PyTorch环境准备会比本地大模型简单很多。需要安装的 Python 包如下pip install jieba pandas fastapi uvicorn python-multipart如果只是跑文本解析脚本fastapi、uvicorn 和 python-multipart 可以暂时不装等需要启动接口服务时再安装。建议在项目目录下单独创建虚拟环境避免和系统 Python 包冲突python -m venv venv source venv/bin/activate # Windows 下执行 venv\Scripts\activate pip install jieba pandas fastapi uvicorn python-multipart准备一个输入目录后续用于批量任务测试。目录结构可以这样规划text_parser/ ├── venv/ ├── input/ # 待分析文本 ├── output/ # 导出结果 ├── parser.py # 核心解析脚本 ├── batch.py # 批量处理脚本 └── api_server.py # 接口服务文本输入统一使用 UTF-8 编码。如果你从 Windows 记事本拿到的是 GBK 编码的文件先转成 UTF-8 再放入 input 目录否则后续中文读取会报错。这一步看起来很简单却是很多人批量处理时卡住的第一道门槛。4. 文本解析功能测试与效果验证先写核心解析脚本 parser.py。这个脚本会完成四件事金额提取、时间日期提取、中文分词与关键词统计、情感倾向判断。为了演示效果我把功能和代码都写成可直接运行的版本你可以替换成自己的文本。import re import jieba import jieba.analyse from collections import Counter # 金额正则覆盖“三块二”“12.5元”“1,200元”“三万二”等常见表达 MONEY_RE re.compile( r(?:[\d,]\.?\d*|[零一二两三四五六七八九十百千万亿]) r\s*(?:元|块|毛|分|万|亿) r(?:[零一二两三四五六七八九十百千万亿])? ) # 日期正则覆盖“2023年5月12日”“5月12号”“2023-05-12” DATE_RE re.compile( r\d{4}[-/年]\d{1,2}[-/月]\d{1,2}[日号]? r|\d{1,2}月\d{1,2}[日号]? r|\d{4}[-/]\d{1,2}[-/]\d{1,2} ) SENTIMENT_WORDS { positive: [开心, 满意, 感谢, 好, 靠谱, 顺利, 值得], negative: [生气, 失望, 差, 骗子, 投诉, 亏了, 太难, 难受] } def extract_money(text): matched MONEY_RE.findall(text) return [m.strip() for m in matched] def extract_date(text): return DATE_RE.findall(text) def extract_keywords(text, top_k10): jieba.analyse.set_stop_words(stopwords.txt) return jieba.analyse.extract_tags(text, topKtop_k) def sentiment_score(text): score 0 for word in SENTIMENT_WORDS[positive]: if word in text: score 1 for word in SENTIMENT_WORDS[negative]: if word in text: score - 1 return score def parse_text(text): return { money: extract_money(text), date: extract_date(text), keywords: extract_keywords(text), sentiment: sentiment_score(text) }建议在同一目录下放一个 stopwords.txt把“的、了、在、是、我、你、他”这类无意义词放进去。如果没有这个文件关键词提取时会混入大量虚词影响观察结果。测试一段模拟输入if __name__ __main__: sample 确认怀孕那天产房外的三块二算不清我的生死帐。2023年5月12日老公说我花了太多钱5月20号又说住院押金要一万二。 result parse_text(sample) for key, value in result.items(): print(key, value)运行后预期输出类似money [三块二, 2023年5月12日, 一万二] date [2023年5月12日, 5月20号] keywords [产房, 确认怀孕, 生死帐, 住院押金, 算不清] sentiment -1这里有一个细节正则里的日期 pattern 会把“2023年5月12日”中的部分内容也匹配到 money 列表因为“2023年5月”前面匹配了[\d,]。实际生产环境需要进一步过滤日期和金额的交叉误报最简单的方式是如果一段文本同时命中日期正则且包含“年、月、日”结构就优先归为日期。这也是测试这套脚本时最值得调的点。4.1 金额提取测试测试目的确认口语化金额和带千分位的金额都能被正确识别。输入素材住院费三块二押金一万二疫苗费1,200元挂号费15.5元还有杂七杂八的八块。运行 extract_money 后观察是否包含三块二、一万二、1,200元、15.5元、八块。如果只提取到数字没提取到单位检查正则中\s*(?:元|块|毛|分|万|亿)部分是否被注释掉。常见失败原因有两个一是“一万二”这种表达中正则匹配链可能把“一万”和“二”分开本文给出的正则已经加了(?:[零一二两三四五六七八九十百千万亿])?来兜底捕获单位后的附加数字但依然无法覆盖所有方言表达二是全角逗号和半角逗号混用正则里已经包含但仍有其他分隔符需要自行补充。4.2 实体识别与关键词测试测试目的验证从一段口语文本中提取人物关系、地点和行为相关的关键词。输入素材老公傅骁在人民医院产科门口按计算器护士喊他签字他一直在算产检费。运行 extract_keywords理想结果应包含“人民医院”“产检费”“计算器”“产科”等词。如果“人民医院”和“产科”这类词没有被正确识别说明 jieba 的默认词典缺少医疗领域词汇需要把自定义词加入词典jieba.add_word(人民医院) jieba.add_word(产科) jieba.add_word(产检费)实体识别这里没有引入完整 NER 模型目的是保持轻量化。如果你的业务需要更准确的“人名-地名-机构名”分类可以接入 HanLP 或 LTP 这类本地组件但那样内存和模型文件都会明显增加。当前版本更偏向“快速看重点”而不是精确分类。4.3 情感判断测试测试目的验证情感倾向能对“正面、负面、中性”文本做出区分。输入素材确认怀孕那天他一直在算钱说住院要花多少产检要花多少最后连三块二也算进去。运行 sentiment_score预期结果应为负数因为文本中包含“算钱”“三块二”这样带有不满情绪的表达而这些词可能不会出现在预置的负面词列表里。如果分数偏中性说明规则词典需要补充领域负面词比如“算钱”“斤斤计较”“嫌弃”。情感判断这块我用了最简单的词表计数法因为它不需要下载额外模型便于演示。真实项目里可以用 snownlp、BERT 微调模型或大模型 API 提高准确率但成本和部署复杂度会同步上升。这套脚本适合先跑通流程再按业务反馈迭代词表。4.4 全角符号与编码测试测试目的确认中文全角符号和数字能被正确处理。输入素材押金元全角数字手续费元全角数字全角点。在默认情况下Python 正则\d只匹配半角数字全角数字不会被识别。如果你想兼容全角数字需要在匹配前做转换def full2half(text): result [] for char in text: code ord(char) if code 0x3000: code 32 elif 0xFF01 code 0xFF5E: code - 0xFEE0 result.append(chr(code)) return .join(result)转换后再调用正则提取就能同时处理全角和半角。这个点在做银行流水、法院文书和手录单据处理时经常遇到值得提前考虑。5. 批量任务目录扫描、解析与 CSV 导出批量处理是文本抽取方案从“demo”走向“可用”的关键一步。下面用一个 batch.py 脚本实现遍历 input 目录下所有 .txt 文件逐个解析最终合并输出到一个 CSV。import os import csv from parser import parse_text INPUT_DIR input OUTPUT_FILE output/result.csv def process_file(filepath): with open(filepath, r, encodingutf-8) as f: text f.read() result parse_text(text) result[file] os.path.basename(filepath) result[text] text[:100] return result def main(): records [] for filename in os.listdir(INPUT_DIR): if not filename.endswith(.txt): continue filepath os.path.join(INPUT_DIR, filename) try: record process_file(filepath) records.append(record) print(f[OK] {filename}) except Exception as e: print(f[FAIL] {filename}: {e}) if not records: print(No txt files found in input dir.) return os.makedirs(output, exist_okTrue) fieldnames [file, money, date, keywords, sentiment, text] with open(OUTPUT_FILE, w, encodingutf-8-sig, newline) as f: writer csv.DictWriter(f, fieldnamesfieldnames) writer.writeheader() writer.writerows(records) print(fDone. Total {len(records)} records, saved to {OUTPUT_FILE}) if __name__ __main__: main()运行方式python batch.py这里用 utf-8-sig 编码是为了兼容 Excel 直接打开 CSV避免中文乱码。如果你的下游系统是 Python pandas改成 utf-8 也可以。批量任务最容易翻车的地方是单个文件读取失败。建议在正式跑全量数据前先用 5 到 10 个文件做小批量测试确认输出格式符合预期再放开跑全量目录。如果一批文件里有几十万条记录建议加入进度打印和失败文件日志不要用 print 打印每条记录可以先写入 error.log。import logging logging.basicConfig(filenamebatch_error.log, levellogging.ERROR) logger logging.getLogger(__name__) # 在 except 异常的位置改为 logger.error(f{filename}: {e}, exc_infoTrue)批量任务跑完后重点检查三类数据money 列中是否有日期误入库keywords 列是否有大量重复无意义词sentiment 列是否大量为 0。如果出现这些问题优先回调节点而不是直接进入分析。6. 接口 API 与外部系统集成批量脚本适合离线处理但如果你的业务系统要在线调用就需要把解析能力封装成 HTTP 接口。这里用 FastAPI 实现一个最简单的 POST 接口输入 JSON 文本返回结构化结果。from fastapi import FastAPI from pydantic import BaseModel from parser import parse_text app FastAPI() class TextRequest(BaseModel): text: str class ParseResponse(BaseModel): money: list date: list keywords: list sentiment: int app.post(/api/parse, response_modelParseResponse) def parse(request: TextRequest): return parse_text(request.text) if __name__ __main__: import uvicorn uvicorn.run(app, host127.0.0.1, port8000)启动服务python api_server.py启动后可以用 curl 验证curl -X POST http://127.0.0.1:8000/api/parse \ -H Content-Type: application/json \ -d {text: 产房外的三块二算不清我的生死帐}返回 JSON 示例{ money: [三块二], date: [], keywords: [产房, 生死帐, 算不清], sentiment: -1 }也可以用 Python requests 调用import requests url http://127.0.0.1:8000/api/parse payload {text: 确认怀孕那天傅骁把计算器按得噼啪响说产检要花三万。} response requests.post(url, jsonpayload, timeout10) print(response.status_code) print(response.json())接口上线后要做的三件事限制访问来源FastAPI 默认绑定的 127.0.0.1 只允许本机访问如果跨机器调用要把 host 改成 0.0.0.0同时配合防火墙或反向代理限制 IP在请求体里增加 text 长度校验防止超大文本撑爆内存对高频调用做限流最简单的实现是用中间件统计 IP 的调用次数也可以直接借助 FastAPI 的 SlowAPI 库这里不做展开先保证接口稳定。对于已经存在的业务系统接口服务可以继续扩展为批量提交模式。比如客户端一次传入多个文本 id 和内容服务端解析完以后回调结果地址。这个设计在“工单舆情批量分析”中很常见但需要额外维护一个任务队列。当前接口最适合的是单条在线查询或小批量调用几十条以上建议走离线 batch 流程。7. 资源占用与性能观察很多读者会关心这套方案跑起来占多少资源。由于不涉及深度学习模型资源占用非常可控。从运行逻辑看jieba 分词器和正则匹配都是 CPU 操作主要消耗在文本长度和文件数量上。观察资源占用可以分三个阶段。启动阶段首次运行 jieba 时会构建前缀词典内存会出现一个小峰值通常在 200MB 到 500MB 之间具体数值取决于 jieba 词典版本和系统环境不是固定值。解析阶段单条短文本处理时间在毫秒级几千字的文档也不会产生明显等待除非你在关键词提取时使用jieba.analyse.textrank这类较重算法耗时会上涨。批量阶段大量文件连续处理时内存会随着结果列表不断累积所以 batch.py 里建议不要把所有记录都放在内存中一次性写入 CSV而是每处理完一批就追加写入一次。def append_to_csv(record, filepath): fieldnames [file, money, date, keywords, sentiment, text] if not os.path.exists(filepath): with open(filepath, w, encodingutf-8-sig, newline) as f: writer csv.DictWriter(f, fieldnamesfieldnames) writer.writeheader() with open(filepath, a, encodingutf-8-sig, newline) as f: writer csv.DictWriter(f, fieldnamesfieldnames) writer.writerow(record)性能优化建议如果文本量很大优先减少关键词提取的 topK 值默认设置为 10实际分析中取 5 到 8 就够了如果文本结构固定比如都是“金额日期描述”的工单格式可以用更严格的正则替代 jieba 分词如果同时处理上万文件建议写成多进程版本Python 的 multiprocessing Pool 可以直接套在文件遍历外层核数按照 CPU 逻辑核心数的一半配置避免磁盘 IO 和内存相互竞争。不要盲目追求高并发。这个工具链本身是轻量级的把接口服务部署在多核服务器上时uvicorn 默认只使用单进程。如果需要更高吞吐用uvicorn api_server:app --workers 2启动多个 worker但要注意每个 worker 都会加载一份 jieba 词典内存会相应翻倍。生产环境建议先压测再定 worker 数不要一上来就开 8 个 worker。8. 常见问题与排查方法问题现象可能原因排查方式解决方案中文文字乱码输入文件不是 UTF-8 编码用文本编辑器查看文件编码统一转成 UTF-8或者在 open 函数中指定 encoding金额提取不到正则覆盖不全或全角数字未转换打印原始文本检查是否包含全角字符先做全角转半角再执行正则关键词全是“的、了、是”缺少停用词表检查 stopwords.txt 是否存在添加停用词表并在 extract_tags 中设置 set_stop_words批量处理某文件报错文件编码问题或读取权限查看 batch_error.log跳过异常文件或修复编码后重跑fastapi 接口 404接口路由写错或服务未重启访问 /docs 查看接口列表核对 app.post 路径和请求方式端口被占用8000 端口已被其他服务使用检查端口监听状态换端口启动例如 --port 8001内存持续增长批量脚本把记录全放内存观察任务管理器内存曲线改为逐条追加写入 CSV情感分数全为 0领域词表未覆盖业务词汇打印识别后的分词结果扩展正面/负面词典或引入预训练模型日期误识别为金额正则里日期模式先命中打印匹配片段检查 pattern 顺序在金额正则前先排除日期格式接口返回 500输入为空或超长查看服务端错误日志增加 text 非空校验和长度限制其中日期和金额互相干扰的问题是调试过程中最容易被忽视的。就像开头那句“产房外的三块二”如果文本里混着“2023年5月12日”正则可能把“2023”当作金额的数字部分提取出来导致 money 字段里出现一个假金额。更稳妥的处理顺序是先提取并剔除日期再提取金额。你可以用下面的占位符方式实现def mask_date(text): return DATE_RE.sub( __DATE__ , text)把日期先替换成固定占位符再做金额提取就可以避免日期数字被当成金额。这个技巧在处理医疗账单、发票备注等场景时非常有效。9. 最佳实践与使用建议第一次跑这套工具时先不要追求“一次识别所有字段”从小范围开始。拿 20 条真实文本过一遍把识别错误的案例收集起来逐个调整正则和词表再扩展成批量流程。文本抽取项目本质上是一个持续迭代的工程不是写完一个正则就能一劳永逸。目录管理上建议把输入素材、输出结果、词表和脚本分层存放。input 目录只放原始数据output 目录按日期生成子目录比如 output/20230601/result.csv这样回溯结果时不会乱。stopwords.txt 和自定义词典用 Git 管理改了什么词、哪个版本效果更好都能保留变更记录。部署和调用方面给几个工程化建议接口服务内网部署时绑定 127.0.0.1 并通过 Nginx 反向代理不要在公网裸奔。每次调整正则后跑一遍回归用例把 20 条典型样本固化成 test_case.py防止改一处坏三处。批量处理大文件时增加进度条或每 100 条记录打印一行状态方便观察是否卡住。输出 CSV 里增加一列“parse_time”记录每条耗时后续做性能优化时有据可查。如果要把实体识别扩展到人名、地名、机构名优先选带有模型文件的本地库并控制内存占用。合规方面最后再强调一次不要用这套工具去分析未经授权的聊天记录、个人隐私或商业机密涉及人脸、声音、肖像、医疗、金融等信息时必须确认有合法授权并做好数据脱敏。文本数据看起来不像图片那样敏感但它的泄露风险一点也不小。10. 总结与下一步这套文本解析方案最值得尝试的点是让你在不需要 GPU、不需要大模型、不需要注册任何服务的情况下快速把一段口语化文本变成结构化数据。最先应该验证的功能是金额提取和关键词提取拿你自己的文本跑一遍看正则和 jieba 是否覆盖了常见表达然后调整词典。最容易踩的坑有两个编码问题导致中文乱码日期被误识别成金额。这两个坑在第一次跑数据时大概率会遇到提前有预期就不会慌。后续可以继续扩展的方向包括接入真实 NER 模型来细分人名、地名、机构名把规则引擎和模型结果做成双通道互相校验增加任务队列支持超大文件异步解析在接口层加入 API Key 鉴权和调用统计。如果只是处理当前这几十条文本这套轻量方案已经完全够用不需要一上来就上大模型。建议把代码和词表保存好后续遇到新文本格式时只需调整正则和词典就能快速复用。