说真的,从PDF里面搜索关键字并提取上下文真的是个噩梦,PDF是个面向打印的文档格式,从里面抓数据太痛苦了,搞了一天已经想吐了。不过我还是沉淀了一点点东西。
python解析PDF文档大致有以下几个库:
- PDFMiner
- pdfplumer
- ta
我们主要是提取文本内容,所以推荐使用pdfminer。
安装
默认使用python3.7的环境, 如果是2.7的请酌情处理。
pip install pdfminer
pip install pdfminer3k
pip install pdfminer.six
可能会用到的的相关类:
- PDFParser: 从一个文件中获取数据。
- PDFDocument: 保存获取的数据,和PDFParser是相互关联的。
- PDFPageInterpreter: 处理页面内容。
- PDFDevice: 将其翻译成你需要的格式。
- PDFResourceManager: 用于存储共享资源,如字体或图像。
准备
这里有一个很重要的关于PDF文档结构的知识,有助于你用代码筛选出你想要的东西:
LTPage :表示整个页。可能会含有LTTextBox,LTFigure,LTImage,LTRect,LTCurve和LTLine子对象。
LTTextBox:表示一组文本块可能包含在一个矩形区域。注意此box是由几何分析中创建,并且不一定表示该文本的一个逻辑边界。它包含LTTextLine对象的列表。使用 get_text()方法返回文本内容。
LTTextLine :包含表示单个文本行LTChar对象的列表。字符对齐要么水平或垂直,取决于文本的写入模式。使用get_text()方法返回文本内容。
LTAnno:在文本中字母实际上被表示为Unicode字符串。需要注意的是,虽然一个LTChar对象具有实际边界,LTAnno对象没有,因为这些是“虚拟”的字符,根据两个字符间的关系(例如,一个空格)由布局分析后插入。
LTImage:表示一个图像对象。嵌入式图像可以是JPEG或其它格式,但是目前PDFMiner没有放置太多精力在图形对象。
LTLine:代表一条直线。可用于分离文本或附图。
LTRect:表示矩形。可用于框架的另一图片或数字。
LTCurve:表示一个通用的Bezier曲线
————————————————
版权声明:本文为CSDN博主「周小董」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xc_zhou/java/article/details/81009809
开始发车
真的想吐,本来想好好写一下的,现在完全没心情,直接上代码吧。这个是我封装的一个单独的文件:
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTTextBox, LTTextBoxHorizontal
from pdfminer.pdfpage import PDFTextExtractionNotAllowed
from pdfminer.pdfpage import PDFPage
import glob
def get_file_list(root=".", file_suffix="*") -> list:
"""
获取指定目录和指定文件类型的文件列表。
:param root: 默认为当前目录
:param file_suffix: 文件名后缀,默认为*
:return: 返回指定目录和指定文件类型的list
"""
l = glob.glob("{}/*.".format(root) + file_suffix)
return l
# 将一个pdf转换成dict
def pdf_to_txt(file_path: str):
"""
可以搜索指定PDF文件中含关键词的段落内容
:param file_path: PDF文档路径
:param key_words: 搜索的关键词
:return: Dict格式的搜索结果
"""
# try:
rs = {}
my_pdf = open(file_path, 'rb')
print("File:", file_path)
# 创建一个文档解析器
par = PDFParser(my_pdf)
# 创建一个PDF文档对象存储文档结构
my_doc = PDFDocument(par)
# 判断文件是否允许文本提取
if not my_doc.is_extractable:
raise PDFTextExtractionNotAllowed
else:
# 创建一个PDF资源管理器对象用来存储资源
res = PDFResourceManager()
# 设定参数进行分析
la = LAParams()
# 创建一个PDF设备对象
dev = PDFPageAggregator(res, laparams=la)
# 创建一个PDF解释器对象
my_interpreter = PDFPageInterpreter(res, dev)
# return PDFPage.create_pages(my_doc)
# 按页开始处理
for pg in PDFPage.create_pages(my_doc):
my_interpreter.process_page(pg)
lt_dict = {}
rs[pg.pageid] = lt_dict
print("Page ID:", pg.pageid)
# 接收改页面的LTPage对象
lt = dev.get_result()
for l in lt:
if isinstance(l, LTTextBox):
# splitlines方法用来处理 \n 换行符
lt_dict[l.index] = l.get_text().splitlines()
return rs
# 从上面转换出来的字典中搜索关键字并返回它所在的LTTextBox内容以及下一个LTTextBox的内容,别问我为什么,我想吐~~
def search_keywords(the_dict: dict, keywords: str):
"""
在指定的PDF转成的dict中搜索关键词,并返回其上下文
:param the_dict: 必须是用本文件中的pdf_to_txt方法返回的字典才可以
:param keywords: 要搜索的关键字
:return: dict形式返回搜索结果
"""
s = {}
for key, value in the_dict.items():
# print("koala", key, value)
if keywords in value:
s[key - 1] = the_dict[key - 1]
s[key] = value
s[key + 1] = the_dict[key + 1]
print("Bingo!", key, value)
if len(s) > 0:
rs = {keywords: s}
return rs
注释写的非常详细了,虽然折腾了快一天,试来试去,但最后剩下的代码还是比较精简的,踩了不少坑。
以下是一个demo,演示如何调用上面封装好的方法:
fl = pdfTools.get_file_list(file_suffix="pdf")
key_words = "我是关键字"
s = {fl[0]: pdfTools.pdf_to_txt(fl[0])}
aa =[]
for key, value in s[fl[0]].items():
aa.append(pdfTools.search_keywords(value, key_words))
我不喜欢输入到控制台或者文件,因为用pyCharm,可以直接看到Jupyter Server上的变量值。
从PDF里面找东西真的真的太痛苦了,要是有高手有更好的方法求指教,谢了谢了!
有心情的时候也许我会再完善一下这篇文章吧,今天就这样了,我去吐会儿~~~~~
再次声明一下,本文参考了此篇文章部分内容:[312]python提取pdf文本内容,不要来找我麻烦,我没工夫应付。