目前我正在考虑使用pyPdf进行一些PDF合并,但有时输入的顺序不正确,所以我正在考虑抓每页的页码以确定它应该进入的顺序(例如,如果有人将一本书分成20页10页PDF,我想将它们重新组合在一起)。
我有两个问题 - 1.)我知道有时页码会存储在某个地方的文档数据中,因为我已经看到在Adobe上呈现的PDF类似于[1243](10 of 150),但我我已经将这类文件读入pyPDF,我找不到任何指示页码的信息 - 这是存储在哪里的?
2。)如果avenue#1不可用,我想我可以遍历给定页面上的对象以尝试查找页码 - 可能是它自己的对象中有一个数字。但是,我似乎无法找到任何明确的方法来确定对象的内容。如果我跑:
pdf.getPage(0).getContents()
这通常会返回:
{'/Filter': '/FlateDecode'}
或它返回IndirectObject(num,num)对象的列表。我真的不知道如何处理其中任何一个,据我所知,它没有真正的文档。熟悉这种事情的人是否能指出我正确的方向?
答案 0 :(得分:26)
以下对我有用:
from PyPDF2 import PdfFileReader
pdf = PdfFileReader(open('path/to/file.pdf','rb'))
pdf.getNumPages()
答案 1 :(得分:6)
有关完整文档,请参阅Adobe的978页PDF Reference。 : - )
更具体地说,PDF文件包含指示PDF的物理页面如何映射到逻辑页码以及如何格式化页码的元数据。这是您获取规范结果的地方。示例2 of this page显示了这在PDF标记中的显示方式。你必须把它解决,解析它,然后自己进行映射。
在PyPDF中,要获取此信息,请尝试作为起点:
pdf.trailer["/Root"]["/PageLabels"]["/Nums"]
顺便提一下,当您看到IndirectObject
实例时,可以调用其getObject()
方法来检索指向的实际对象。
正如您所说,您的选择是检查文本对象并尝试确定哪个是页码。您可以使用页面对象的extractText()
,但是您将获得一个字符串,并且必须尝试从中删除页码。 (当然,页码可能是罗马字母或字母而不是数字,有些页面可能没有编号。)相反,看看extractText()
实际上如何完成其工作 - 毕竟PyPDF是用Python编写的,毕竟 - 并将其用作例程的基础,该例程单独检查页面上的每个文本对象以查看它是否像页码一样。警惕那些有很多页码的TOC /索引页面!
答案 2 :(得分:4)
善意的回答非常好。但是,由于稍后(由梦想家)请求工作代码示例,并且因为我今天遇到了同样的问题,所以我想添加一些注释。
pdf结构不统一;您可以依赖的东西相当少,因此任何工作代码示例都不太可能适用于所有人。可以找到一个非常好的解释in this answer。
正如所解释的那样,您很可能需要探索您正在处理的PDF格式。
像这样:
import sys
import PyPDF2 as pyPdf
"""Open your pdf"""
pdf = pyPdf.PdfFileReader(open(sys.argv[1], "rb"))
"""Explore the /PageLabels (if it exists)"""
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]
print(page_label_type)
except:
print("No /PageLabel object")
"""Select the item that is most likely to contain the information you desire; e.g.
{'/Nums': [0, IndirectObject(42, 0)]}
here, we only have "/Num". """
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"]
print(page_label_type)
except:
print("No /PageLabel object")
"""If you see a list, like
[0, IndirectObject(42, 0)]
get the correct item from it"""
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1]
print(page_label_type)
except:
print("No /PageLabel object")
"""If you then have an indirect object, like
IndirectObject(42, 0)
use getObject()"""
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()
print(page_label_type)
except:
print("No /PageLabel object")
"""Now we have e.g.
{'/S': '/r', '/St': 21}
meaning roman numerals, starting with page 21, i.e. xxi. We can now also obtain the two variables directly."""
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/S"]
print(page_label_type)
start_page = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/St"]
print(start_page)
except:
print("No /PageLabel object")
脚本:
import sys
import PyPDF2 as pyPdf
def arabic_to_roman(arabic):
roman = ''
while arabic >= 1000:
roman += 'm'
arabic -= 1000
diffs = [900, 500, 400, 300, 200, 100, 90, 50, 40, 30, 20, 10, 9, 5, 4, 3, 2, 1]
digits = ['cm', 'd', 'cd', 'ccc', 'cc', 'c', 'xc', 'l', 'xl', 'xxx', 'xx', 'x', 'ix', 'v', 'iv', 'iii', 'ii', 'i']
for i in range(len(diffs)):
if arabic >= diffs[i]:
roman += digits[i]
arabic -= diffs[i]
return(roman)
def get_page_labels(pdf):
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/S"]
except:
page_label_type = "/D"
try:
page_start = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/St"]
except:
page_start = 1
page_count = pdf.getNumPages()
##or, if you feel fancy, do:
#page_count = pdf.trailer["/Root"]["/Pages"]["/Count"]
page_stop = page_start + page_count
if page_label_type == "/D":
page_numbers = list(range(page_start, page_stop))
for i in range(len(page_numbers)):
page_numbers[i] = str(page_numbers[i])
elif page_label_type == '/r':
page_numbers_arabic = range(page_start, page_stop)
page_numbers = []
for i in range(len(page_numbers_arabic)):
page_numbers.append(arabic_to_roman(page_numbers_arabic[i]))
print(page_label_type)
print(page_start)
print(page_count)
print(page_numbers)
pdf = pyPdf.PdfFileReader(open(sys.argv[1], "rb"))
get_page_labels(pdf)
答案 3 :(得分:4)
其他答案使用PyPDF / PyPDF2,它似乎读取整个文件。这对于大文件来说需要很长时间。
与此同时,我写了一些快速而又肮脏的东西,并不需要花费太长的时间。它执行shell调用,但我不知道有任何其他方法可以执行此操作。它可以非常快速地获得大约5000页的pdf页面。
它只需调用“pdfinfo”shell命令即可,所以它可能只适用于linux。到目前为止我只在ubuntu上测试过它。
我见过的一个奇怪的行为是,在try / except块中围绕这个行为不能捕获错误,除了subprocess.CalledProcessError之外你必须这样做。
from subprocess import check_output
def get_num_pages(pdf_path):
output = check_output(["pdfinfo", pdf_path]).decode()
pages_line = [line for line in output.splitlines() if "Pages:" in line][0]
num_pages = int(pages_line.split(":")[1])
return num_pages
答案 4 :(得分:0)
另一个选择是pymupdf: https://pymupdf.readthedocs.io/en/latest/tutorial.html
import fitz
doc = fitz.open('Path To File')
doc.pageCount
pip install pymupdf
对于大型文档,使用pypdf2时出现递归错误,因此这是另一种快速简便的方法。