【问题标题】:Searching text in a PDF using Python?使用 Python 在 PDF 中搜索文本?
【发布时间】:2013-06-10 12:34:10
【问题描述】:

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

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

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

【问题讨论】:

  • 我一直在寻找相同的解决方案。问题在于 PDF 文档因将文本分解成难以重新组合的块而臭名昭著。这取决于编写 PDF 的程序。我最终使用 PDFminer 和大量“elif”代码来解析 PDF。
  • 只是一个想法,也许不切实际...如果您急于找到解决方法,可以尝试调用 pdfgrep (pdfgrep.sourceforge.net) 进行搜索。

标签: python parsing pdf text


【解决方案1】:

pdfminer.six 为例

from pdfminer import high_level

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

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

【讨论】:

    【解决方案2】:

    使用 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))}')
    

    【讨论】:

      【解决方案3】:

      这个答案遵循@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}')
      

      【讨论】:

        【解决方案4】:

        如果你使用 bash,有一个不错的工具叫做 pdfgrep, 因为,这是在 apt 存储库中,您可以使用以下方式安装它:

        sudo apt install pdfgrep
        

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

        【讨论】:

          【解决方案5】:

          尝试在 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()
          

          【讨论】:

            【解决方案6】:

            我完全是个新手,但是这个脚本适合我:

            # 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)
            

            【讨论】:

            • 嗨,Amey,只需将“社交”更改为您要搜索的任何文本!
            • 嗨,Emma,搜索不是问题,但我需要将这个词替换为其他词。例如,将“社交”一词替换为“朋友”。
            • @AmeyPNaik 那将是修改 PDF,而不仅仅是阅读/搜索 PDF。在没有布局和格式问题的情况下以编程方式修改现有 PDF 更加复杂。
            • 注意:在很多 PDF 页面上,这只会读取页眉和页脚,而不是其余页面。
            【解决方案7】:

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

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

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

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

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

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

            [更新]

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

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

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

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

            【讨论】:

            • 所有文档都以 pdf 格式扫描并经过 OCR 处理以可供搜索——这与您所描述的不同吗?
            • @Insarov:正是我所说的,任何物有所值的 OCR 都可以选择输出纯文本文件和 PDF 文件。
            • 如果在相对一致的叙述上下文(不一定是格式)中有相当大的文档样本,我们可以训练一个人工智能来理解它,所以它可以用来阅读 PDF 文件的文本超出样本范围?
            • @xappppp 只要有足够的时间和资源,几乎一切皆有可能。
            【解决方案8】:

            这是我觉得可以解决这个问题的解决方案。在文本变量中,您可以从 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)
            

            【讨论】:

              【解决方案9】:

              我最近开始使用 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
              

              这段代码将打印一大堆丑陋的&lt;text&gt; 标签。 每一页都用&lt;/page&gt; 分隔,如果有什么安慰的话。

              如果您想要 &lt;text&gt; 标记内的内容,例如可能包括用 &lt;b&gt; 包裹的标题,请使用 line.contents

              如果你只想要每一行文本,不包括标签,使用line.getText()

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

              【讨论】:

              • 我尝试使用scraperwiki,我得到系统找不到指定路径的错误。 @JasTonAChair 任何帮助表示赞赏。
              • @JasTonAChair 出现错误:- BeautifulSoup([your markup]) to this: BeautifulSoup([your markup], "lxml")
              【解决方案10】:

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

              【讨论】:

              • 为什么它是最快最可靠的方法?有证据吗?
              • 如果有办法将 PDF 转换为文本文件,有没有办法在不编写实际新文件的情况下做到这一点?类似于将文档读入内存? (至少,以一种与转换一样直接的方式?)。
              • @Insarov,我不这么认为,不是 pdftotext。但我可能错了,你必须检查文档。你可以使用 pyPdf 和 pdfminer 来做到这一点,但它们比 pdftotext 慢很多,即使 pdftotext 写入文件也是如此。
              • @Insarov 来自 pdftotext 文档,“如果文本文件为 '-',则将文本发送到标准输出。”因此,您可以使用grep 或类似名称将其通过管道传输到搜索中。
              【解决方案11】:

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

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

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

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-01-21
                • 2014-05-26
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多