Python与BeautifulSoup:从网站下载PDF并自定义文件名

霞舞
发布: 2025-11-24 12:00:11
原创
711人浏览过

python与beautifulsoup:从网站下载pdf并自定义文件名

本文将详细介绍如何使用Python的requests和BeautifulSoup库,从动态生成的ASP网站上抓取PDF文件链接,并利用HTML链接的显示文本作为下载文件的本地文件名。教程涵盖了HTTP POST请求、HTML解析、URL处理、文件下载与保存等核心步骤,旨在提供一个结构清晰、实用的网页内容自动化下载解决方案。

1. 概述

在进行网页内容抓取时,经常会遇到需要下载特定文件(如PDF、图片等)并以更具描述性的名称保存它们的需求。传统的下载方式可能只提供一个由URL路径生成的默认文件名,这通常不够直观。本教程将聚焦于一个常见场景:从一个通过POST请求动态加载内容的ASP网站上,识别并下载PDF文件,同时将HTML中对应的<a>标签文本作为本地文件名。

我们将使用以下Python库:

  • requests: 用于发送HTTP请求(GET和POST)。
  • BeautifulSoup: 用于解析HTML内容,方便地提取所需元素和属性。
  • os: 用于文件系统操作,如创建目录和构建文件路径。

2. 环境准备与库导入

首先,确保你的Python环境中安装了requests和BeautifulSoup库。如果没有,可以通过pip进行安装:

立即学习Python免费学习笔记(深入)”;

pip install requests beautifulsoup4 lxml
登录后复制

接下来,在你的Python脚本中导入必要的模块:

import os
import requests
from bs4 import BeautifulSoup
from urllib import parse as urlparse # 用于URL处理,尽管在本例中主要用于替换路径分隔符
登录后复制

3. 配置请求参数与输出路径

为了与目标网站进行交互并保存文件,我们需要定义请求的URL、HTTP头部、POST请求体以及本地文件输出路径。

# 目标网站的基础URL
BASE_URL = "https://www.svpo.nl/curriculum.asp"

# 模拟浏览器行为的HTTP头部
HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}

# 定义要抓取的分类和科目(作为POST请求的payload)
klassen = ['1e klas']
vakken = ['Wiskunde'] # 可以扩展此列表以抓取更多科目

# 本地PDF文件保存的根目录
OUTPUT_ROOT_PATH = r'c:ooks' # 注意使用原始字符串r''以避免反斜杠转义问题
登录后复制

4. 发送POST请求并解析HTML

目标网站通过POST请求动态加载内容。我们需要为每个“班级”和“科目”组合发送一个POST请求,然后解析返回的HTML内容以找到PDF链接。

for klas in klassen:
    for vak in vakken:
        # 构建当前科目和班级的本地保存路径
        current_save_path = os.path.join(OUTPUT_ROOT_PATH, klas, vak)
        # 确保目录存在,如果不存在则创建,exist_ok=True避免目录已存在时报错
        os.makedirs(current_save_path, exist_ok=True)

        # 构建POST请求的表单数据
        payload = {'vak': vak, 'klas_en_schoolsoort': klas}

        print(f"正在处理:班级 '{klas}', 科目 '{vak}'")

        try:
            # 发送POST请求
            response = requests.post(BASE_URL, data=payload, headers=HEADERS)
            response.raise_for_status() # 检查HTTP请求是否成功 (200 OK)

            # 使用BeautifulSoup解析响应的HTML内容
            soup = BeautifulSoup(response.text, "lxml")

            # 查找所有带有href属性的<a>标签
            all_links = soup.find_all('a', {'href': True})

            # ... 后续步骤将在此处处理找到的链接

        except requests.exceptions.RequestException as e:
            print(f"请求失败:{e}")
            continue # 继续处理下一个科目或班级
登录后复制

5. 提取PDF链接与自定义文件名

