2013-04-01 26 views
5

我想解析PDF文件使用pdfMiner,但提取的文本得到合併。我正在使用以下鏈接中的pdf文件。使用PdfMiner和PyPDF2合併列提取文本

PDF File

我好與任何類型的輸出(文件/串)。下面是代碼,它將提取的文本作爲字符串返回給我,但由於某些原因,列被合併。

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 

我也試過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") 

示例代碼我也曾嘗試pdf2txt.py但無法得到格式化輸出。

+0

應該第一個代碼塊讀取'retstr = StringIO.StringIO()'? – Stedy

+0

從pdf中閱讀多列是非常痛苦的。取決於你想要什麼[k2pdfopt](http://www.willus.com/k2pdfopt/)從每個頁面製作圖像。 – bobrobbob

回答

1

我想你的代碼的第一個塊,並得到了一堆的結果看起來像這樣:

多住戶AGARDEN COMPLEX 14945010314370 TO 372WILLOWRD W多重住家AGARDEN COMPLEX 14945010314380 TO 384WILLOWRD W多重住家AGARDEN COMPLEX 149450103141000 TO 1020WILLOWBROOKRD多住宅AROOMING HOUSE 198787

我在猜測你和answer處於類似的位置,並且所有的空格都用於將單詞放在正確的位置,而不是實際可打印的空格字符。事實上,你已經嘗試過與其他PDF庫一起使用,這讓我認爲這可能是任何PDF庫難以解析的問題。

13

我最近在類似的問題上掙扎,雖然我的pdf結構稍微簡單一些。

PDFMiner使用稱爲「設備」的類來解析PDF文件中的頁面。基本設備類是PDFPageAggregator類,它簡單地解析文件中的文本框。轉換器類,例如TextConverter,XMLConverter和HTMLConverter也會將結果輸出到一個文件中(或者像在你的例子中那樣在一個字符串流中)並且對內容做更精細的解析。

TextConverter(和PDFPageAggregator)的問題在於,它們不會遞歸足夠深的文檔結構以正確提取不同的列。另外兩個轉換器需要一些關於文檔結構的信息用於顯示,以便收集更詳細的數據。在你的例子pdf中,這兩個簡單的設備只能粗略地解析包含列的整個文本框,這使得不可能(或者至少非常困難)正確分離不同的行。這個,我發現非常有效的解決方案,是要麼

  • 創建一個新的類,從PDFPageAggregator繼承,或
  • 使用XMLConverter和分析使用例如生成的XML文檔Beautifulsoup

在這兩種情況下,您都必須使用邊框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 

在上面,每找到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) 

變量device.rows包含了所有的文本行安排爲使用他們的頁碼和Y座標的有序列表。你可以用相同的y座標遍歷文本行和組行,形成行,存儲列數據等。

我試圖用上面的代碼解析你的pdf,並且大多數列大部分都是正確解析的。但是,某些列非常接近,默認的PDFMiner啓發式算法無法將它們分離到各自的元素中。您可以通過調整單詞margin參數(命令行工具pdf2text.py中的-W標誌)來解決此問題。無論如何,您可能需要閱讀(文檔記錄不佳)PDFMiner API以及瀏覽可從github獲得的PDFMiner的源代碼。 (唉,我不能粘貼鏈接,因爲我沒有足夠的代表點:'<,但你可以希望谷歌正確的回購)