Python 实现给 pdf 文件自动识别标题并增添大纲

news2024/12/27 6:06:48

一、背景:

客户方提供过来一个开放平台的pdf文档,文档里有几十个接口,没有大纲和目录可以定位到具体内容,了解整体的API功能,观看体验极度差劲,所以想使用Python代码自动解析pdf文档,给文档增添大纲内容,便于观看和理解。

二、实现思路:

1、可行性调研
  • pdf文档是文本格式的,而非扫描图像,所以可以拿到具体的文本内容。
  • 内容格式整体比较整齐,标题有特定的格式可以识别。
2、技术细节
  • 使用pyPDF2和pdfplumber类库来实现pdf的解析。
  • 根据章节标题的格式,编写正则表达式进行匹配
  • 记录识别结果到csv文件中,方便比对和删除多余的标题内容。
  • 使用pyPDF2来添加书签,生成新的PDF大纲。

三、代码

1、详细python代码
import csv

import pdfplumber
import re
from PyPDF2 import PdfReader, PdfWriter

# TODO PDF文件路径
pdf_path = 'C:\\Users\\admin\\Desktop\\use_book.pdf'  # 更改为您的PDF文件路径
output_pdf_path = '.\output_use_book_with_bookmarks.pdf'  # 输出文件的路径
# 保存目录信息的CSV文件路径
csv_path = 'titles.csv'


# 检测是否为标题的函数
# 现在只匹配包含至少两个点的数字序列
def is_title(line):
    # TODO 正则表达式匹配类似 "3.8.1.2.3 业务接口" 的格式
    # 确保后面紧跟着的是文字而不是数字或符号
    pattern = r'^\d+(\.\d+)+\s+[^\d\W]+.*'
    return re.match(pattern, line) is not None


# 提取标题的函数
def extract_titles(pdf_path):
    titles = []
    with pdfplumber.open(pdf_path) as pdf:
        for page_number, page in enumerate(pdf.pages):
            text = page.extract_text()
            if text:
                for line in text.split('\n'):
                    if is_title(line):
                        titles.append((line.strip(), page_number))  # PDF页码从0开始
    return titles


# 将标题保存到CSV文件
def save_titles_to_csv(titles, csv_path):
    with open(csv_path, 'w', newline='', encoding='utf-8') as file:
        writer = csv.writer(file)
        writer.writerow(['Title', 'Page Number'])
        for title in titles:
            writer.writerow(title)


# 从CSV文件读取标题
def read_titles_from_csv(csv_path):
    titles = []
    with open(csv_path, 'r', encoding='utf-8') as file:
        reader = csv.reader(file)
        next(reader)  # 跳过标题行
        for row in reader:
            titles.append((row[0], int(row[1]) - 1))  # 将页码转换为从0开始的索引
    return titles


# 检查是否已经有保存的目录信息
try:
    titles = read_titles_from_csv(csv_path)
except FileNotFoundError:
    # 如果没有找到文件,则提取并保存目录信息
    titles = extract_titles(pdf_path)
    save_titles_to_csv(titles, csv_path)

# 打印目录信息
for title in titles:
    print(f"Title: {title[0]}, Page: {title[1]}")


# 添加大纲的函数
def add_bookmarks_to_pdf(input_pdf_path, output_pdf_path, titles):
    reader = PdfReader(input_pdf_path)
    writer = PdfWriter()

    for page in reader.pages:
        writer.add_page(page)

    for title, page_number in titles:
        writer.add_outline_item(title, page_number)

    with open(output_pdf_path, 'wb') as output_file:
        writer.write(output_file)


if __name__ == '__main__':
    # 调用函数
    titles = read_titles_from_csv(csv_path)
    add_bookmarks_to_pdf(pdf_path, output_pdf_path, titles)

2、涉及依赖版本库
python = 3.10 (anaconda3)
pdfplumber = 0.10.3
PyPDF2 = 3.0.1
3、运行效果
  • csv 文件示例 ( title.csv )
Title,Page Number  
2.1 运行硬件环境,5  
2.2 运行软件环境,5  
3.1 门户应用,6  
3.1.1 门户配置,6  
3.1.2 常用应用,6  
3.1.3 快捷导航,7  
3.1.4 下载,9  
3.1.4.1 插件助手,9  
3.1.4.2 客户端,10  
3.1.4.3 插件,10  
3.1.5 用户登录管理,10
  • pdf 示例
    在这里插入图片描述

