Python专题, 语言

使用pdfminer查找包含某个关键字的文献

本篇为读取PDF文件的一个练习例子,功能不常使用。

有过这样的经历:(1)看过某篇文献中的某段内容,只记得关键字,却不记得文章的名字;(2)想要找出提及某个关键字的所有文献(仅限于本地文献),而电脑的搜索却只能搜索出文件名。为了解决这个长期存在的需求,这里整理出“查找包含某个关键字的文献”的代码,方便之后使用。在某些文献管理软件内貌似也有这样pdf文件内部检索的功能。

代码说明:

  • 有的PDF文件可能不容易识别,因此搜索的结果不一定包含所有信息。
  • 该代码不支持模糊搜索,只支持关键词的绝对匹配。如果要实现模糊搜索,可自行修改代码中的判断条件。
  • 整体的查找速度会有点慢。一个文件的查找时间大约需要10秒,因为包含了PDF转文本的时间。如果有1000篇文献,大约需要2.8小时;如果有5000篇文献,大约需要14小时;如果有1万篇文献,大约需要28小时。
  • 推荐一次性设置多个关键词,同时查找,结果会保存在txt文件中。

首先要安装pdfminer3k包:pip install pdfminer3k

1. 查找某个文件夹下包含某个关键字的文献

使用时修改:

  • 关键字key_word_array
  • 初始文件夹路径original_path

代码为:

"""
This code is supported by the website: https://www.guanjihuan.com
The newest version of this code is on the web page: https://www.guanjihuan.com/archives/9129
"""

import os
import re 
import time
import logging 
logging.Logger.propagate = False 
logging.getLogger().setLevel(logging.ERROR)  # 只显示error级别的通知


def main():
    # 参数
    key_word_array = ['photonic', 'Berry phase']
    original_path = 'D:\\文献'
    
    # 查找所有的PDF文件路径
    pdf_file_all = find_files_pdf(original_path)
    print('\n该文件夹下总共有', len(pdf_file_all), '个PDF文件。\n')
    
    f = open('error.txt','w',encoding='utf-8')
    f.close()
    for key_word in key_word_array:
        f = open(str(key_word)+'.txt','w',encoding='utf-8')
        f.write('该文件夹下总共有'+str(len(pdf_file_all))+'个PDF文件。\n')
        f.close()

    # 查找包含关键词的PDF文件
    i0 = 1
    begin = time.time()
    for pdf_file in pdf_file_all:
        print('查找第', i0, '个文件,', end='')
        begin0 = time.time()
        try: 
            content = get_text_from_pdf(pdf_file)
            for key_word in key_word_array:
                if re.search(re.compile(key_word),content):
                    print('发现文件!关键词', key_word, '对应的文件位置在:\n\n', pdf_file, '\n')
                    with open(str(key_word)+'.txt','a',encoding='utf-8') as f:
                        f.write('\n查找第'+str(i0)+'个文件时发现文件!位置在:\n'+pdf_file+'\n')
        except: 
            print('出现异常!位置在:\n\n', pdf_file, '\n')
            with open('error.txt','a',encoding='utf-8') as f:
                f.write('\n解析第'+str(i0)+'个文件时出现异常!位置在:\n'+pdf_file+'\n')
        end0 = time.time()
        print('用时', end0-begin0, '秒')
        i0 += 1
    print('\n全部搜索结束!')
    end = time.time()
    print('\n总共用时:', (end-begin)/60, '分')


def find_files_pdf(path):  # 查找所有PDF文件
    file_all = find_files(path)
    pdf_file_all = []
    for file0 in file_all:
        if re.search(re.compile('^fdp.'),file0[::-1]): # 如果文件是以.pdf结尾
            pdf_file_all.append(file0)
    return pdf_file_all


def find_files(path):  # 查找所有文件
    file_all = []
    path_next_loop = [path]
    for i in range(10000):  # i为文件在文件夹中的深度
        file_all_in_one_loop, path_next_loop = find_files_loop_module(path_next_loop)
        for file_in_one_loop in file_all_in_one_loop:
            file_all.append(file_in_one_loop)
        if path_next_loop == []:
            break
    return file_all


def find_files_loop_module(path_all): # 查找文件的一个循环模块
    file_all_in_one_loop = []
    path_next_loop = []
    for path in path_all:
        filenames = os.listdir(path)
        for filename in filenames:
            filename = os.path.join(path,filename) 
            if os.path.isfile(filename): # 如果是文件
                file_all_in_one_loop.append(filename) 
            else:  # 如果是文件夹
                path_next_loop.append(filename)
    return file_all_in_one_loop, path_next_loop


