如何使用 pdfminer 作为库

Posted

技术标签:

【中文标题】如何使用 pdfminer 作为库【英文标题】:How do I use pdfminer as a library 【发布时间】:2011-08-09 04:15:26 【问题描述】:

我正在尝试使用pdfminer 从 pdf 中获取文本数据。我可以使用 pdfminer 命令行工具 pdf2txt.py 成功地将这些数据提取到 .txt 文件中。我目前这样做,然后使用 python 脚本清理 .txt 文件。我想将 pdf 提取过程合并到脚本中并为自己节省一步。

I thought I was on to something when I found this link,但我没有成功解决任何问题。也许那里列出的功能需要再次更新,因为我使用的是更新版本的 pdfminer。

I also tried the function shown here, but it also did not work.

我尝试的另一种方法是使用os.system 在脚本中调用脚本。这也失败了。

我正在使用 Python 版本 2.7.1 和 pdfminer 版本 20110227。

【问题讨论】:

你试过this吗? 我确实看过api谢谢,但不幸的是我的python技能不够强,无法获得工作功能。 您可能会发现此解决方案对类似问题很有用:***.com/a/61857301/7483211 【参考方案1】:

完全披露,我是pdfminer.six 的维护者之一。它是用于 python 3 的 pdfminer 的社区维护版本。

现在,它有多个 api 用于从 PDF 中提取文本,具体取决于您的需要。在幕后,所有这些 api 都使用相同的逻辑来解析和分析布局。

(所有示例都假定您的 PDF 文件名为 example.pdf

命令行

如果您只想提取文本一次,可以使用命令行工具 pdf2txt.py:

$ pdf2txt.py example.pdf

高级 API

如果你想用 Python 提取文本(属性),你可以使用高级 api。如果您想以编程方式从 PDF 中提取信息,这种方法是首选解决方案。

from pdfminer.high_level import extract_text

# Extract text from a pdf.
text = extract_text('example.pdf')

# Extract iterable of LTPage objects.
pages = extract_pages('example.pdf')

可组合的 api

还有一个可组合的 api,它在处理生成的对象时提供了很大的灵活性。例如,它允许您创建自己的布局算法。其他答案中建议使用此方法,但仅当您需要自定义某些组件时,我才会推荐此方法。

from io import StringIO

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

output_string = StringIO()
with open('example.pdf', 'rb') as in_file:
    parser = PDFParser(in_file)
    doc = PDFDocument(parser)
    rsrcmgr = PDFResourceManager()
    device = TextConverter(rsrcmgr, output_string, laparams=LAParams())
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    for page in PDFPage.create_pages(doc):
        interpreter.process_page(page)

print(output_string.getvalue())

【讨论】:

嗨@Pieter,你能看看这个问题吗:***.com/questions/68614884/…【参考方案2】:

这是我的解决方案

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO
import os 

def convert_pdf_to_txt(path, pages=None):
    if not pages:
        pagenums = set()
    else:
        pagenums = set(pages)
    output = StringIO()
    manager = PDFResourceManager()
    converter = TextConverter(manager, output, laparams=LAParams())
    interpreter = PDFPageInterpreter(manager, converter)

    infile = open(path, 'rb')
    for page in PDFPage.get_pages(infile, pagenums):
        interpreter.process_page(page)
    infile.close()
    converter.close()
    text = output.getvalue()
    output.close()
    return text

例如,您只想阅读 pdf 文件的前 3 页:

text = convert_pdf_to_txt('../Data/EN-FINAL Table 9.pdf', pages=[0,1,2])

pdfminer.six==20160614

python: 3.x

【讨论】:

不错!这对我有用。 Python 3.6.2 pdfminer.six Python 3.6 / pdfminer.six / Windows 7下的最佳解决方案 嗨。这将为我的pdf返回这样的字符串。 '(cid:42)(cid:82)(cid:89)(cid:72)(cid:85)(cid:81)(cid:80)(cid:72)(cid:81)(cid:87 )(cid:3)(cid:57)(cid:72)(cid:75)(cid:76)(cid:70)(cid:79)(cid:72)(cid:86)\n\n (cid:20)(cid:25)(cid:18)(cid:19)(cid:2id:87)(cid:86)(cid:15)(cid:3)(cid:37)(cid: 79)'【参考方案3】:

这个在 python 3 中为我工作。 它需要 PDFMiner.six 包

pip install pdfminer.six

代码如下(和大家的代码一样,有小修正):

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from six import StringIO

def convert_pdf_to_txt(path):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    fp = open(path, 'rb')
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos=set()
    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages, password=password,caching=caching, check_extractable=True):
        interpreter.process_page(page)
    fp.close()
    device.close()
    str = retstr.getvalue()
    retstr.close()

    return str.replace("\\n","\n")

