Python技术问询:无参考文献标题的双栏PDF参考文献移除方案
无明确参考文献标题的PDF文本提取优化方案
问题描述
我编写了如下Python代码,用于提取在线双栏PDF中的手稿文本。由于目标PDF没有明确的“References”或“Bibliography”章节标题,多次尝试检测并移除提取文本中的参考文献均未成功。请问针对这种无参考文献标题的情况,有什么移除参考文献的可行方案?
现有代码
import fitz # PyMuPDF import requests import io def extract_text_from_pdf(pdf_file): # 从流中打开PDF文件 doc = fitz.open(stream=pdf_file, filetype="pdf") full_text = [] for page_num in range(len(doc)): page = doc[page_num] blocks = page.get_text("dict")["blocks"] # 分析页面宽度以检测分栏结构 page_width = page.rect.width mid_x = page_width / 2 # 页面中线,用于拆分左右栏 left_column = [] right_column = [] for block in blocks: if "bbox" in block: x0, y0, x1, y1 = block["bbox"] # 提取文本块的边界框 # 将文本块归类到左栏或右栏 if x1 <= mid_x: left_column.append(block) elif x0 >= mid_x: right_column.append(block) # 按垂直位置(顶部)对每栏的文本块排序 left_column.sort(key=lambda b: b["bbox"][1]) right_column.sort(key=lambda b: b["bbox"][1]) # 提取每栏的文本并拼接 page_text = [] for column in [left_column, right_column]: for block in column: if "lines" in block: block_text = "" for line in block["lines"]: for span in line["spans"]: block_text += span["text"] + " " page_text.append(block_text.strip()) # 合并左右栏文本作为当前页面内容 full_text.append("\n".join(page_text)) return "\n\n".join(full_text) # 从URL获取PDF url = 'https://hal.science/hal-04206682/document' try: response = requests.get(url) response.raise_for_status() # 请求失败时抛出异常 pdf_file = io.BytesIO(response.content) # 将PDF内容加载到内存 # 提取PDF文本 text = extract_text_from_pdf(pdf_file) print(text) except requests.exceptions.RequestException as e: print(f"下载PDF时出错: {e}")
补充说明
上述代码可正确识别双栏PDF结构并提取文本,但无法区分图注(Figure)文本与手稿主文本,导致图注文本会混入主文本中对应位置,也不清楚如何移除该部分内容。
解决方案
一、移除无标题参考文献的可行方案
1. 基于格式特征匹配识别
参考文献条目通常有明显格式规律,可通过正则匹配连续符合规律的段落来定位:
- 常见特征:以数字编号(如
[1]、1.)开头、包含作者名+年份+文献来源组合、外文作者姓氏大写开头 - 实现示例:
import re def remove_references(text): # 匹配典型参考文献条目格式 ref_pattern = re.compile(r'^\s*(\[\d+\]|\d+\.)\s+[A-Z][a-z]+(?:\s+[A-Z]\.)?,?.*', re.MULTILINE) lines = text.split('\n') ref_start = -1 consecutive_matches = 0 # 阈值:连续匹配5条则判定为参考文献起始 threshold = 5 for idx, line in enumerate(lines): if ref_pattern.match(line): consecutive_matches += 1 if consecutive_matches >= threshold and ref_start == -1: ref_start = idx else: consecutive_matches = 0 if ref_start != -1: return '\n'.join(lines[:ref_start]) return text
2. 基于页面位置过滤
参考文献几乎都集中在文档末尾,可直接跳过最后若干页或页面底部区域:
- 实现思路:
- 获取PDF总页数,处理时跳过最后1-3页(可根据文档类型调整)
- 或分析文本块的垂直位置,页面底部70%以下区域的文本块优先判定为参考文献(需结合格式验证)
3. 基于字体与排版特征识别
参考文献字体通常比正文小,行间距更紧凑:
- 实现思路:利用PyMuPDF的
span["size"]获取字体大小,统计页面内字体大小分布,当连续出现多块小字体文本且符合参考文献格式时,排除该区域
二、移除图注的方案
图注通常包含Figure X/Fig. X/Table X关键词,且位置紧邻图片:
- 实现思路(集成到原提取函数中):
def extract_text_from_pdf(pdf_file): doc = fitz.open(stream=pdf_file, filetype="pdf") full_text = [] for page_num in range(len(doc)): page = doc[page_num] blocks = page.get_text("dict")["blocks"] page_width = page.rect.width mid_x = page_width / 2 left_column = [] right_column = [] # 先标记图片附近的图注块 caption_blocks = set() for block in blocks: if block["type"] == 1: # 图片块 img_y0, img_y1 = block["bbox"][1], block["bbox"][3] # 查找图片上下20px范围内的文本块 for text_block_idx, text_block in enumerate(blocks): if text_block["type"] == 0: tb_y0, tb_y1 = text_block["bbox"][1], text_block["bbox"][3] if abs(tb_y0 - img_y1) < 20 or abs(tb_y1 - img_y0) < 20: # 检查是否包含图注关键词 block_text = "" if "lines" in text_block: for line in text_block["lines"]: for span in line["spans"]: block_text += span["text"] if any(key in block_text for key in ["Figure", "Fig.", "Table"]): caption_blocks.add(text_block_idx) # 处理文本块时跳过图注块 for block_idx, block in enumerate(blocks): if block_idx in caption_blocks: continue if "bbox" in block: x0, y0, x1, y1 = block["bbox"] if x1 <= mid_x: left_column.append(block) elif x0 >= mid_x: right_column.append(block) # 后续排序、提取逻辑保持不变 left_column.sort(key=lambda b: b["bbox"][1]) right_column.sort(key=lambda b: b["bbox"][1]) page_text = [] for column in [left_column, right_column]: for block in column: if "lines" in block: block_text = "" for line in block["lines"]: for span in line["spans"]: block_text += span["text"] + " " page_text.append(block_text.strip()) full_text.append("\n".join(page_text)) return "\n\n".join(full_text)
内容的提问来源于stack exchange,提问作者BostonPlummer
相关产品推荐
相关产品推荐

