" />

欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python使用PDFMiner解析PDF代碼實(shí)例

 更新時(shí)間:2017年03月27日 10:37:27   作者:JamesPei  
本篇文章主要介紹了Python使用PDFMiner解析PDF代碼實(shí)例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

近期在做爬蟲時(shí)有時(shí)會(huì)遇到網(wǎng)站只提供pdf的情況,這樣就不能使用scrapy直接抓取頁面內(nèi)容了,只能通過解析PDF的方式處理,目前的解決方案大致只有pyPDF和PDFMiner。因?yàn)閾?jù)說PDFMiner更適合文本的解析,而我需要解析的正是文本,因此最后選擇使用PDFMiner(這也就意味著我對(duì)pyPDF一無所知了)。

即使是PDFMiner對(duì)于格式不工整的PDF解析效果也不怎么樣,所以連PDFMiner的開發(fā)者都吐槽PDF is evil. 不過這些并不重要。官方文檔在此:http://www.unixuser.org/~euske/python/pdfminer/index.html

一.安裝:

1.首先下載源文件包 http://pypi.python.org/pypi/pdfminer/,解壓,然后命令行安裝即可:python setup.py install

2.安裝完成后使用該命令行測(cè)試:pdf2txt.py samples/simple1.pdf,如果顯示以下內(nèi)容則表示安裝成功:

Hello World Hello World H e l l o W o r l d H e l l o W o r l d

3.如果要使用中日韓文字則需要先編譯再安裝: 

# make cmap

python tools/conv_cmap.py pdfminer/cmap Adobe-CNS1 cmaprsrc/cid2code_Adobe_CNS1.txtreading 'cmaprsrc/cid2code_Adobe_CNS1.txt'...writing 'CNS1_H.py'......(this may take several minutes) 

# python setup.py install 

二.使用

由于解析PDF是一件非常耗時(shí)和內(nèi)存的工作,因此PDFMiner使用了一種稱作lazy parsing的策略,只在需要的時(shí)候才去解析,以減少時(shí)間和內(nèi)存的使用。要解析PDF至少需要兩個(gè)類:PDFParser 和 PDFDocument,PDFParser 從文件中提取數(shù)據(jù),PDFDocument保存數(shù)據(jù)。另外還需要PDFPageInterpreter去處理頁面內(nèi)容,PDFDevice將其轉(zhuǎn)換為我們所需要的。PDFResourceManager用于保存共享內(nèi)容例如字體或圖片。

Figure 1. Relationships between PDFMiner classes

比較重要的是Layout,主要包括以下這些組件:

LTPage

Represents an entire page. May contain child objects like LTTextBox, LTFigure, LTImage, LTRect, LTCurve and LTLine.

LTTextBox

Represents a group of text chunks that can be contained in a rectangular area. Note that this box is created by geometric analysis and does not necessarily represents a logical boundary of the text. It contains a list of LTTextLine objects. get_text() method returns the text content.

LTTextLine

Contains a list of LTChar objects that represent a single text line. The characters are aligned either horizontaly or vertically, depending on the text's writing mode. get_text() method returns the text content.

LTChar

LTAnno

Represent an actual letter in the text as a Unicode string. Note that, while a LTChar object has actual boundaries, LTAnno objects does not, as these are "virtual" characters, inserted by a layout analyzer according to the relationship between two characters (e.g. a space).

LTFigure

Represents an area used by PDF Form objects. PDF Forms can be used to present figures or pictures by embedding yet another PDF document within a page. Note that LTFigure objects can appear recursively.

LTImage

Represents an image object. Embedded images can be in JPEG or other formats, but currently PDFMiner does not pay much attention to graphical objects.

LTLine

Represents a single straight line. Could be used for separating text or figures.

LTRect

Represents a rectangle. Could be used for framing another pictures or figures.

LTCurve

Represents a generic Bezier curve.

 

官方文檔給了幾個(gè)Demo但是都過于簡(jiǎn)略,雖然給了一個(gè)詳細(xì)一些的Demo,但鏈接地址是舊的現(xiàn)在已經(jīng)失效,不過最終還是找到了新的地址:http://denis.papathanasiou.org/posts/2010.08.04.post.html

這個(gè)Demo就比較詳細(xì)了,源碼如下:

#!/usr/bin/python

import sys
import os
from binascii import b2a_hex