【讨论】:

【参考方案4】:

这适用于我使用最新版本的 pdfminer(截至 2014 年 9 月):

from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfpage import PDFTextExtractionNotAllowed
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.pdfdevice import PDFDevice
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
import unicodedata, codecs
from io import StringIO

def getPDFText(pdfFilenamePath):
    retstr = StringIO()
    parser = PDFParser(open(pdfFilenamePath,'r'))
    try:
        document = PDFDocument(parser)
    except Exception as e:
        print(pdfFilenamePath,'is not a readable pdf')
        return ''
    if document.is_extractable:
        rsrcmgr = PDFResourceManager()
        device = TextConverter(rsrcmgr,retstr, codec='ascii' , laparams = LAParams())
        interpreter = PDFPageInterpreter(rsrcmgr, device)
        for page in PDFPage.create_pages(document):
            interpreter.process_page(page)
        return retstr.getvalue()
    else:
        print(pdfFilenamePath,"Warning: could not extract text from pdf file.")
        return ''

if __name__ == '__main__':
    words = getPDFText(path)

【讨论】:

当它说pdf不可读时该怎么办?【参考方案5】:

这是一个适用于运行 python 3.6 的 pdfminer.six 的答案。如果您只想从简单的 PDF 文件中提取原始文本,它使用 pdfminer.high_level 模块抽象出许多底层细节。

import pdfminer
import io

def extract_raw_text(pdf_filename):
    output = io.StringIO()
    laparams = pdfminer.layout.LAParams() # Using the defaults seems to work fine

    with open(pdf_filename, "rb") as pdffile:
        pdfminer.high_level.extract_text_to_fp(pdffile, output, laparams=laparams)

    return output.getvalue()

【讨论】:

【参考方案6】:

以下代码 sn-ps 能够使用最新版本的 pdfminer(截至 2016 年 3 月 23 日)从 pdf 文档中提取纯文本。希望这会有所帮助。

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from cStringIO import StringIO

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

    fp = file(path, 'rb')

    parser = PDFParser(fp)
    doc = PDFDocument(parser)
    parser.set_document(doc)

    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos=set()

    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages,        password=password,caching=caching, check_extractable=True):
        interpreter.process_page(page)

    text = retstr.getvalue()

    fp.close()
    device.close()
    retstr.close()
    print text
    return text

convert_pdf_to_txt(<path_of_the_pdf_file>)

【讨论】:

【参考方案7】:

以防万一还有人需要这个, 让它与请求和python 3.4一起工作。 感谢@bahmait 在上面的回答:)

import requests

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


def pdf_to_text(url=None):
    text = None
    pdf = requests.get(url)

    if pdf.ok:
        fp = StringIO(str(pdf.content, 'utf-8'))
        outfp = StringIO()

        rsrcmgr = PDFResourceManager()
        device = TextConverter(rsrcmgr, outfp, laparams=LAParams())
        process_pdf(rsrcmgr, device, fp)
        device.close()

        text = outfp.getvalue()
        outfp.close()
        fp.close()
    return text


if __name__ == "__main__":
    hello_world_text = pdf_to_text("https://bytebucket.org/hsoft/pdfminer3k/raw/28edfc91caed830674ca0b928f42571f7dee6091/samples/simple1.pdf")
    no_pdf = pdf_to_text('http://www.google.com/404')
    print(hello_world_text)
    print(no_pdf)

【讨论】:

你试过这个人吗? [链接]pypi.python.org/pypi/pdfminer3k ... 看起来不再支持它,但它给了我一个很好的起点。最后,我使用他们的 pdf2txt 模块混合了 pypdf2 中的一些方法用于我的特定用例。【参考方案8】:

这是我最终制作的对我有用的清理版本。以下只是简单地返回 PDF 中的字符串,给定它的文件名。我希望这可以节省一些时间。

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

def convert_pdf(path):

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

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

    str = retstr.getvalue()
    retstr.close()
    return str

此解决方案在API changes in November 2013 之前有效。

【讨论】:

仅供参考,这不再适用于当前版本的 pdfminer - 我收到“ImportError: cannot import name process_pdf” 这里是一个可行的解决方案:***.com/questions/26494211/…(截至 2015 年 11 月中旬) process_pdf 仅被 PDFPageInterpreter 取代 StringIO 也没有了,你可以改用“from io import StringIO”【参考方案9】:

仅当有人仍然需要它时:如何使用 PDFMiner 从 PDF 打印 html

import sys
import getopt
from Core.Interfaces.IReader import IReader
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter, process_pdf
from pdfminer.pdfdevice import PDFDevice, TagExtractor
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.cmapdb import CMapDB
from pdfminer.layout import LAParams
from cStringIO import StringIO

