【问题标题】:Python - Split pdf by pagesPython - 按页面拆分 pdf
【发布时间】:2017-02-13 01:32:46
【问题描述】:

我正在使用 PyPdf2 将大的 PDF 拆分为页面。问题是这个过程很慢。

这是我使用的代码:

import os
from PyPDF2 import PdfFileWriter, PdfFileReader

with open(input_pdf_path, "rb") as input_file:
    input_pdf = PdfFileReader(input_file)
    directory = "%s/paging/" % os.path.dirname(input_pdf_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

    page_files = []
    for i in range(0, input_pdf.numPages):
        output = PdfFileWriter()
        output.addPage(input_pdf.getPage(i))
        file_name = "%s/#*#*#*##-%s.pdf" % (directory, i)
        page_files.append(file_name)
        with open(file_name, "wb") as outputStream:
            output.write(outputStream)

使用此代码拆分 177 页的 pdf 大约需要 35 到 55 秒。有没有办法改进这段代码?还有其他更适合这个工作的库吗?

【问题讨论】:

  • PyPdf2 是一个纯 Python 库,所以……在此处查看替代解决方案:binpress.com/tutorial/manipulating-pdfs-with-python/167
  • Dany,请注意:这个文件名模式很奇怪“%s/#*#*#*##-%s.pdf”,在 Windows 下无效。
  • 很好奇,因为我在 40 秒内拆分了 2135 个页面……你的操作系统是什么,PyDF2 版本,Python 版本?
  • 我正在使用 Ubuntu。我将在下一步使用此模式,并且我正在使用正则表达式,因此它必须是唯一的。
  • 关于模式,为什么不使用类似:file_name = os.path.join(directory, u"{page:04d}.pdf".format(page=i))?

标签: python python-3.x pdf pypdf pypdf2


【解决方案1】:

重构

我已经像这样重构了代码:

import os

import PyPDF2


def split_pdf_pages(input_pdf_path, target_dir, fname_fmt=u"{num_page:04d}.pdf"):
    if not os.path.exists(target_dir):
        os.makedirs(target_dir)

    with open(input_pdf_path, "rb") as input_stream:
        input_pdf = PyPDF2.PdfFileReader(input_stream)

        if input_pdf.flattenedPages is None:
            # flatten the file using getNumPages()
            input_pdf.getNumPages()  # or call input_pdf._flatten()

        for num_page, page in enumerate(input_pdf.flattenedPages):
            output = PyPDF2.PdfFileWriter()
            output.addPage(page)

            file_name = os.path.join(target_dir, fname_fmt.format(num_page=num_page))
            with open(file_name, "wb") as output_stream:
                output.write(output_stream)

注意:很难做得更好……

分析

使用这个split_pdf_pages 函数,您可以进行分析:

import cProfile
import pstats
import io

pdf_path = "path/to/file.pdf"
directory = os.path.join(os.path.dirname(pdf_path), "pages")

pr = cProfile.Profile()
pr.enable()
split_pdf_pages(pdf_path, directory)
pr.disable()

s = io.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats('cumulative')
ps.print_stats()
print(s.getvalue())

使用您自己的 PDF 文件运行分析,并分析结果……

分析结果

分析给了我这个结果:

         159696614 function calls (155047949 primitive calls) in 57.818 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.899    0.899   57.818   57.818 $HOME/workspace/pypdf2_demo/src/pypdf2_demo/split_pdf_pages.py:14(split_pdf_pages)
     2136    0.501    -.---   53.851    0.025 $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/pdf.py:445(write)
103229/96616    1.113    -.---   36.924    -.--- $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/generic.py:544(writeToStream)
    27803    9.066    -.---   25.381    0.001 $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/generic.py:445(writeToStream)
4185807/2136    5.054    -.---   14.635    0.007 $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/pdf.py:541(_sweepIndirectReferences)
50245/41562    0.117    -.---    9.028    -.--- $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/pdf.py:1584(getObject)
 31421489    6.898    -.---    8.193    -.--- $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/utils.py:231(b_)
    56779    2.070    -.---    7.882    -.--- $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/generic.py:142(writeToStream)
     8683    0.322    -.---    7.020    0.001 $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/pdf.py:1531(_getObjectFromStream)
459978/20068    1.098    -.---    6.490    -.--- $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/generic.py:54(readObject)
26517/19902    0.484    -.---    6.360    -.--- $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/generic.py:553(readFromStream)
    27803    3.893    -.---    5.565    -.--- $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/generic.py:1162(encode_pdfdocencoding)
 15735379    4.173    -.---    5.412    -.--- $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/utils.py:268(chr_)
  3617738    2.105    -.---    4.956    -.--- $HOME/virtualenv/py3-pypdf2_demo/lib/site-packages/PyPDF2/generic.py:265(writeToStream)
 18882076    3.856    -.---    3.856    -.--- {method 'write' of '_io.BufferedWriter' objects}

看来:

  • writeToStream 函数被大量调用,但我不知道如何优化它。
  • write 方法直接写入流,不在内存中 => 可以进行优化。

改进

在缓冲区(内存中)中序列化 PDF 页面,然后将缓冲区写入文件:

buffer = io.BytesIO()
output.write(buffer)
with open(file_name, "wb") as output_stream:
    output_stream.write(buffer.getvalue())

我在 35 秒而不是 40 秒内处理了 2135 个页面。

确实优化很差:-(

【讨论】:

  • 库本身可以优化:在PdfObject中使用__slots__(参见:PyPDF2\generic.py)。我们优化了 10% 的执行时间(也许更多的内存)。
  • 感谢您的回答!我试过这个,但它只会让速度快一点。最终使用 pdftk,我将在我的回答中解释。
【解决方案2】:

任何优化都无法真正带来真正的改进。我最终使用了pdftk。我遇到了这个page,它很好地解释了如何快速拆分页面。

pdftk 是一个命令行工具(和图形工具),有一些非常好的选项。

安装:

 sudo apt-get update
 sudo apt-get install pdftk

与python3一起使用:

 process = Popen(['pdftk',
                     input_pdf_path,
                     'burst',
                     'output',
                     PdfSplitter.FILE_FORMAT + '%d.pdf'],
                     stdout=PIPE,
                     stderr=PIPE)
 stdout, stderr = process.communicate()

使用此工具,我设法在 2 秒内拆分了 177 页 pdf。

【讨论】:

  • 如何指定输出路径?
猜你喜欢
  • 1970-01-01
  • 2012-01-26
  • 1970-01-01
  • 2020-03-04
  • 1970-01-01
  • 1970-01-01
  • 2016-08-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多