企业年报PDF爬虫开发求助:关键词数据提取方案
高效提取企业年报PDF中营收、利润数据的可行策略
Hey Robin, 针对你爬取企业年报PDF并提取REVENUE、PROFIT这类关键数值到DataFrame的需求,我整理了几个经过实践验证的高效方案,解决你之前用tabula-py、PyPDF2遇到的效率问题:
1. 正则表达式(Regex)+ 文本提取(适合纯文本场景)
既然你已经能把PDF转成TXT,那正则是最直接的工具,重点是写能匹配年报中数值格式的正则规则——毕竟年报里的数值通常带千位分隔符、货币符号,位置也可能灵活。
示例代码:
import re import pandas as pd # 读取转换好的TXT文件 with open("annual_report.txt", "r", encoding="utf-8") as f: text = f.read() # 匹配关键词(支持大小写)+ 数值(适配千位分隔符、货币符号) pattern = r"(?i)(revenue|profit)\s*[:\-]?\s*([\d.,]+)\s*([€$£]|EUR|USD|GBP)?" matches = re.findall(pattern, text) # 整理成DataFrame results = pd.DataFrame(matches, columns=["Metric", "Value", "Currency"]) # 清洗数值(统一千位分隔符和小数点) results["Cleaned_Value"] = results["Value"].str.replace(".", "", regex=False).str.replace(",", ".", regex=False).astype(float)
优化点:
- 正则里的
(?i)忽略大小写,能匹配REVENUE、Revenue、revenue等变体 - 适配不同的数值格式:比如
1.000.000.000€、1,000,000$、1000000 EUR - 可以扩展关键词列表,比如加上
Net Profit、Gross Revenue等
2. 优化Tabula-py的表格提取逻辑(适合表格密集的年报)
年报里的核心数据大多在表格里,之前用tabula没效果可能是参数没调对。Tabula有两种模式:lattice(适合带边框的表格)和stream(适合无边框的表格),可以结合使用。
示例代码:
import tabula import pandas as pd # 读取PDF所有页面的表格,自动识别模式 dfs = tabula.read_pdf( "annual_report.pdf", pages="all", guess=True, # 自动判断表格模式 multiple_tables=True, encoding="utf-8" ) # 合并所有表格,筛选包含目标关键词的行 combined_df = pd.concat(dfs, ignore_index=True) # 模糊匹配关键词(支持大小写) target_rows = combined_df[combined_df.apply(lambda row: row.astype(str).str.contains(r"(?i)revenue|profit").any(), axis=1)] # 提取数值列(可根据实际表格结构调整) final_results = target_rows.melt(id_vars=[col for col in target_rows.columns if "description" in col.lower()], value_vars=[col for col in target_rows.columns if "amount" in col.lower() or col.isnumeric()], var_name="Metric_Type", value_name="Value")
优化点:
- 用
pages="all"批量提取所有页面的表格 - 如果知道目标表格在特定页面,直接指定
pages="5-10"提升效率 - 对于复杂表格,可以用
area参数指定表格的坐标(Tabula的GUI工具可以帮你获取坐标)
3. 用PyMuPDF(fitz)替代PyPDF2(精准文本定位)
PyMuPDF比PyPDF2的文本提取精度更高,还能获取文本的位置信息——可以通过关键词的位置,提取其附近的数值,避免纯正则的误匹配。
示例代码:
import fitz import re import pandas as pd doc = fitz.open("annual_report.pdf") matches = [] for page in doc: # 搜索关键词(支持大小写) text_instances = page.search_for("revenue", hit_max=10) + page.search_for("profit", hit_max=10) for inst in text_instances: # 提取关键词周围的文本(比如右侧和下方的区域) surrounding_text = page.get_textbox(fitz.Rect(inst.x0, inst.y0, inst.x1+200, inst.y1+50)) # 用正则提取数值 num_match = re.search(r"([\d.,]+)\s*([€$£]|EUR|USD|GBP)?", surrounding_text) if num_match: matches.append({ "Page": page.number+1, "Metric": inst.get_text().strip(), "Value": num_match.group(1), "Currency": num_match.group(2) if num_match.group(2) else None }) doc.close() results_df = pd.DataFrame(matches)
优势:
- 能精准定位关键词在页面的位置,提取更相关的数值
- 支持提取特定区域的文本,减少无关内容的干扰
4. 结合LLM辅助提取(处理复杂格式的终极方案)
如果年报格式非常多样化,正则和表格提取都搞不定,可以试试用大语言模型(比如GPT-3.5/4)来结构化提取。把提取的文本或表格片段传给模型,让它直接返回JSON格式的结果,再转成DataFrame。
示例代码(伪代码):
import openai import pandas as pd import json # 假设已经提取了页面文本 page_text = "..." # 从PDF中提取的文本片段 prompt = f""" 请从以下企业年报文本中提取所有REVENUE、PROFIT相关的数值,返回JSON格式,包含字段:Metric, Value, Currency。 文本内容:{page_text} """ response = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[{"role": "user", "content": prompt}] ) # 解析JSON结果到DataFrame results = json.loads(response.choices[0].message.content) results_df = pd.DataFrame(results)
注意事项:
- 要注意数据隐私,不要把敏感的年报内容传给公开API
- 可以批量处理文本片段,降低API调用成本
总结建议
实际处理年报时,建议组合使用多种方法:
- 先用PyMuPDF或Tabula提取所有文本和表格
- 用正则初步筛选关键词和数值
- 对复杂格式的内容,用LLM辅助补全
这样既能保证效率,又能覆盖大部分年报的格式差异。
内容的提问来源于stack exchange,提问作者rbnspckrs




