读取 PDF 文档中的所有书签,并使用书签的页码和标题创建字典

mdo*_*wes 2 pypdf python-3.x

我尝试使用 Python 和 PyPDF2 包来阅读 PDF 文档。目标是读取pdf中的所有书签,并构建一个以书签页码为键、书签标题为值的字典。

除了这篇文章之外,互联网上没有太多关于如何实现它的支持。其中发布的代码不起作用,我不是 python 专家来纠正它。PyPDF2的阅读器对象有一个名为outlines的属性,它为您提供所有书签对象的列表,但书签没有页码,并且遍历该列表并不困难,因为书签之间没有父/子关系。

我在下面分享我的代码来阅读 pdf 文档并检查轮廓属性。

import PyPDF2

reader = PyPDF2.PdfFileReader('SomeDocument.pdf')

print(reader.numPages)
print(reader.outlines[1][1])
Run Code Online (Sandbox Code Playgroud)

mpo*_*tes 9

通过使列表彼此嵌套来保留父/子关系。此示例代码将以缩进的目录形式递归显示书签:

import PyPDF2


def show_tree(bookmark_list, indent=0):
    for item in bookmark_list:
        if isinstance(item, list):
            # recursive call with increased indentation
            show_tree(item, indent + 4)
        else:
            print(" " * indent + item.title)


reader = PyPDF2.PdfFileReader("[your filename]")

show_tree(reader.getOutlines())
Run Code Online (Sandbox Code Playgroud)

我不知道如何检索页码。我尝试了几个文件,对象page的属性Destination始终是 的实例IndirectObject,它似乎不包含有关页码的任何信息。

更新:

有一个getDestinationPageNumber方法可以从对象中获取页码Destination。修改代码以创建您想要的字典:

import PyPDF2


def bookmark_dict(bookmark_list):
    result = {}
    for item in bookmark_list:
        if isinstance(item, list):
            # recursive call
            result.update(bookmark_dict(item))
        else:
            result[reader.getDestinationPageNumber(item)] = item.title
    return result


reader = PyPDF2.PdfFileReader("[your filename]")

print(bookmark_dict(reader.getOutlines()))
Run Code Online (Sandbox Code Playgroud)

但是,请注意,如果同一页面上有多个书签(字典键必须是唯一的),您将覆盖并丢失一些值。

  • 请注意,大多数方法“现在”已被弃用并已被重命名,但错误回溯告诉如何纠正,请参阅 [`outline`](https://pypdf2.readthedocs.io/en/3.0.0/modules) 的前文档/PdfReader.html#PyPDF2.PdfReader.outline) (2认同)

Mar*_*oma 6

编辑:PyPDF2 还没有死!我是新的维护者。

\n

编辑:PyPDF2 移至 pypdf我现在也是该项目的维护者

\n

使用 pypdf

\n

这是mportes 答案的更新/改进版本:

\n
from typing import Dict, Union\n\nfrom pypdf import PdfReader\n\n\ndef bookmark_dict(\n    bookmark_list, reader: PdfReader, use_labels: bool = False,\n) -> Dict[Union[str, int], str]:\n    """\n    Extract all bookmarks as a flat dictionary.\n\n    Args:\n        bookmark_list: The reader.outline or a recursive call\n        use_labels: If true, use page labels. If False, use page indices.\n\n    Returns:\n        A dictionary mapping page labels (or page indices) to their title\n\n    Examples:\n        Download the PDF from https://zenodo.org/record/50395 to give it a try\n    """\n    result = {}\n    for item in bookmark_list:\n        if isinstance(item, list):\n            # recursive call\n            result.update(bookmark_dict(item, reader))\n        else:\n            page_index = reader.get_destination_page_number(item)\n            page_label = reader.page_labels[page_index]\n            if use_labels:\n                result[page_label] = item.title\n            else:\n                result[page_index] = item.title\n    return result\n\n\nif __name__ == "__main__":\n    reader = PdfReader("GeoTopo-A5.pdf")\n    bms = bookmark_dict(reader.outline, reader, use_labels=True)\n\n    for page_nb, title in sorted(bms.items(), key=lambda n: f"{str(n[0]):>5}"):\n        print(f"{page_nb:>3}: {title}")\n
Run Code Online (Sandbox Code Playgroud)\n

我的旧答案

\n

以下是使用 PyMupdf 和类型注释的方法:

\n
from typing import Dict\n\nimport fitz  # pip install pymupdf\n\n\ndef get_bookmarks(filepath: str) -> Dict[int, str]:\n    # WARNING! One page can have multiple bookmarks!\n    bookmarks = {}\n    with fitz.open(filepath) as doc:\n        toc = doc.getToC()  # [[lvl, title, page, \xe2\x80\xa6], \xe2\x80\xa6]\n        for level, title, page in toc:\n            bookmarks[page] = title\n    return bookmarks\n\n\nprint(get_bookmarks("my.pdf"))\n
Run Code Online (Sandbox Code Playgroud)\n