这是实现核心功能的部分。我们需要遍历所有找到的<a>标签,筛选出PDF链接,并提取其文本内容作为文件名。

        # ... (接上一步骤的循环内部) ...
        for link_tag in all_links:
            # 获取href属性值
            href_url = link_tag.get('href')

            # 检查链接是否以.pdf结尾(不区分大小写)
            if href_url and href_url.lower().endswith('.pdf'):
                # 统一URL中的路径分隔符,将反斜杠替换为正斜杠,以确保URL的正确性
                download_url = href_url.replace('\', '/')

                # 从<a>标签的文本内容中获取期望的本地文件名
                # 例如:<a href="...">Chapter 3 - Weird science</a> -> "Chapter 3 - Weird science.pdf"
                filename_base = link_tag.text.strip() # .strip()去除首尾空白

                # 构建完整的本地文件名,添加.pdf后缀
                local_filename = filename_base + '.pdf'

                # 构建PDF文件在本地的完整保存路径
                full_local_path = os.path.join(current_save_path, local_filename)

                print(f"  发现PDF: {filename_base}")
                print(f"    下载链接: {download_url}")
                print(f"    保存至: {full_local_path}")

                # ... 后续步骤将在此处下载文件
登录后复制

6. 下载并保存PDF文件

使用requests.get()方法下载PDF文件的二进制内容,并将其写入本地文件。

绘蛙AI修图
绘蛙AI修图

绘蛙平台AI修图工具,支持手脚修复、商品重绘、AI扩图、AI换色

绘蛙AI修图 279
查看详情 绘蛙AI修图
                # ... (接上一步骤的循环内部) ...
                try:
                    # 发送GET请求下载PDF文件
                    pdf_response = requests.get(download_url, headers=HEADERS, stream=True)
                    pdf_response.raise_for_status() # 检查下载请求是否成功

                    # 以二进制写入模式打开文件,并将下载内容分块写入
                    with open(full_local_path, 'wb') as f:
                        for chunk in pdf_response.iter_content(chunk_size=8192):
                            f.write(chunk)
                    print(f"    成功下载 '{local_filename}'")

                except requests.exceptions.RequestException as e:
                    print(f"    下载 '{download_url}' 失败: {e}")
                except IOError as e:
                    print(f"    保存文件 '{full_local_path}' 失败: {e}")
                print('---') # 分隔不同PDF的处理信息
登录后复制

7. 完整示例代码

将以上所有步骤整合,形成一个完整的Python脚本:

import os
import requests
from bs4 import BeautifulSoup
from urllib import parse as urlparse

# 目标网站的基础URL
BASE_URL = "https://www.svpo.nl/curriculum.asp"

# 模拟浏览器行为的HTTP头部
HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}

# 定义要抓取的分类和科目
klassen = ['1e klas']
vakken = ['Wiskunde']
# 可以扩展此列表以抓取更多科目,例如:
'''vakken = ['Engels','Aardrijkskunde','Economie', 'Filosofie','Frans', 'Geschiedenis', 
          'Nask', 'Natuurkunde', 'Nederlands', 'Scheikunde', 'Spaans', 'Wiskunde',
          'Biologie', 'Duits', 'Grieks','Latijn','Leesmateriaal', 
          'Loopbaanorientatie','NLT']'''

# 本地PDF文件保存的根目录
OUTPUT_ROOT_PATH = r'c:ooks' # 使用原始字符串r''以避免反斜杠转义问题