class PdfReader(object):
def __init__(self):
    pass

def readText(self,path, outtype='text', opts=):
    outfile = path[:-3] + outtype
    outdir = '/'.join(path.split('/')[:-1])
    # debug option
    debug = 0
    # input option
    password = ''
    pagenos = set()
    maxpages = 0
    # output option
    # ?outfile = None
    # ?outtype = None
    outdir = None
    #layoutmode = 'normal'
    codec = 'utf-8'
    pageno = 1
    scale = 1
    showpageno = True
    laparams = LAParams()
    for (k, v) in opts:
        if k == '-d': debug += 1
        elif k == '-p': pagenos.update( int(x)-1 for x in v.split(',') )
        elif k == '-m': maxpages = int(v)
        elif k == '-P': password = v
        elif k == '-o': outfile = v
        elif k == '-n': laparams = None
        elif k == '-A': laparams.all_texts = True
        elif k == '-V': laparams.detect_vertical = True
        elif k == '-M': laparams.char_margin = float(v)
        elif k == '-L': laparams.line_margin = float(v)
        elif k == '-W': laparams.word_margin = float(v)
        elif k == '-F': laparams.boxes_flow = float(v)
        elif k == '-Y': layoutmode = v
        elif k == '-O': outdir = v
        elif k == '-t': outtype = v
        elif k == '-c': codec = v
        elif k == '-s': scale = float(v)

    print laparams
    #
    #PDFDocument.debug = debug
    #PDFParser.debug = debug
    CMapDB.debug = debug
    PDFResourceManager.debug = debug
    PDFPageInterpreter.debug = debug
    PDFDevice.debug = debug
    #
    rsrcmgr = PDFResourceManager()

    #outtype = 'text'

    outfp = StringIO()

    device = HTMLConverter(rsrcmgr, outfp, codec=codec, laparams=laparams)


    fp = file(path, 'rb')
    process_pdf(rsrcmgr, device, fp, pagenos, maxpages=maxpages, password=password,
                    check_extractable=True)
    fp.close()
    device.close()
    print outfp.getvalue()
    outfp.close()

    return



reader = PdfReader()
opt = map(None,['-W','-L','-t'],[0.5,0.4,'html'])
reader.readText("/test_data/test.pdf","html",opt)

【讨论】:

【参考方案10】:

以下代码适用于我最新版本的 PDFMiner,它采用 pdf 的路径并以 .txt 格式返回文本。

P.S:这是对上述答案的修改。

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from cStringIO import StringIO

def convert_pdf_to_txt(path, outtype='txt'):
    outfile = path[:-3] + outtype
    rsrcmgr = PDFResourceManager()
    codec = 'utf-8'
    laparams = LAParams()
    if outfile:
        outfp = file(outfile, 'w')
    else:
        outfp = sys.stdout
    device = TextConverter(rsrcmgr, outfp, codec=codec, laparams=laparams)
    fp = file(path, 'rb')
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos=set()
    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages, password=password,caching=caching, check_extractable=True):
        interpreter.process_page(page)
    fp.close()
    device.close()
    outfp.close()
    return

【讨论】:

【参考方案11】:

以下对 non-process_pdf 答案的修改直接从 URL 字符串名称中提取文本,并适用于版本 20140328 和 Python 2.7:

from urllib2 import urlopen
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from cStringIO import StringIO
def convert_pdf_to_txt(url):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)

    scrape = urlopen(url).read()
    fp = StringIO(scrape)

    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos=set()
    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages, password=password,caching=caching, check_extractable=True):
        interpreter.process_page(page)

    fp.close()
    device.close()
    textstr = retstr.getvalue()
    retstr.close()
    return textstr

【讨论】:

【参考方案12】:

这是一个适用于最新版本的新解决方案:

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from cStringIO import StringIO

def convert_pdf_to_txt(path):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    fp = file(path, 'rb')
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos=set()
    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages, password=password,caching=caching, check_extractable=True):
        interpreter.process_page(page)
    fp.close()
    device.close()
    str = retstr.getvalue()
    retstr.close()
    return str

【讨论】:

太棒了!最后是一个不使用process_pdf 的正在运行的解决方案:) 我用official git repository 中的simple1.pdf 尝试了这一点 这适用于版本pdfminer 20131113。谢谢。 @czw。您如何将其与内置选项一起使用?有没有办法指定要转换的页码? 如果你想获得 html 输出,只需将 HTMLConverter 添加到导入和函数中,而不是 TextConverter 放入 HTMLConverter。就这么简单。 只需稍作修改即可:不要使用 str 作为变量名。 str 是一个 python 函数。【参考方案13】:

如果你正在通过 urllib2 处理抓取的数据,试试这个(开发和解释 here):