def get_text_from_pdf(file_path):  # 从PDF中获取文本
    from pdfminer.pdfparser import PDFParser, PDFDocument
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
    from pdfminer.converter import PDFPageAggregator
    from pdfminer.layout import LAParams, LTTextBox
    from pdfminer.pdfinterp import PDFTextExtractionNotAllowed

    # 用文件对象来创建一个pdf文档分析器
    praser = PDFParser(open(file_path, 'rb'))
    # 创建一个PDF文档
    doc = PDFDocument()
    # 连接分析器 与文档对象
    praser.set_document(doc)
    doc.set_parser(praser)

    # 提供初始化密码
    # 如果没有密码 就创建一个空的字符串
    doc.initialize()

    # 检测文档是否提供txt转换,不提供就忽略
    if not doc.is_extractable:
        raise PDFTextExtractionNotAllowed
    else:
        # 创建PDf 资源管理器 来管理共享资源
        rsrcmgr = PDFResourceManager()
        # 创建一个PDF设备对象
        laparams = LAParams()
        device = PDFPageAggregator(rsrcmgr, laparams=laparams)
        # 创建一个PDF解释器对象
        interpreter = PDFPageInterpreter(rsrcmgr, device)

        # 循环遍历列表,每次处理一个page的内容
        content = ''
        for page in doc.get_pages():
            interpreter.process_page(page)                        
            # 接受该页面的LTPage对象
            layout = device.get_result()
            # 这里layout是一个LTPage对象,里面存放着这个 page 解析出的各种对象
            # 包括 LTTextBox, LTFigure, LTImage, LTTextBoxHorizontal 等                            
            for x in layout:
                if isinstance(x, LTTextBox):
                    # print(x.get_text().strip())
                    content  = content + x.get_text().strip()
    return content


if __name__ == "__main__":
    main()

2. 使用pdfminer提取PDF的文本内容

这部分的代码来源于参考资料[1]。这里整理为函数的形式,代码为:

import os
os.chdir('D:/')  # PDF文件存放的位置
import logging 
logging.Logger.propagate = False 
logging.getLogger().setLevel(logging.ERROR)  # 只显示error级别的通知


def main():
    content = get_text_from_pdf('a')
    with open('a.txt', 'w', encoding='utf-8') as f:
        f.write(content)


def get_text_from_pdf(filename):
    from pdfminer.pdfparser import PDFParser, PDFDocument
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
    from pdfminer.converter import PDFPageAggregator
    from pdfminer.layout import LAParams, LTTextBox
    from pdfminer.pdfinterp import PDFTextExtractionNotAllowed

    path = filename+".pdf"

    # 用文件对象来创建一个pdf文档分析器
    praser = PDFParser(open(path, 'rb'))
    # 创建一个PDF文档
    doc = PDFDocument()
    # 连接分析器 与文档对象
    praser.set_document(doc)
    doc.set_parser(praser)

    # 提供初始化密码
    # 如果没有密码 就创建一个空的字符串
    doc.initialize()

    # 检测文档是否提供txt转换,不提供就忽略
    if not doc.is_extractable:
        raise PDFTextExtractionNotAllowed
    else:
        # 创建PDf 资源管理器 来管理共享资源
        rsrcmgr = PDFResourceManager()
        # 创建一个PDF设备对象
        laparams = LAParams()
        device = PDFPageAggregator(rsrcmgr, laparams=laparams)
        # 创建一个PDF解释器对象
        interpreter = PDFPageInterpreter(rsrcmgr, device)

        # 循环遍历列表,每次处理一个page的内容
        content = ''
        for page in doc.get_pages():
            interpreter.process_page(page)                        
            # 接受该页面的LTPage对象
            layout = device.get_result()
            # 这里layout是一个LTPage对象,里面存放着这个 page 解析出的各种对象
            # 包括 LTTextBox, LTFigure, LTImage, LTTextBoxHorizontal 等                            
            for x in layout:
                if isinstance(x, LTTextBox):
                    # print(x.get_text().strip())
                    content  = content + x.get_text().strip()
    return content


if __name__ == "__main__":
    main()

参考资料:

[1] Python:解析PDF文本及表格——pdfminer、tabula、pdfplumber 的用法及对比

996 次浏览

【说明:本站主要是个人的一些笔记和代码分享,内容可能会不定期修改。为了使全网显示的始终是最新版本,这里的文章未经同意请勿转载。引用请注明出处:https://www.guanjihuan.com

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

Captcha Code