27

目前,我正在考虑将一些 PDF 与 pyPdf 合并,但有时输入的顺序不正确,因此我正在考虑抓取每个页面的页码以确定它应该进入的顺序(例如,如果有人将一本书分成 20 个 10 页的 PDF,我想将它们重新组合在一起)。

我有两个问题 - 1.) 我知道有时页码存储在文档数据中的某处,因为我看到在 Adob​​e 上呈现的 PDF 类似于 [1243](150 中的 10),但我读过此类文档转换为 pyPDF,但我找不到任何指示页码的信息 - 它存储在哪里?

2.) 如果第 1 大道不可用,我想我可以遍历给定页面上的对象以尝试找到页码 - 可能它是它自己的对象,其中有一个数字。但是,我似乎找不到任何明确的方法来确定对象的内容。如果我运行:

pdf.getPage(0).getContents()

这通常会返回:

{'/Filter': '/FlateDecode'}

或者它返回 IndirectObject(num, num) 个对象的列表。我真的不知道如何处理其中任何一个,据我所知,也没有关于它的真正文档。有没有人熟悉这种可以为我指明正确方向的事情?

4

6 回答 6

79

以下对我有用:

from PyPDF2 import PdfFileReader
pdf = PdfFileReader(open('path/to/file.pdf','rb'))
pdf.getNumPages()
于 2013-07-29T18:14:23.913 回答
13

其他答案使用 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
于 2017-11-07T23:55:02.767 回答
9

有关完整文档,请参阅 Adob​​e 的 978 页PDF 参考。:-)

更具体地说,PDF 文件包含指示 PDF 的物理页面如何映射到逻辑页码以及页码应该如何格式化的元数据。这是您获取规范结果的地方。本页的示例 2显示了它在 PDF 标记中的外观。你必须把它找出来,解析它,然后自己执行映射。

在 PyPDF 中,要获取此信息,请尝试作为起点:

pdf.trailer["/Root"]["/PageLabels"]["/Nums"]

顺便说一句,当你看到一个IndirectObject实例时,你可以调用它的getObject()方法来检索实际指向的对象。

正如您所说,您的替代方法是检查文本对象并尝试找出哪个是页码。您可以extractText()为此使用页面对象,但您会得到一个字符串,并且必须尝试从中找出页码。(当然,页码可能是罗马字母或字母而不是数字,有些页面可能没有编号。)相反,看看extractText()它的实际工作是如何的——毕竟 PyPDF 是用 Python 编写的——并将其用作一个例程的基础,它分别检查页面上的每个文本对象以查看它是否像页码。警惕上面有很多页码的目录/索引页面!

于 2012-09-11T15:36:37.783 回答
5

kindall的回答非常好。但是,由于后来(由梦想家)请求了一个工作代码示例,并且由于我今天遇到了同样的问题,所以我想添加一些注释。

  1. pdf结构不统一;您可以依赖的东西很少,因此任何工作代码示例都不太可能适用于所有人。在这个答案中可以找到一个很好的解释。

  2. 正如 kindall 所解释的,您很可能需要探索您正在处理的 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")
  1. 正如您从 ISO pdf 1.7 规范(此处的相关部分)中看到的那样,如何标记页面有很多可能性。作为一个简单的工作示例,请考虑这个脚本,它至少可以处理十进制(阿拉伯)和罗马数字:

脚本:

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)
于 2016-03-01T17:04:03.770 回答
2

另一个选项是 pymupdf: https ://pymupdf.readthedocs.io/en/latest/tutorial.html

import fitz

doc = fitz.open('Path To File')
doc.pageCount

pip install pymupdf

对于大型文档,我在使用 pypdf2 时遇到递归错误,所以这是另一种快速简单的方法。

于 2020-09-08T23:15:48.100 回答
1

使用 Python 从 doc 获取页码

PyMuPDF

import fitz
doc = fitz.open('source_path')
print(doc.pageCount)
# prints total page count of input PDF

PyPDF2

import PyPDF2
pdfFileObj = open('source.pdf', 'rb')
pdfReader = PyPDF2.PdfFileReader(pdfFileObj)
print(pdfReader.numPages)
#get totalnumber of pages and page numbering in PyPDF2 starts with 0
pageObj = pdfReader.getPage(0)
pageObj.extractText()
pdfFileObj.close()

PDFinfo pdfinfo : 提取 PDF 文件中 Info 字典的内容。Xpdf 项目的另一部分。

pdfinfo filename.pdf
**Output**
Title:          HILs.pdf
Subject:
Keywords:
Author:        
Creator:        Acrobat PDFMaker 10.0 for Word
Producer:       Acrobat Distiller 9.3.0 (Windows)
CreationDate:   Mon Jun  2 11:16:53 2014
ModDate:        Mon Jun  2 11:16:53 2014
Tagged:         no
Pages:          3
Encrypted:      no
Page size:      612 x 792 pts (letter)
File size:      39177 bytes
Optimized:      yes
PDF version:    1.5

PDFminer

from pdfminer.pdfpage import PDFPage
infile = file(fname, 'rb')
print(PDFPage.pagenums(infile))
于 2021-11-24T08:38:56.613 回答