目录页的生成考虑了标题的长度,对过长的标题进行适当的分行处理,确保目录的整洁性。页码的添加在页面底部中央,通过绘制白色矩形覆盖原有页码区域后添加新的页码信息,以避免页码重叠。最终,所有页面(包括目录页和带有新页码的原始页面)被合并成一个PDF文件。
技术亮点
- 文本提取与处理:通过
PyMuPDF
和PyPDF2
库提取PDF文件的文本和元数据,使用正则表达式和文本处理技术清洗和格式化标题。 - 动态内容生成:使用
reportlab
库动态生成包含自定义文本(如页码和目录项)的PDF页面。 - 文档合并与修改:利用
PyPDF2
库合并PDF页面,并在合并过程中添加自定义内容。
通过这个Python项目,我们可以自动化处理一系列复杂的PDF文档管理任务,包括提取标题、生成目录、添加页码和合并文件。这大大减轻了手动处理的负担,使得管理大量PDF文档变得既简单又高效。无论是学术研究者、图书管理员还是文档管理专业人士,都可以从这个项目中受益。
代码
步骤一:提取PDF标题(Step_two.ipynb)
### 第一步:读取pdf\_dir路径下所有.pdf为后缀的文件,打开CSV文件以写入文件名和标题
### 第二步:手动对CSV文件内错误标题进行修改
# 读取路径下所有.pdf为后缀的文件
pdf_dir = '老师的论文集/'
# 合并后的PDF名字
output_pdf_path = "合并后/老师的论文集.pdf"
# 用于中间 存放文件名与标题的CSV文件
TitlesCSV = '合并后/老师的论文集.csv'```
import csv
import html
import os
import re # 导入正则表达式模块
import fitz # PyMuPDF
from PyPDF2 import PdfReader
def find\_non\_text\_chars(sentence):
# 用于检测提取的文本中是否出现非文本类型的,若有则通过类似title = title.replace("fi", "fi")替换
import regex as re
# 定义正则表达式,匹配非文本字符(除了字母、数字、空格和标点符号之外的字符)
non_text_pattern = re.compile(r'[^a-zA-Z0-9\s\p{P}]', re.UNICODE)
# 使用正则表达式搜索句子中的非文本字符
non_text_chars = non_text_pattern.findall(sentence)
# 打印出非文本字符及其类型
for char in non_text_chars:
print(title)
print(f"非文本字符 '{char}' 的类型是 '{type(char)}\n\n'")
return None
def get\_pdf\_title\_1(pdf_path):
"""读取PDF文件的标题,并进行处理。"""
with open(pdf_path, 'rb') as pdf_file:
pdf_reader = PdfReader(pdf_file)
doc_info = pdf_reader.metadata
# 尝试从文档信息中获取标题
paper_title = doc_info.get('/Title', 'untitled') if doc_info else 'untitled'
# 如果标题有效,则进行进一步处理
if paper_title != 'untitled' and paper_title != 'Untitled' and not paper_title.endswith('.pdf'):
# 解码HTML实体
paper_title = html.unescape(paper_title)
# 替换不适合作为文件名的字符
paper_title = re.sub(r'[:/\\\*?"\'<>|]', ' ', paper_title)
else:
# 无效的标题,返回默认值
paper_title = 'untitled'
return paper_title
def get\_pdf\_title\_2(pdf_path):
# 检查文件名是否符合特定模式
filename = os.path.basename(pdf_path)
if filename == "[SCI 】ions for nonlinear dynamical systems.pdf":
return "Estynamical systems"
doc = fitz.open(pdf_path)
first_page = doc[0] # 只查看第一页
# 获取页面上所有文本块,每个块包含文字、字体大小和位置
blocks = first_page.get_text("dict")["blocks"]
# 只考虑页面上半部分的文本块
mid_y = first_page.rect.height / 2
top_blocks = [b for b in blocks if b['type'] == 0 and b['bbox'][3] < mid_y]
# 提取每个文本块的字体大小和文本内容
text_blocks_with_size = []
for block in top_blocks:
if 'lines' in block: # 确保文本块包含行
for line in block['lines']:
if 'spans' in line: # 确保行包含span
for span in line['spans']:
if 'size' in span and len(span['text'].strip()) >= 2: # 检查span中是否有size信息且文本长度符合要求
text_blocks_with_size.append((span['text'], span['size'], span['bbox']))
# 排除特定关键词
excluded_keywords = ["Research Article", "Physica A", "Neurocomputing",
"Sustainable Energy Technologies and Assessments"]
filtered_blocks = [block for block in text_blocks_with_size if
not any(keyword in block[0] for keyword in excluded_keywords)]
# 在过滤后的文本块中基于字体大小和垂直位置来识别可能的标题
if filtered_blocks:
max_font_size = max([size for _, size, _ in filtered_blocks], default=0)
possible_title_blocks = [block for block in filtered_blocks if block[1] == max_font_size]
# 合并具有相同最大字体大小的连续文本块
title_texts = [block[0] for block in possible_title_blocks]
title = " ".join(title_texts) if title_texts else "untitled"
else:
title = "untitled"
doc.close()
title = title.replace("fi", "fi")
title = title.replace("ff", "ff")
# 查找句子中的非文本字符
find_non_text_chars(title)
return title
def get\_pdf\_title(pdf_path):
# 先使用get\_pdf\_title\_1获取标题,若获取失败则使用get\_pdf\_title\_2获取
paper_title = get_pdf_title_1(pdf_path) # 假设这是从PDF提取标题的函数
# 编写一个正则表达式来匹配以连续4个数字和.pdf为后缀的字符串
# 匹配以连续三个数字和.pdf结尾的字符串,或者包含空格和点的字符串,以及不包含空格但包含点的字符串
regex_pattern = r'\d{3}\.pdf$|^[A-Z]+-\w+\s\d+\.\.\d+$|\w+\.\d+\s\d+\.\.\d+$|^[a-zA-Z]+\_\d+\w\*$'
# 判断条件:标题不是'untitled'且不匹配正则表达式(即不是以连续4个数字和.pdf结尾)
if paper_title != 'untitled' and not re.search(regex_pattern, paper_title):
return paper_title
else:
paper_title = get_pdf_title_2(pdf_path)
return paper_title
def get\_titles\_from\_directory(directory_path, specific_file):
titles = []
specific_pdf_path = None # 用于存储特定文件的路径
for root, dirs, files in os.walk(directory_path):
for file in files:
if file.lower().endswith('.pdf'):
pdf_path = os.path.join(root, file)
if file == specific_file: # 如果当前文件是特定文件
specific_pdf_path = pdf_path
else:
try:
title = get_pdf_title(pdf_path)
titles.append((file, title))
except Exception as e:
print(f"Error processing {file}: {e}")
# 处理特定文件
if specific_pdf_path:
try:
title = get_pdf_title(specific_pdf_path)
titles.insert(0, (specific_file, title)) # 将特定文件的标题插入到列表的最前面
except Exception as e:
print(f"Error processing {specific\_file}: {e}")
return titles
specific_file = "lic health.pdf"
# 替换为你的PDF文件所在的目录路径
directory_path = pdf_dir
titles = get_titles_from_directory(directory_path, specific_file)
with open(TitlesCSV, 'w', newline='', encoding='utf-8') as csv_file:
csv_writer = csv.writer(csv_file, delimiter=',')
csv_writer.writerow(['Files', 'Title']) # 写入头部信息
for file, title in titles:
# 写入文件名和标题
csv_writer.writerow([file, title])
步骤二:生成目录和页码,合并PDF(Step_two.ipynb)
### 第三步:读取 Step\_one.ipynb获取的标题的CSV文件
### 第四步:根据文件名字 标题 合并PDF 并生成目录与页码
# 读取路径下所有.pdf为后缀的文件
pdf_dir = '老师的论文集/'
# 合并后的PDF名字
output_pdf_path = "合并后/老师的论文集.pdf"
# 用于中间 存放文件名与标题的CSV文件
TitlesCSV = '合并后/老师的论文集.csv'
import csv
import io
import os
from PyPDF2 import PdfReader, PdfWriter
from reportlab.lib.pagesizes import letter
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.pdfgen import canvas
def create\_footer\_page(footer_text):
packet = io.BytesIO()
c = canvas.Canvas(packet, pagesize=letter)
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
**因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/56f6353e85b788623127447c4ab479ce.png)
![img](https://img-blog.csdnimg.cn/img_convert/183d900448f7c7f8803a5a0b4534fe51.png)
![](https://img-blog.csdnimg.cn/img_convert/46506ae54be168b93cf63939786134ca.png)
![](https://img-blog.csdnimg.cn/img_convert/252731a671c1fb70aad5355a2c5eeff0.png)
![](https://img-blog.csdnimg.cn/img_convert/6c361282296f86381401c05e862fe4e9.png)
![](https://img-blog.csdnimg.cn/img_convert/9f49b566129f47b8a67243c1008edf79.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Python开发知识点,真正体系化!**
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
**如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注Python)**
![img](https://img-blog.csdnimg.cn/img_convert/75627adc4403de5312252cbc032b3759.png)
[**一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!**](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)
**AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算**
net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)
**AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算**