四、补充

理论上如果标题格式有层级关系,是可以在添加书签的时候,调整每个书签的层级,达到更好的阅读体验。

# 解析标题层级的函数
def parse_title_level(title):
    # 假设标题格式为 "1.1.1 标题"
    level = title.count('.')  # 层级由点的数量决定
    return level

# 添加大纲的函数 (修改版)
def add_bookmarks_to_pdf(input_pdf_path, output_pdf_path, titles):
    reader = PdfReader(input_pdf_path)
    writer = PdfWriter()

    for page in reader.pages:
        writer.add_page(page)

    bookmarks = [None] * 10  # 重点在这段,假设最大层级为10 
    for title, page_number, level in titles:
        parent = bookmarks[level-1] if level > 0 else None  # 确定父书签
        bookmark = writer.add_outline_item(title, page_number, parent)
        bookmarks[level] = bookmark  # 更新当前层级的书签

    with open(output_pdf_path, 'wb') as output_file:
        writer.write(output_file)

以上代码未进行调试,仅做参考 (* ^ ▽ ^ *) 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1352284.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AntDB设计之CheckPoint——引言与功能简述

1.引言 数据库服务能力提升是一项系统性的工程,在不同的应用场景下,用户对于数据库各项能力的关注点也不同,如:读写延迟、吞吐量、扩展性、可靠性、可用性等等。国内不少数据库系统通过系统架构优化、硬件设备升级等方式&#xf…

【单片机 TB作品】节拍器,电子音乐节拍器,51单片机,Proteus仿真

节拍器的使用可以使练琴者正确掌握乐曲的速度,从而使音 乐练习达到事半功倍的效果。本课题基于单片机设计具有声光晋 示的电子乐器节拍器,充分利用单片机的定时和中断系统,通过 C语言程序设计,控制外部相关硬件电路,实现对音乐速,度 40~120次/分钟范围内连续可调,节拍114、 2/4…

Redis命令---Hash(哈希)篇 (超全)

目录 1.Redis Hmset 命令 - 同时将多个 field-value (域-值)对设置到哈希表 key 中。简介语法可用版本: > 2.0.0返回值: 如果命令执行成功,返回 OK 。 示例 2.Redis Hmget 命令 - 获取所有给定字段的值简介语法可用版本: > 2.0.0返回值: 一个包含多个给定字段…

Simple Facebook Sign-In

简单的Facebook登录为Android、iOS、Windows、Mac、通用Windows平台(UWP)和Unity制作的WebGL应用程序提供了基于OAuth 2.0的Facebook登录。 优点: ● 跨平台游戏和应用程序的跨平台用户身份验证 ● 无插件,无第三方库,无依赖● 对建筑规模没有影响 ● 客户端-服务器应…

PMP证书可以挂靠吗?

PMP证书不是国内的证书,挂靠不了呀,想挂靠,可以考软考/一建等,里面也有项目管理相关的证书。 PMP证书虽然不能挂靠,但是用处还是很大的,例如提升个人能力、薪资待遇,还有持证可享一些城市的福利…

kafka容灾演练的方案

背景 kafka可以通过MirrorMaker工具把集群的数据从一个集群同步到另一个集群,通过在另一个数据中心创建灾备集群的方式可以做到容灾的效果,但是如果我们不通过如此重量级的工具也想达到容灾演练的目的,可以怎么做呢 kafka简单容灾实现 当原kafka集群发…

计算机网络--作业

作业一 1、比较电路交换、报文交换和分组报文交换优缺点 电路交换 电路交换是以电路连接为目的的交换方式,通信之前要在通信双方之间建立一条被双方独占的物理通道(由通信双方之间的交换设备和链路逐段连接而成)。 优点: ①由于…

MyBatis学习一:快速入门

前言 公司要求没办法,前端也要了解一下后端知识,这里记录一下自己的学习 学习教程:黑马mybatis教程全套视频教程,2天Mybatis框架从入门到精通 文档: https://mybatis.net.cn/index.html MyBatis 快速入门&#xf…

