0

0

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

霞舞

霞舞

发布时间:2025-11-24 12:00:11

|

745人浏览过

|

来源于php中文网

原创

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

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

1. 概述

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

我们将使用以下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:\books' # 注意使用原始字符串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属性的标签
            all_links = soup.find_all('a', {'href': True})

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

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

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

这是实现核心功能的部分。我们需要遍历所有找到的标签,筛选出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('\\', '/')

                # 从标签的文本内容中获取期望的本地文件名
                # 例如:Chapter 3 - Weird science -> "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文件的二进制内容,并将其写入本地文件。

Tun2企业网站管理系统(支持静态生成)2.0.18
Tun2企业网站管理系统(支持静态生成)2.0.18

Tun2企业网站管理系统是一套针对企业开发的CMS。它是一款具有专业级的功能和傻瓜式管理的网站站管理软件。Tun2追求的是简单与实用。它不同于大型门户CMS,繁杂并令人无从下手。因为它正对的用户是企业网站,所以它将更贴近企业本身【2.0.18更新说明】1、修复linux主机无法群发邮件的bug2、增加自定义栏目静态文件保存名、内容静态文件保存名& K6 V& N4

下载
                # ... (接上一步骤的循环内部) ...
                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:\books' # 使用原始字符串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"\n--- 正在处理:班级 '{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属性的标签
                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('\\', '/')

                        # 从标签的文本内容中获取期望的本地文件名
                        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("\n所有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开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

753

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

636

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

758

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

618

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1262

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

547

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

577

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

707

2023.08.11

公务员递补名单公布时间 公务员递补要求
公务员递补名单公布时间 公务员递补要求

公务员递补名单公布时间不固定,通常在面试前,由招录单位(如国家知识产权局、海关等)发布,依据是原入围考生放弃资格,会按笔试成绩从高到低递补,递补考生需按公告要求限时确认并提交材料,及时参加面试/体检等后续环节。要求核心是按招录单位公告及时响应、提交材料(确认书、资格复审材料)并准时参加面试。

0

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Django 教程
Django 教程

共28课时 | 3.1万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

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