52

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

到目前为止
,我已经查看了 pypdf、pdfminer、adobe pdf 文档以及我能找到的任何问题(尽管似乎没有一个可以直接解决这个问题)。PDFminer 似乎最具潜力,但在阅读完文档后,我什至不确定从哪里开始。

是否有一种简单有效的方法来阅读 PDF 文本,无论是按页、行还是整个文档?还是有其他解决方法?

4

11 回答 11

50

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

  • PDF 是一种设计用于打印而非解析的文档格式。在 PDF 文档中,文本没有特定的顺序(除非顺序对打印很重要),大部分时间原始文本结构丢失(字母可能不会分组为单词,单词可能不会在句子中分组,并且顺序它们被放置在纸上往往是随机的)。
  • 有大量生成 PDF 的软件,其中很多都是有缺陷的。

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

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

所以我的回答是否定的,没有从 PDF 文件中提取文本的简单、有效的方法——如果你的文档有一个已知的结构,你可以微调规则并获得好的结果,但这始终是一种赌博.

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

[更新]

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

  1. 计算机视觉在 2018 年触手可及。如果您有一个很好的已分类文档样本,您可以使用 OpenCV 或 SciKit-Image 来提取特征并训练机器学习分类器来确定文档的类型。

  2. 如果您正在分析的 PDF 是“可搜索的”,您可以使用pdftotext和贝叶斯过滤器(用于分类垃圾邮件的同一类算法)等软件提取所有文本。

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

于 2013-06-14T00:52:40.733 回答
37

我完全是一个新手,但这个脚本对我有用:

# 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)
于 2018-06-10T03:46:48.290 回答
17

我为我工作的公司编写了广泛的系统,将 PDF 转换为数据进行处理(发票、结算、扫描的票证等),@Paulo Scardine 是正确的——没有完全可靠和简单的方法可以做到这一点. 也就是说,最快、最可靠和最省力的方法是使用xpdfpdftotext工具集的一部分。此工具将快速将可搜索的 PDF 转换为文本文件,您可以使用 Python 阅读和解析该文件。提示:使用参数。顺便说一句,并不是所有的 PDF 都是可搜索的,只有那些包含文本的。一些 PDF 只包含完全没有文字的图像。-layout

于 2013-06-14T01:07:30.010 回答
9

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

这是使用 ScraperWiki 提取 PDF 数据的示例

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 文档。到目前为止,我发现这是准确的,但很痛苦。

于 2015-11-14T07:38:16.890 回答
6

这是我觉得很适合这个问题的解决方案。在文本变量中,您可以从 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)
于 2017-12-01T12:12:23.947 回答
5

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

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

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

pdftotext是一个命令行实用程序,但使用起来非常简单。它将为您提供文本文件,您可能会发现这些文件更易于使用。

于 2013-06-14T01:02:50.270 回答
3

如果您使用 bash,有一个名为pdfgrep的好工具,因为它位于 apt 存储库中,您可以使用以下命令安装它:

sudo apt install pdfgrep

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

于 2020-07-13T12:31:34.917 回答
0

使用 PyMuPDF 的版本。我发现它比 PyPDF2 更强大。

import fitz
import re

# load document
doc = fitz.open(filename)

# define keyterms
String = "hours"

# get text, search for string and print count on page.
for page in doc:
    text = ''
    text += page.getText()
    print(f'count on page {page.number +1} is: {len(re.findall(String, text))}')
于 2021-11-12T18:45:03.923 回答
0

这个答案遵循@Emma Yu的:

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

import PyPDF2
import re

pattern = input("Enter string pattern to search: ")
fileName = input("Enter file path and name: ")

object = PyPDF2.PdfFileReader(fileName)
numPages = object.getNumPages()

for i in range(0, numPages):
    pageObj = object.getPage(i)
    text = pageObj.extractText()
   
    for match in re.finditer(pattern, text):
        print(f'Page no: {i} | Match: {match}')
于 2020-11-29T13:53:53.737 回答
0

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

Wand 将 PDF 中的所有单独页面转换为图像 blob,然后在图像 blob 上运行 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()
于 2020-05-10T15:10:36.623 回答
0

pdfminer.six示例

from pdfminer import high_level

with open('file.pdf', 'rb') as f:
    text = high_level.extract_text(f)
    print(text)

与 PyPDF2 相比,它可以使用西里尔字母

于 2021-12-28T00:05:00.413 回答