###
### pdf-miner requirements
###

from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument, PDFNoOutlines
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTTextBox, LTTextLine, LTFigure, LTImage, LTChar

def with_pdf (pdf_doc, fn, pdf_pwd, *args):
 """Open the pdf document, and apply the function, returning the results"""
 result = None
 try:
  # open the pdf file
  fp = open(pdf_doc, 'rb')
  # create a parser object associated with the file object
  parser = PDFParser(fp)
  # create a PDFDocument object that stores the document structure
  doc = PDFDocument(parser, pdf_pwd)
  # connect the parser and document objects
  parser.set_document(doc)
  # supply the password for initialization

  if doc.is_extractable:
   # apply the function and return the result
   result = fn(doc, *args)

  # close the pdf file
  fp.close()
 except IOError:
  # the file doesn't exist or similar problem
  pass
 return result


### 
### Table of Contents
### 

def _parse_toc (doc):
 """With an open PDFDocument object, get the table of contents (toc) data
 [this is a higher-order function to be passed to with_pdf()]"""
 toc = []
 try:
  outlines = doc.get_outlines()
  for (level,title,dest,a,se) in outlines:
   toc.append( (level, title) )
 except PDFNoOutlines:
  pass
 return toc

def get_toc (pdf_doc, pdf_pwd=''):
 """Return the table of contents (toc), if any, for this pdf file"""
 return with_pdf(pdf_doc, _parse_toc, pdf_pwd)


###
### Extracting Images
###

def write_file (folder, filename, filedata, flags='w'):
 """Write the file data to the folder and filename combination
 (flags: 'w' for write text, 'wb' for write binary, use 'a' instead of 'w' for append)"""
 result = False
 if os.path.isdir(folder):
  try:
   file_obj = open(os.path.join(folder, filename), flags)
   file_obj.write(filedata)
   file_obj.close()
   result = True
  except IOError:
   pass
 return result

def determine_image_type (stream_first_4_bytes):
 """Find out the image file type based on the magic number comparison of the first 4 (or 2) bytes"""
 file_type = None
 bytes_as_hex = b2a_hex(stream_first_4_bytes)
 if bytes_as_hex.startswith('ffd8'):
  file_type = '.jpeg'
 elif bytes_as_hex == '89504e47':
  file_type = '.png'
 elif bytes_as_hex == '47494638':
  file_type = '.gif'
 elif bytes_as_hex.startswith('424d'):
  file_type = '.bmp'
 return file_type

def save_image (lt_image, page_number, images_folder):
 """Try to save the image data from this LTImage object, and return the file name, if successful"""
 result = None
 if lt_image.stream:
  file_stream = lt_image.stream.get_rawdata()
  if file_stream:
   file_ext = determine_image_type(file_stream[0:4])
   if file_ext:
    file_name = ''.join([str(page_number), '_', lt_image.name, file_ext])
    if write_file(images_folder, file_name, file_stream, flags='wb'):
     result = file_name
 return result


###
### Extracting Text
###

def to_bytestring (s, enc='utf-8'):
 """Convert the given unicode string to a bytestring, using the standard encoding,
 unless it's already a bytestring"""
 if s:
  if isinstance(s, str):
   return s
  else:
   return s.encode(enc)

def update_page_text_hash (h, lt_obj, pct=0.2):
 """Use the bbox x0,x1 values within pct% to produce lists of associated text within the hash"""

 x0 = lt_obj.bbox[0]
 x1 = lt_obj.bbox[2]

 key_found = False
 for k, v in h.items():
  hash_x0 = k[0]
  if x0 >= (hash_x0 * (1.0-pct)) and (hash_x0 * (1.0+pct)) >= x0:
   hash_x1 = k[1]
   if x1 >= (hash_x1 * (1.0-pct)) and (hash_x1 * (1.0+pct)) >= x1:
    # the text inside this LT* object was positioned at the same
    # width as a prior series of text, so it belongs together
    key_found = True
    v.append(to_bytestring(lt_obj.get_text()))
    h[k] = v
 if not key_found:
  # the text, based on width, is a new series,
  # so it gets its own series (entry in the hash)
  h[(x0,x1)] = [to_bytestring(lt_obj.get_text())]

 return h

