Python - 按页拆分 pdf
Python - Split pdf by pages
我正在使用 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 秒。有什么办法可以改进这段代码吗?有没有其他图书馆更适合这个工作?
重构
我已经重构代码如下:
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文件进行profiling,分析结果…
分析结果
分析给了我这个结果:
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 页。
确实优化不佳:-(
任何优化都无法真正带来真正的改善。我最终使用了 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
我正在使用 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 秒。有什么办法可以改进这段代码吗?有没有其他图书馆更适合这个工作?
重构
我已经重构代码如下:
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文件进行profiling,分析结果…
分析结果
分析给了我这个结果:
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 页。
确实优化不佳:-(
任何优化都无法真正带来真正的改善。我最终使用了 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