def download_pdfs_from_website():
    """
    从指定网站下载PDF文件,并使用链接文本作为文件名。
    """
    print("开始下载PDF文件...")
    for klas in klassen:
        for vak in vakken:
            # 构建当前科目和班级的本地保存路径
            current_save_path = os.path.join(OUTPUT_ROOT_PATH, klas, vak)
            # 确保目录存在,如果不存在则创建
            os.makedirs(current_save_path, exist_ok=True)

            # 构建POST请求的表单数据
            payload = {'vak': vak, 'klas_en_schoolsoort': klas}

            print(f"
--- 正在处理:班级 '{klas}', 科目 '{vak}' ---")

            try:
                # 发送POST请求获取HTML内容
                response = requests.post(BASE_URL, data=payload, headers=HEADERS)
                response.raise_for_status() # 检查HTTP请求是否成功

                # 使用BeautifulSoup解析HTML
                soup = BeautifulSoup(response.text, "lxml")

                # 查找所有带有href属性的<a>标签
                all_links = soup.find_all('a', {'href': True})

                found_pdfs_count = 0
                for link_tag in all_links:
                    href_url = link_tag.get('href')

                    # 检查链接是否是PDF文件
                    if href_url and href_url.lower().endswith('.pdf'):
                        # 统一URL中的路径分隔符(将反斜杠替换为正斜杠)
                        download_url = href_url.replace('\', '/')

                        # 从<a>标签的文本内容中获取期望的本地文件名
                        filename_base = link_tag.text.strip()

                        # 构建完整的本地文件名
                        local_filename = filename_base + '.pdf'

                        # 构建PDF文件在本地的完整保存路径
                        full_local_path = os.path.join(current_save_path, local_filename)

                        print(f"  发现PDF: '{filename_base}'")
                        print(f"    下载链接: {download_url}")
                        print(f"    保存至: {full_local_path}")

                        try:
                            # 发送GET请求下载PDF文件
                            pdf_response = requests.get(download_url, headers=HEADERS, stream=True)
                            pdf_response.raise_for_status() # 检查下载请求是否成功

                            # 以二进制写入模式打开文件,并将下载内容分块写入
                            with open(full_local_path, 'wb') as f:
                                for chunk in pdf_response.iter_content(chunk_size=8192):
                                    f.write(chunk)
                            print(f"    成功下载 '{local_filename}'")
                            found_pdfs_count += 1

                        except requests.exceptions.RequestException as e:
                            print(f"    下载 '{download_url}' 失败: {e}")
                        except IOError as e:
                            print(f"    保存文件 '{full_local_path}' 失败: {e}")
                        print('---') # 分隔不同PDF的处理信息

                if found_pdfs_count == 0:
                    print(f"  在班级 '{klas}', 科目 '{vak}' 中未找到PDF文件。")

            except requests.exceptions.RequestException as e:
                print(f"  处理班级 '{klas}', 科目 '{vak}' 时请求失败:{e}")

    print("
所有PDF文件下载尝试完成。")

if __name__ == "__main__":
    download_pdfs_from_website()
登录后复制

8. 注意事项与优化

  • 文件名合法性: link_tag.text获取的文本可能包含操作系统不允许作为文件名的特殊字符(如/, , :, *, ?, ", <, >, |等)。在实际应用中,建议对filename_base进行额外的清理或“slugify”处理,例如使用正则表达式移除或替换非法字符,确保生成的文件名在所有操作系统上都是有效的。

    import re
    def sanitize_filename(filename):
        # 移除或替换非法字符
        filename = re.sub(r'[\/:*?"<>|]', '_', filename)
        # 移除文件名末尾的点,防止Windows系统问题
        filename = filename.rstrip('.')
        return filename
    
    # 在代码中使用:
    # local_filename = sanitize_filename(filename_base) + '.pdf'
    登录后复制
  • URL编码 虽然本例中的link.text直接用于本地文件名,但如果URL本身包含特殊字符(如空格、非ASCII字符),requests库通常会正确处理其编码。对于URL路径中的反斜杠,我们已经通过replace('\', '/')进行了标准化。

  • 错误处理: 代码中包含了try-except块来捕获requests.exceptions.RequestException(网络错误、HTTP状态码非2xx)和IOError(文件保存错误),这使得脚本更加健壮。

  • User-Agent: 模拟浏览器User-Agent是良好的实践,可以避免某些网站的访问限制。

  • 速率限制与反爬: 对于频繁或大规模的抓取任务,应考虑增加请求之间的延迟(time.sleep()),以避免给服务器造成过大负担或触发网站的反爬机制。

  • stream=True与iter_content: 在下载大文件时,requests.get(..., stream=True)结合response.iter_content()可以避免将整个文件一次性加载到内存中,从而节省内存资源。

9. 总结

通过本教程,你已经学会了如何使用Python requests和BeautifulSoup库,实现从动态加载内容的网站上抓取PDF文件,并根据HTML链接的文本内容自定义保存文件名。这个方法不仅提升了下载文件的可读性,也展示了Python在自动化网页数据处理方面的强大能力。掌握这些技术,可以帮助你高效地管理和组织从网络获取的信息。

以上就是Python与BeautifulSoup:从网站下载PDF并自定义文件名的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号