def parse_lt_objs (lt_objs, page_number, images_folder, text=[]):
 """Iterate through the list of LT* objects and capture the text or image data contained in each"""
 text_content = [] 

 page_text = {} # k=(x0, x1) of the bbox, v=list of text strings within that bbox width (physical column)
 for lt_obj in lt_objs:
  if isinstance(lt_obj, LTTextBox) or isinstance(lt_obj, LTTextLine):
   # text, so arrange is logically based on its column width
   page_text = update_page_text_hash(page_text, lt_obj)
  elif isinstance(lt_obj, LTImage):
   # an image, so save it to the designated folder, and note its place in the text 
   saved_file = save_image(lt_obj, page_number, images_folder)
   if saved_file:
    # use html style <img /> tag to mark the position of the image within the text
    text_content.append('<img src="'+os.path.join(images_folder, saved_file)+'" />')
   else:
    print >> sys.stderr, "error saving image on page", page_number, lt_obj.__repr__
  elif isinstance(lt_obj, LTFigure):
   # LTFigure objects are containers for other LT* objects, so recurse through the children
   text_content.append(parse_lt_objs(lt_obj, page_number, images_folder, text_content))

 for k, v in sorted([(key,value) for (key,value) in page_text.items()]):
  # sort the page_text hash by the keys (x0,x1 values of the bbox),
  # which produces a top-down, left-to-right sequence of related columns
  text_content.append(''.join(v))

 return '\n'.join(text_content)


###
### Processing Pages
###

def _parse_pages (doc, images_folder):
 """With an open PDFDocument object, get the pages and parse each one
 [this is a higher-order function to be passed to with_pdf()]"""
 rsrcmgr = PDFResourceManager()
 laparams = LAParams()
 device = PDFPageAggregator(rsrcmgr, laparams=laparams)
 interpreter = PDFPageInterpreter(rsrcmgr, device)

 text_content = []
 for i, page in enumerate(PDFPage.create_pages(doc)):
  interpreter.process_page(page)
  # receive the LTPage object for this page
  layout = device.get_result()
  # layout is an LTPage object which may contain child objects like LTTextBox, LTFigure, LTImage, etc.
  text_content.append(parse_lt_objs(layout, (i+1), images_folder))

 return text_content

def get_pages (pdf_doc, pdf_pwd='', images_folder='/tmp'):
 """Process each of the pages in this pdf file and return a list of strings representing the text found in each page"""
 return with_pdf(pdf_doc, _parse_pages, pdf_pwd, *tuple([images_folder]))

a = open('a.txt','a')
for i in get_pages('/home/jamespei/nova.pdf'):
 a.write(i)
a.close()

這段代碼重點(diǎn)在于第128行,可以看到PDFMiner是一種基于坐標(biāo)來解析的框架,PDF中能解析的組件全都包括上下左右邊緣的坐標(biāo),如x0 = lt_obj.bbox[0]就是lt_obj元素的左邊緣的坐標(biāo),同理x1則為右邊緣。以上代碼的意思就是把所有x0且x1的坐標(biāo)相差在20%以內(nèi)的元素分成一組,這樣就實(shí)現(xiàn)了從PDF文件中定向抽取內(nèi)容。

----------------補(bǔ)充--------------------

有一個(gè)需要注意的地方,在解析有些PDF的時(shí)候會(huì)報(bào)這樣的異常:pdfminer.pdfdocument.PDFEncryptionError: Unknown algorithm: param={'CF': {'StdCF': {'Length': 16, 'CFM': /AESV2, 'AuthEvent': /DocOpen}}, 'O': '\xe4\xe74\xb86/\xa8)\xa6x\xe6\xa3/U\xdf\x0fWR\x9cPh\xac\xae\x88B\x06_\xb0\x93@\x9f\x8d', 'Filter': /Standard, 'P': -1340, 'Length': 128, 'R': 4, 'U': '|UTX#f\xc9V\x18\x87z\x10\xcb\xf5{\xa7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'V': 4, 'StmF': /StdCF, 'StrF': /StdCF}

從字面意思來看是因?yàn)檫@個(gè)PDF是一個(gè)加密的PDF,所以無法解析 ,但是如果直接打開PDF卻是可以的并沒有要求輸密碼什么的,原因是這個(gè)PDF雖然是加過密的,但密碼是空,所以就出現(xiàn)了這樣的問題。

