Python操作PDF实例:提取超链接、下载、修改超链接

@高效码农  January 14, 2025

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)


添加新评论