def pdf_to_text(scraped_pdf_data): 
    from pdfminer.pdfinterp import PDFResourceManager, process_pdf 
    from pdfminer.pdfdevice import PDFDevice 
    from pdfminer.converter import TextConverter 
    from pdfminer.layout import LAParams 

    import StringIO 
    fp = StringIO.StringIO() 
    fp.write(scraped_pdf_data) 
    fp.seek(0) 
    outfp = StringIO.StringIO() 

    rsrcmgr = PDFResourceManager() 
    device = TextConverter(rsrcmgr, outfp, laparams=LAParams()) 
    process_pdf(rsrcmgr, device, fp) 
    device.close() 

    t = outfp.getvalue() 
    outfp.close() 
    fp.close() 
    return t

与其他答案一样,此处的代码适应了 PDFMiner 本身提供的 pdf2txt 实用程序。因此,您也可以转换为 html 或 xml —— 只需将HTMLConverterXMLConverter 转换为上面的TextConverter

【讨论】:

【参考方案14】:

这是我最终制作的对我有用的清理版本。以下只是简单地返回 PDF 中的字符串,给定它的文件名。我希望这可以节省一些时间。

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

def convert_pdf(path):

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

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

    str = retstr.getvalue()
    retstr.close()
    return str

谁能告诉我:有没有特定的地方放置pdf文件??

【讨论】:

【参考方案15】:

我知道回答你自己的问题很糟糕,但我想我可能已经想通了,我不希望其他人浪费时间寻找解决我问题的方法。

我遵循了我的问题中发布的链接之一中的建议,并重新利用了 pdfminer 中包含的当前 pdf2txt.py 脚本。这是功能,以防它对其他人有用。感谢用户 skyl 发布了这个答案,我所要做的就是进行一些更改以使其与当前版本的 pdfminer 一起工作。

这个函数获取一个pdf并在同一个目录中创建一个同名的.txt文件。

def convert_pdf(path, outtype='txt', opts=):
import sys
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter, process_pdf
from pdfminer.pdfdevice import PDFDevice, TagExtractor
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.cmapdb import CMapDB
from pdfminer.layout import LAParams
import getopt

outfile = path[:-3] + outtype
outdir = '/'.join(path.split('/')[:-1])

# debug option
debug = 0
# input option
password = ''
pagenos = set()
maxpages = 0
# output option
# ?outfile = None
# ?outtype = None
outdir = None
#layoutmode = 'normal'
codec = 'utf-8'
pageno = 1
scale = 1
showpageno = True
laparams = LAParams()
for (k, v) in opts:
    if k == '-d': debug += 1
    elif k == '-p': pagenos.update( int(x)-1 for x in v.split(',') )
    elif k == '-m': maxpages = int(v)
    elif k == '-P': password = v
    elif k == '-o': outfile = v
    elif k == '-n': laparams = None
    elif k == '-A': laparams.all_texts = True
    elif k == '-V': laparams.detect_vertical = True
    elif k == '-M': laparams.char_margin = float(v)
    elif k == '-L': laparams.line_margin = float(v)
    elif k == '-W': laparams.word_margin = float(v)
    elif k == '-F': laparams.boxes_flow = float(v)
    elif k == '-Y': layoutmode = v
    elif k == '-O': outdir = v
    elif k == '-t': outtype = v
    elif k == '-c': codec = v
    elif k == '-s': scale = float(v)
#
#PDFDocument.debug = debug
#PDFParser.debug = debug
CMapDB.debug = debug
PDFResourceManager.debug = debug
PDFPageInterpreter.debug = debug
PDFDevice.debug = debug
#
rsrcmgr = PDFResourceManager()

outtype = 'text'

if outfile:
    outfp = file(outfile, 'w')
else:
    outfp = sys.stdout
device = TextConverter(rsrcmgr, outfp, codec=codec, laparams=laparams)


fp = file(path, 'rb')
process_pdf(rsrcmgr, device, fp, pagenos, maxpages=maxpages, password=password,
                check_extractable=True)
fp.close()
device.close()
outfp.close()
return

【讨论】:

自己的问题可以自己回答,不用担心。 我还重新调整了该代码的用途。它可能会更苗条 由于您提到的原因,回答您自己的问题非常可取

以上是关于如何使用 pdfminer 作为库的主要内容,如果未能解决你的问题,请参考以下文章

React.js + Summernote,如何导入 JavaScript 依赖库

如何在Webstorm中添加js库 (青瓷H5游戏引擎)

django 使用第三方js库“My97DatePicker”作为时间选择插件

django 使用第三方js库“My97DatePicker”作为时间选择插件

如何在 django 模板中使用 vue 库

干货 | Popmotion: 函数式的 JS 动作库;应用程序的运行状况检查;如何正确使用D3属性;提升设计水平的3大法宝