使用PdfMiner和PyPDF2提取文本合并列

use*_*334 7 python pypdf pdftotext

我试图使用pdfMiner解析pdf文件文本,但提取的文本被合并.我正在使用以下链接中的pdf文件.

PDF文件

我对任何类型的输出(文件/字符串)都很好.下面是代码,它将提取的文本作为字符串返回给我,但由于某种原因,列被合并.

from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
import StringIO

def convert_pdf(filename):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec)

    fp = file(filename, 'rb')
    process_pdf(rsrcmgr, device, fp)
    fp.close()
    device.close()

    str = retstr.getvalue()
    retstr.close()
    return str
Run Code Online (Sandbox Code Playgroud)

我也试过PyPdf2,但面临同样的问题.以下是PyPDF2的示例代码

from PyPDF2.pdf import PdfFileReader
import StringIO
import time

def getDataUsingPyPdf2(filename):
    pdf = PdfFileReader(open(filename, "rb"))
    content = ""

    for i in range(0, pdf.getNumPages()):
        print str(i)
        extractedText = pdf.getPage(i).extractText()
        content +=  extractedText + "\n"

    content = " ".join(content.replace("\xa0", " ").strip().split())
    return content.encode("ascii", "ignore")
Run Code Online (Sandbox Code Playgroud)

我也试过pdf2txt.py但无法获得格式化的输出.

小智 14

我最近遇到了类似的问题,尽管我的pdf结构稍微简单一些.

PDFMiner使用名为"devices"的类来解析pdf文件中的页面.基本设备类是PDFPageAggregator类,它只是解析文件中的文本框.转换器类(例如TextConverter,XMLConverter和HTMLConverter)也将结果输出到文件中(或在示例中的字符串流中),并对内容进行更精细的解析.

TextConverter(和PDFPageAggregator)的问题在于它们没有足够深入地处理文档结构以正确提取不同的列.另外两个转换器需要一些有关文档结构的信息以供显示,因此它们可以收集更详细的数据.在您的示例中,两个简单设备仅解析(粗略地)包含列的整个文本框,这使得不可能(或至少非常困难)正确地分隔不同的行.我发现的解决方案非常好,也就是说

  • 创建一个继承自PDFPageAggregator的新类,或
  • 使用XMLConverter并使用例如Beautifulsoup解析生成的XML文档

在这两种情况下,您都必须使用其边界框y坐标将不同的文本段组合到行.

在新设备类的情况下(我认为这更有说服力),您必须覆盖receive_layout在渲染过程中为每个页面调用的方法.然后,此方法递归地解析每个页面中的元素.例如,像这样的东西可能会让你开始:

from pdfminer.pdfdocument import PDFDocument, PDFNoOutlines
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LTPage, LTChar, LTAnno, LAParams, LTTextBox, LTTextLine

class PDFPageDetailedAggregator(PDFPageAggregator):
    def __init__(self, rsrcmgr, pageno=1, laparams=None):
        PDFPageAggregator.__init__(self, rsrcmgr, pageno=pageno, laparams=laparams)
        self.rows = []
        self.page_number = 0
    def receive_layout(self, ltpage):        
        def render(item, page_number):
            if isinstance(item, LTPage) or isinstance(item, LTTextBox):
                for child in item:
                    render(child, page_number)
            elif isinstance(item, LTTextLine):
                child_str = ''
                for child in item:
                    if isinstance(child, (LTChar, LTAnno)):
                        child_str += child.get_text()
                child_str = ' '.join(child_str.split()).strip()
                if child_str:
                    row = (page_number, item.bbox[0], item.bbox[1], item.bbox[2], item.bbox[3], child_str) # bbox == (x1, y1, x2, y2)
                    self.rows.append(row)
                for child in item:
                    render(child, page_number)
            return
        render(ltpage, self.page_number)
        self.page_number += 1
        self.rows = sorted(self.rows, key = lambda x: (x[0], -x[2]))
        self.result = ltpage
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,每个找到的LTTextLine元素都存储在一个有序的元组列表中,这些元组包含页码,边界框的坐标和该特定元素中包含的文本.然后你会做类似的事情:

from pprint import pprint
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.layout import LAParams

fp = open('pdf_doc.pdf', 'rb')
parser = PDFParser(fp)
doc = PDFDocument(parser)
doc.initialize('password') # leave empty for no password

rsrcmgr = PDFResourceManager()
laparams = LAParams()
device = PDFPageDetailedAggregator(rsrcmgr, laparams=laparams)
interpreter = PDFPageInterpreter(rsrcmgr, device)

for page in PDFPage.create_pages(doc):
    interpreter.process_page(page)
    # receive the LTPage object for this page
    device.get_result()

pprint(device.rows)
Run Code Online (Sandbox Code Playgroud)

变量device.rows包含有序列表,其中所有文本行使用其页码和y坐标排列.您可以使用相同的y坐标循环文本行和组线以形成行,存储列数据等.

我尝试使用上面的代码解析你的pdf,并且列主要是正确解析的.但是,有些列非常接近,因此默认的PDFMiner启发式方法无法将它们分离为自己的元素.您可以通过调整margin参数一词(命令行工具pdf2text.py中的-W标志)来解决这个问题.在任何情况下,您可能希望阅读(记录不完整的)PDFMiner API以及浏览PDFMiner的源代码,您可以从github获取该代码.(唉,我无法粘贴链接,因为我没有足够的重复点:'<,但你可以希望谷歌正确的回购)


Ste*_*edy 2

我尝试了你的第一个代码块并得到了一堆如下所示的结果:

多住宅花园综合体 14945010314370 至 372WILLOWRD W 多住宅花园综合体 14945010314380 至 384WILLOWRD W 多住宅花园综合体 149450103141000 至 1020WILLOWBROOKRD MULT IPLE Dwelling 客房 198787

我猜您的处境与此答案类似,并且所有空格都用于将单词放置在正确的位置,而不是作为实际的可打印空格字符。您尝试使用其他 pdf 库的事实让我认为这可能是任何 pdf 库都难以解析的问题。