解決這個(gè)的問題的辦法是通過qpdf命令來解密文件(要確保已經(jīng)安裝了qpdf),要想在python中調(diào)用該命令只需使用call即可:

 from subprocess import call
call('qpdf --password=%s --decrypt %s %s' %('', file_path, new_file_path), shell=True)

其中參數(shù)file_path是要解密的PDF的路徑,new_file_path是解密后的PDF文件路徑,然后使用解密后的文件去做解析就OK了

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • python中set()函數(shù)簡(jiǎn)介及實(shí)例解析

    python中set()函數(shù)簡(jiǎn)介及實(shí)例解析

    這篇文章主要介紹了python中set()函數(shù)簡(jiǎn)介及實(shí)例解析,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • python3發(fā)送郵件需要經(jīng)過代理服務(wù)器的示例代碼

    python3發(fā)送郵件需要經(jīng)過代理服務(wù)器的示例代碼

    今天小編就為大家分享一篇python3發(fā)送郵件需要經(jīng)過代理服務(wù)器的示例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • PyTorch如何搭建一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)

    PyTorch如何搭建一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)

    這篇文章主要介紹了PyTorch如何搭建一個(gè)簡(jiǎn)單的網(wǎng)絡(luò),幫助大家更好的理解和學(xué)習(xí)PyTorch,感興趣的朋友可以了解下
    2020-08-08
  • pycharm安裝深度學(xué)習(xí)pytorch的d2l包失敗問題解決

    pycharm安裝深度學(xué)習(xí)pytorch的d2l包失敗問題解決

    當(dāng)新生在學(xué)習(xí)pytorch時(shí),導(dǎo)入d2l_pytorch包總會(huì)遇到問題,下面這篇文章主要給大家介紹了關(guān)于pycharm安裝深度學(xué)習(xí)pytorch的d2l包失敗問題的解決方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • python3+RobotFramework環(huán)境搭建過程

    python3+RobotFramework環(huán)境搭建過程

    之前用的python2.7+robotframework進(jìn)行的自動(dòng)化測(cè)試,python3的還沒嘗試,今天嘗試了下,搭建環(huán)境的時(shí)候也是各種報(bào)錯(cuò),今天給大家分享下python3+RobotFramework環(huán)境搭建過程,感興趣的朋友一起看看吧
    2023-08-08
  • 使用Python實(shí)現(xiàn)有趣的鎖屏小工具

    使用Python實(shí)現(xiàn)有趣的鎖屏小工具

    這篇文章主要為大家詳細(xì)介紹了如何使用Python實(shí)現(xiàn)有趣的鎖屏小工具,這樣再也不用擔(dān)心因?yàn)闆]有鎖屏被扣工資啦,打工人快跟隨小編一起學(xué)習(xí)一下吧
    2023-12-12
  • Pandas DataFrame數(shù)據(jù)存儲(chǔ)格式比較分析

    Pandas DataFrame數(shù)據(jù)存儲(chǔ)格式比較分析

    Pandas 支持多種存儲(chǔ)格式,在本文中將對(duì)不同類型存儲(chǔ)格式下的Pandas Dataframe的讀取速度、寫入速度和大小的進(jìn)行測(cè)試對(duì)比,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2023-09-09
  • python案例中Flask全局配置示例詳解

    python案例中Flask全局配置示例詳解

    這篇文章主要為大家介紹了python案例中Flask全局配置示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 使用NumPy進(jìn)行數(shù)組數(shù)據(jù)處理的示例詳解

    使用NumPy進(jìn)行數(shù)組數(shù)據(jù)處理的示例詳解

    NumPy是Python中用于數(shù)值計(jì)算的核心包之一,它提供了大量的高效數(shù)組操作函數(shù)和數(shù)學(xué)函數(shù),可以支持多維數(shù)組和矩陣運(yùn)算。本文主要為大家介紹了NumPy進(jìn)行數(shù)組數(shù)據(jù)處理的具體方法,需要的可以參考一下
    2023-03-03
  • python讀取raw binary圖片并提取統(tǒng)計(jì)信息的實(shí)例

    python讀取raw binary圖片并提取統(tǒng)計(jì)信息的實(shí)例

    今天小編就為大家分享一篇python讀取raw binary圖片并提取統(tǒng)計(jì)信息的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-01-01

最新評(píng)論