如何从PDF中提取结构化交通数据并整理为DataFrame
PDF交通数据结构化提取适配方案
现有代码问题说明
- PyPDF2的文本提取逻辑本身优先级较低,对于多栏、带不规则间距的排版缺失内容是已知问题,不建议继续使用
- Fitz(PyMuPDF)的
getTextBlocks()接口默认返回的是不带排序的文本块,你没有利用返回结果自带的坐标信息做排序、规则匹配,才会出现结构混乱的问题
Fitz适配修改方案
你用到的getTextBlocks()返回的每个元素结构为(x0, y0, x1, y1, 文本内容, 块序号, 块类型),可以通过坐标排序、关键词匹配实现固定字段的提取,由于BTS各州2020年报告排版完全统一,写一次规则即可适配所有州的PDF,示例代码如下:
import fitz import pandas as pd # 提前定义需要匹配的字段关键词,键为PDF内的英文关键词,值为输出的中文字段名 TARGET_FIELDS = { "Miles of Public Road": "公共道路里程", "Major Airports": "主要机场数量", "Bridges": "桥梁数量", "Major Water Ports": "主要水运港口数量", "Miles of Freight Railroad": "货运铁路里程", "Number of MPOs": "MPO数量", "Miles of Waterway": "航道里程" } def extract_state_traffic_data(pdf_path, state_name): res = {"State": state_name} with fitz.open(pdf_path) as doc: # 只处理第一页的统计数据,需要提取全页内容可调整遍历范围 first_page = doc[0] blocks = first_page.getTextBlocks() # 按y坐标(垂直位置)升序、x坐标(水平位置)升序排序,恢复正常阅读顺序 blocks.sort(key=lambda x: (x[1], x[0])) # 遍历所有文本块匹配字段和对应数值 for i in range(len(blocks)): block_text = blocks[i][4].strip() for field_en, field_cn in TARGET_FIELDS.items(): if field_en in block_text: # 数值一般在当前字段的下1-2个文本块,匹配纯数字内容即可 for j in range(i+1, min(i+3, len(blocks))): num_text = blocks[j][4].strip().replace(",", "") if num_text.isdigit(): res[field_cn] = int(num_text) break return res # 单文件提取示例 alabama_data = extract_state_traffic_data("US PDF/Alabama.pdf", "阿拉巴马州") # 批量遍历所有州PDF提取数据后,统一转DataFrame df = pd.DataFrame([alabama_data]) print(df)
如果需要提取全文档其他章节的结构化数据,可以按照同样的逻辑,先定位对应章节的坐标范围,再写对应匹配规则即可。
替代工具推荐
如果不想手动处理坐标逻辑,可以选择以下工具简化开发:
pdfplumber:比fitz更适合文本规则匹配,自带文本块邻接查询、行级提取能力,对于固定排版的PDF提取效率更高camelot-py:如果目标数据是明确的表格格式,可以直接用该工具提取表格转DataFrame,不需要手动写匹配规则
内容的提问来源于stack exchange,提问作者Nhyi
相关产品推荐
相关产品推荐

