使用Python搜索PDF中的文本?

时间:2013-06-13 23:07:39

标签: python parsing pdf text

问题
我试图通过搜索文本来确定文档的类型(例如恳求,通信,传票等),最好使用python。所有的PDF都是可搜索的,但是我还没有找到解决方案,用python解析它并应用脚本来搜索它(首先不是将它转换为文本文件,但对于n个文档来说这可能是资源密集型的)。

到目前为止我做了什么
我已经研究了pypdf,pdfminer,adobe pdf文档,以及我能找到的任何问题(虽然似乎没有一个直接解决这个问题)。 PDFminer似乎最具潜力,但在阅读完文档后我甚至不知道从哪里开始。

是否有一种简单有效的方法可以通过页面,行或整个文档阅读PDF文本?或任何其他解决方法?

9 个答案:

答案 0 :(得分:29)

这称为PDF挖掘,非常困难,因为:

  • PDF是一种文档格式,旨在打印,不需要解析。在PDF文档中, 在大多数情况下,文本没有特定的顺序(除非订单对于打印很重要) 原始文本结构丢失(字母可能不会分组 因为单词和单词可能不会被分组在句子中,以及它们的顺序 这篇论文经常是随机的。
  • 有大量的软件生成PDF,很多都是有缺陷的。

像PDFminer这样的工具使用启发式方法根据它们在页面中的位置再次对字母和单词进行分组。我同意,界面水平相当低,但是当你知道它时更有意义 他们试图解决什么问题(最重要的是,选择一个字母/单词/行必须与邻居有多接近才能被视为段落的一部分。)

一个昂贵的替代方案(在时间/计算机能力方面)为每个页面生成图像并将它们提供给OCR,如果你有一个非常好的OCR,可能值得一试。

所以我的答案是否定的,没有一种简单有效的方法可以从PDF文件中提取文本 - 如果你的文档具有已知的结构,你可以微调规则并获得好的结果,但它是总是赌博。

我真的很想被证明是错的。

[更新]

答案没有改变,但最近我参与了两个项目:其中一个项目是使用计算机视觉来从扫描的医院表格中提取数据。另一个从法庭记录中提取数据。我学到的是:

  1. 计算机视觉在2018年达到了凡人的目标。如果您拥有已经分类的文件的良好样本,您可以使用OpenCV或SciKit-Image来提取功能并训练机器学习分类器以确定什么类型文件是。

  2. 如果您正在分析的PDF是“可搜索的”,您可以使用诸如pdftotext和贝叶斯过滤器(用于对SPAM进行分类的相同类型的算法)之类的软件来提取所有文本。

  3. 因此,没有可靠有效的方法从PDF文件中提取文本,但您可能不需要一个方法来解决手头的问题(文档类型分类)。

答案 1 :(得分:11)

我为我所工作的公司编写了大量系统,将PDF转换成数据进行处理(发票,结算,扫描票证等),而@Paulo Scardine是正确的 - 没有完全可靠和简单的方法去做这个。也就是说,最快,最可靠,最不密集的方法是使用pdftotext xpdf工具集的一部分。该工具可以快速将可搜索的PDF转换为文本文件,您可以使用Python读取和解析该文件。提示:使用-layout参数。顺便说一句,并非所有PDF都是可搜索的,只有那些包含文本的PDF。某些PDF仅包含完全没有文本的图像。

答案 2 :(得分:8)

我完全是一只绿手,但不知何故,这个脚本对我有用:

# import packages
import PyPDF2
import re

# open the pdf file
object = PyPDF2.PdfFileReader("test.pdf")

# get number of pages
NumPages = object.getNumPages()

# define keyterms
String = "Social"

# extract text and do the search
for i in range(0, NumPages):
    PageObj = object.getPage(i)
    print("this is page " + str(i)) 
    Text = PageObj.extractText() 
    # print(Text)
    ResSearch = re.search(String, Text)
    print(ResSearch)

答案 3 :(得分:5)

我最近开始使用ScraperWiki来做你所描述的。

这是使用ScraperWiki提取PDF数据的example

scraperwiki.pdftoxml()函数返回XML结构。

然后,您可以使用BeautifulSoup将其解析为可导航树。

这是我的代码 -

import scraperwiki, urllib2
from bs4 import BeautifulSoup

def send_Request(url):
#Get content, regardless of whether an HTML, XML or PDF file
    pageContent = urllib2.urlopen(url)
    return pageContent

def process_PDF(fileLocation):
#Use this to get PDF, covert to XML
    pdfToProcess = send_Request(fileLocation)
    pdfToObject = scraperwiki.pdftoxml(pdfToProcess.read())
    return pdfToObject

def parse_HTML_tree(contentToParse):
#returns a navigatibale tree, which you can iterate through
    soup = BeautifulSoup(contentToParse)
    return soup

pdf = process_PDF('http://greenteapress.com/thinkstats/thinkstats.pdf')
pdfToSoup = parse_HTML_tree(pdf)
soupToArray = pdfToSoup.findAll('text')
for line in soupToArray:
    print line

这段代码将打印出一大堆<text>标签。 每个页面都以</page>分隔,如果有任何安慰的话。

如果您想要<text>标记内的内容(例如,可能包含<b>中包含的标题),请使用line.contents

如果您只想要每行文字(不包括标签),请使用line.getText()

它很麻烦,很痛苦,但这对于可搜索的PDF文档很有用。到目前为止,我发现这是准确的,但很痛苦。

答案 4 :(得分:4)

我同意@Paulo PDF数据挖掘是一个巨大的痛苦。但是你可能会成功使用pdftotext,它是Xpdf套件的一部分,可以在这里免费获得:

http://www.foolabs.com/xpdf/download.html

如果您只是寻找单个关键字,这应该足以满足您的目的。

pdftotext是一个命令行实用程序,但使用起来非常简单。它会为您提供文本文件,您可以更轻松地使用它们。

答案 5 :(得分:1)

这是我觉得这个问题很舒服的解决方案。在文本变量中,您可以从PDF获取文本以便在其中进行搜索。但我仍然保留了在本网站上发现的关键字文本的想法:https://medium.com/@rqaiserr/how-to-convert-pdfs-into-searchable-key-words-with-python-85aab86c544f我采用了这个解决方案,虽然使得nltk不是很简单,但它可能对进一步的目的有用:

import PyPDF2 
import textract

from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords

def searchInPDF(filename, key):
    occurrences = 0
    pdfFileObj = open(filename,'rb')
    pdfReader = PyPDF2.PdfFileReader(pdfFileObj)
    num_pages = pdfReader.numPages
    count = 0
    text = ""
    while count < num_pages:
        pageObj = pdfReader.getPage(count)
        count +=1
        text += pageObj.extractText()
    if text != "":
       text = text
    else:
       text = textract.process(filename, method='tesseract', language='eng')
    tokens = word_tokenize(text)
    punctuation = ['(',')',';',':','[',']',',']
    stop_words = stopwords.words('english')
    keywords = [word for word in tokens if not word in stop_words and  not word in punctuation]
    for k in keywords:
        if key == k: occurrences+=1
    return occurrences 

pdf_filename = '/home/florin/Downloads/python.pdf'
search_for = 'string'
print searchInPDF (pdf_filename,search_for)

答案 6 :(得分:1)

如果您对bash感兴趣,可以使用一个不错的工具pdfgrep, 由于,这位于apt信息库中,因此您可以使用以下命令进行安装:

sudo apt install pdfgrep

它很好地满足了我的要求。

答案 7 :(得分:0)

尝试为关键字挑选PDF并非易事。我尝试使用pdfminer库,但获得的成功非常有限。基本上是因为PDF在结构上是Pandemonium的化身。 PDF中的所有内容都可以独立或作为水平或垂直部分的一部分(向后或向前)。 Pdfminer在翻译一页时遇到问题,无法识别字体,因此我尝试了另一个方向-文档的光学字符识别。效果几乎完美。

棒将PDF中的所有单独页面转换为图像斑点,然后在图像斑点上运行OCR。我作为BytesIO对象拥有的是来自Web请求的PDF文件的内容。 BytesIO是一个流对象,它模拟文件加载,就像该对象脱离磁盘一样,魔杖需要作为文件参数。这样一来,您只需将数据存储在内存中,而不必先将文件保存到磁盘然后再加载。

这是一个非常基本的代码块,应该可以帮助您入门。我可以设想各种功能,这些功能将遍历不同的URL /文件,对每个文件使用不同的关键字搜索,并采取不同的操作,甚至可能对每个关键字和文件进行操作。

# http://docs.wand-py.org/en/0.5.9/
# http://www.imagemagick.org/script/formats.php
# brew install freetype imagemagick
# brew install PIL
# brew install tesseract
# pip3 install wand
# pip3 install pyocr
import pyocr.builders
import requests
from io import BytesIO
from PIL import Image as PI
from wand.image import Image

if __name__ == '__main__':
    pdf_url = 'https://www.vbgov.com/government/departments/city-clerk/city-council/Documents/CurrentBriefAgenda.pdf'
    req = requests.get(pdf_url)
    content_type = req.headers['Content-Type']
    modified_date = req.headers['Last-Modified']
    content_buffer = BytesIO(req.content)
    search_text = 'tourism investment program'

    if content_type == 'application/pdf':
        tool = pyocr.get_available_tools()[0]
        lang = 'eng' if tool.get_available_languages().index('eng') >= 0 else None
        image_pdf = Image(file=content_buffer, format='pdf', resolution=600)
        image_jpeg = image_pdf.convert('jpeg')

        for img in image_jpeg.sequence:
            img_page = Image(image=img)
            txt = tool.image_to_string(
                PI.open(BytesIO(img_page.make_blob('jpeg'))),
                lang=lang,
                builder=pyocr.builders.TextBuilder()
            )
            if search_text in txt.lower():
                print('Alert! {} {} {}'.format(search_text, txt.lower().find(search_text),
                                               modified_date))

    req.close()

答案 8 :(得分:0)

这个答案是@Emma Yu的:

如果要在每页上打印出所有字符串模式的匹配项。
(请注意,艾玛的代码每页显示一个匹配项):

@Operation(summary = "Save new")
@PostMapping("/store")
public ResponseEntity<ResponseRequest<TransDeliveryPlanning>> saveNewTransDeliveryPlanning(@RequestHeader HttpHeaders headers, 
        @Valid @RequestBody InputRequest<TransDeliveryPlanningDto> request) {

    String token = headers.getFirst(HttpHeaders.AUTHORIZATION);

    TransDeliveryPlanning newTransDeliveryPlanning = transDeliveryPlanningService.save(token, request);

    ResponseRequest<TransDeliveryPlanning> response = new ResponseRequest<TransDeliveryPlanning>();

    if (newTransDeliveryPlanning != null) {
        response.setMessage(PESAN_SIMPAN_BERHASIL);
        response.setData(newTransDeliveryPlanning);
    } else {
        response.setMessage(PESAN_SIMPAN_GAGAL);
    }

    return ResponseEntity.ok(response);
}