Python操作PDF实例:提取超链接、下载、修改超链接
1、提取 PDF 中的超链接
该函数从PDF文件中提取所有链接及其对应的文字:
1.打开PDF文件并遍历每一页。
2.获取每页中的链接信息,检查是否包含URI。
3.提取链接对应的文本,若无则标记为“Unnamed”。
4.将链接的文本、URI、所在页码和矩形位置存入字典,并添加到结果列表中。
5.返回包含所有链接信息的列表。
def extract_links_from_pdf(pdf_path):
"""从PDF文件中提取所有链接及其对应的文字"""
links = []
doc = fitz.open(pdf_path)
for page_num in range(len(doc)):
page = doc[page_num]
for link in page.get_links():
if "uri" in link:
link_text = (
page.get_text("blocks", clip=link["from"])[0][4].strip()
if link["from"] else "Unnamed"
)
links.append({
"text": link_text,
"uri": link["uri"],
"page_num": page_num,
"rect": link["from"]
})
return link
2、下载文件并获取链接映射
该函数根据链接列表下载文件并保存到指定目录,主要功能如下:
1.创建输出目录:如果目录不存在则创建。
2.遍历链接列表:
- 提取链接文字和URL。
- 生成合法的文件名,并检查文件是否已存在,若存在则跳过下载。
3.下载文件:
- 使用requests.get下载文件,分块写入本地。
- 处理下载错误并打印提示信息。
4.返回已下载文件的字典:键为URL,值为文件路径。
def download_files(links, output_dir):
"""根据链接下载文件并按照链接文字命名,如果已下载则跳过"""
os.makedirs(output_dir, exist_ok=True)
downloaded_files = {}
for link in links:
link_text = link["text"]
url = link["uri"]
# 确保文件名合法
sanitized_filename = "".join(c if c.isalnum() or c in "._-" else "_" for c in link_text)
file_name = f"{sanitized_filename}.pdf"
file_path = os.path.join(output_dir, file_name)
# 检查文件是否已存在
if os.path.exists(file_path):
print(f"文件已存在,跳过下载: {file_name}")
downloaded_files[url] = file_path
continue
try:
response = requests.get(url, proxies=proxies, stream=True)
if response.status_code == 200:
with open(file_path, 'wb') as file:
for chunk in response.iter_content(chunk_size=1024):
file.write(chunk)
print(f"下载成功: {file_name}")
downloaded_files[url] = file_path
else:
print(f"无法下载链接: {url}")
except Exception as e:
print(f"下载链接 {url} 时出错: {e}")
return downloaded_files
3、替换 PDF 中的超链接为本地链接
该函数用于将PDF文件中的超链接替换为本地链接,具体功能如下:
1.打开指定路径的PDF文件。
2.遍历每一页,获取页面中的所有链接。
3.检查每个链接是否包含"uri"键,并判断其值是否在link_map字典中。
4.如果匹配成功,则用link_map中对应的值替换原链接。
5.更新页面中的链接信息。
6.保存修改后的PDF到指定输出路径并打印确认信息。
def replace_links_in_pdf(pdf_path, output_pdf_path, link_map):
"""将PDF中的超链接替换为本地链接"""
doc = fitz.open(pdf_path)
for page_num in range(len(doc)):
page = doc[page_num]
links = page.get_links()
for link in links:
if "uri" in link:
original_url = link["uri"]
if original_url in link_map:
# 替换为本地链接
new_link = link_map[original_url]
link["uri"] = new_link
# 更新页面中的链接
page.update_link(link)
# 保存修改后的PDF
doc.save(output_pdf_path)
print(f"已保存修改后的PDF: {output_pdf_path}")
完整代码
import fitz # PyMuPDF库,用于解析PDF
import requests
import os
def extract_links_from_pdf(pdf_path):
"""从PDF文件中提取所有链接及其对应的文字"""
links = []
doc = fitz.open(pdf_path)
for page_num in range(len(doc)):
page = doc[page_num]
for link in page.get_links():
if "uri" in link:
link_text = (
page.get_text("blocks", clip=link["from"])[0][4].strip()
if link["from"] else "Unnamed"
)
links.append({
"text": link_text,
"uri": link["uri"],
"page_num": page_num,
"rect": link["from"]
})
return links
def download_files(links, output_dir):
"""根据链接下载文件并按照链接文字命名,如果已下载则跳过"""
os.makedirs(output_dir, exist_ok=True)
downloaded_files = {}
for link in links:
link_text = link["text"]
url = link["uri"]
# 确保文件名合法
sanitized_filename = "".join(c if c.isalnum() or c in "._-" else "_" for c in link_text)
file_name = f"{sanitized_filename}.pdf"
file_path = os.path.join(output_dir, file_name)
# 检查文件是否已存在
if os.path.exists(file_path):
print(f"文件已存在,跳过下载: {file_name}")
downloaded_files[url] = file_path
continue
try:
response = requests.get(url, proxies=proxies, stream=True)
if response.status_code == 200:
with open(file_path, 'wb') as file:
for chunk in response.iter_content(chunk_size=1024):
file.write(chunk)
print(f"下载成功: {file_name}")
downloaded_files[url] = file_path
else:
print(f"无法下载链接: {url}")
except Exception as e:
print(f"下载链接 {url} 时出错: {e}")
return downloaded_files
def replace_links_in_pdf(pdf_path, output_pdf_path, link_map):
"""将PDF中的超链接替换为本地链接"""
doc = fitz.open(pdf_path)
for page_num in range(len(doc)):
page = doc[page_num]
links = page.get_links()
for link in links:
if "uri" in link:
original_url = link["uri"]
if original_url in link_map:
# 替换为本地链接
new_link = link_map[original_url]
link["uri"] = new_link
# 更新页面中的链接
page.update_link(link)
# 保存修改后的PDF
doc.save(output_pdf_path)
print(f"已保存修改后的PDF: {output_pdf_path}")
if __name__ == "__main__":
pdf_path = "WitnessLeeBooksIndex1.pdf" # 输入 PDF 文件路径
output_pdf_path = "WitnessLeeBooksIndex1_modified.pdf" # 修改后的输出 PDF 文件路径
output_dir = "./downloads_new/" # 下载文件的保存路径
# 提取 PDF 中的超链接
links = extract_links_from_pdf(pdf_path)
# 下载文件并获取链接映射
link_map = download_files(links, output_dir)
# 替换 PDF 中的超链接为本地链接
replace_links_in_pdf(pdf_path, output_pdf_path, link_map)