HackTheBox - Medium - Linux - Bagel

Bagel 今天我开始了《Red Team Development and Operations A Practical Guide》的学习,保持学习,后面差不多到时机后就学CRTOⅡ Bagel 是一款中等难度的 Linux 机器,其特点是电子商店容易受到路径遍历攻击,通过该攻击可以获取应…

Tinker 环境下数据表的用法

如果我们要自己手动创建一个模型文件,最简单的方式是通过 make:model 来创建。 php artisan make:model Article 删除模型文件 rm app/Models/Article.php 创建模型的同时顺便创建数据库迁移 php artisan make:model Article -m Eloquent 表命名约定 在该文件中&am…

k8s中实现pod自动扩缩容

一、k8s应用自动扩缩容概述 1)背景: 在实际的业务场景中,我们经常会遇到某个服务需要扩容的场景(例如:测试对服务压测、电商平台秒杀、大促活动、或由于资源紧张、工作负载降低等都需要对服务实例数进行扩缩容操作&…

SD-WAN组网方式详解

企业网络的演进势不可挡,对于高效、可靠的网络连接需求日益增加。SD-WAN(软件定义广域网)作为一项创新的网络技术,备受企业青睐并得到广泛应用。SD-WAN提供了多种灵活的组网方式,以满足企业多样化的需求和不同的网络环…

AI实景无人直播创业项目:开启自动直播新时代,一部手机即可实现财富增长

在当今社会,直播已经成为了人们日常生活中不可或缺的一部分。无论是商家推广产品、明星互动粉丝还是普通人分享生活,直播已经渗透到了各行各业。然而,传统直播方式存在着一些不足之处,如需现场主持人操作、高昂的费用等。近年来&a…

亚信安慧AntDB数据库引领数字时代:数字驱动创新峰会主旨演讲深度解析

近日,庄严肃穆的数字驱动创新峰会在中国首都北京隆重召开,聚焦于探讨数据经济的创新前沿。在此次盛会中,备受瞩目的亚信安慧AntDB数据库荣幸受邀参与,该数据库的副总裁张桦以其深刻见解和卓越经验发表了引人瞩目的主旨演讲。 图1&…

立体匹配算法(Stereo correspondence)

SGM(Semi-Global Matching)原理: SGM的原理在wiki百科和matlab官网上有比较详细的解释: wiki matlab 如果想完全了解原理还是建议看原论文 paper(我就不看了,懒癌犯了。) 优质论文解读和代码实现 一位大神自己用c实现…

系列九、Feign

一、Feign 1.1、Java中如何实现跨接口调用 (1) Httpclient Httpclient是Apache Jakarta Comon下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议的最新版本和建议。HttpC…

鸿蒙原生应用再添新丁!中国移动 入局鸿蒙

鸿蒙原生应用再添新丁!中国移动 入局鸿蒙 来自 HarmonyOS 微博1月2日消息,#中国移动APP启动鸿蒙原生应用开发#,拥有超3亿用户的中国移动APP宣布,正式基于HarmonyOS NEXT启动#鸿蒙原生应用#及元服务开发。#HarmonyOS#系统的分布式…

openGauss学习笔记-183 openGauss 数据库运维-升级-升级操作

文章目录 openGauss学习笔记-183 openGauss 数据库运维-升级-升级操作183.1 就地升级和灰度升级操作步骤 openGauss学习笔记-183 openGauss 数据库运维-升级-升级操作 介绍就地升级、灰度升级和滚动升级的详细操作。 183.1 就地升级和灰度升级操作步骤 以root身份登录节点。 …

突发!博世「裁员」

对于未来几年的汽车行业需求变化,一级零部件供应商正在加快「降本增效」举措,犹如下游客户更加倾向于降本,而不是无休止的提升整车性能,比如,续航里程、智能化。 本周,全球汽车零部件龙头供应商博世宣布&am…

每日一题 2487. 从链表中移除节点(中等,回溯)

显然只要从后往前遍历链表,设 t 为当前的最大值,只要在遍历过程中比 t 小的节点都删除,大于等于 t 的则更新 t 为新的节点 通过递归回溯的方法可以很简单地实现从后往前遍历链表 # Definition for singly-linked list. # class ListNode: # …