
本文将详细介绍如何使用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 osimport requestsfrom bs4 import BeautifulSoupfrom urllib import parse as urlparse # 用于URL处理,尽管在本例中主要用于替换路径分隔符
3. 配置请求参数与输出路径
为了与目标网站进行交互并保存文件,我们需要定义请求的URL、HTTP头部、POST请求体以及本地文件输出路径。
# 目标网站的基础URLBASE_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属性的标签 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文件的二进制内容,并将其写入本地文件。
# ... (接上一步骤的循环内部) ... 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 osimport requestsfrom bs4 import BeautifulSoupfrom urllib import parse as urlparse# 目标网站的基础URLBASE_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属性的标签 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("所有PDF文件下载尝试完成。")if __name__ == "__main__": download_pdfs_from_website()
8. 注意事项与优化
文件名合法性: link_tag.text获取的文本可能包含操作系统不允许作为文件名的特殊字符(如/, , :, *, ?, “, , |等)。在实际应用中,建议对filename_base进行额外的清理或“slugify”处理,例如使用正则表达式移除或替换非法字符,确保生成的文件名在所有操作系统上都是有效的。
import redef 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并自定义文件名的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1598653.html
微信扫一扫
支付宝扫一扫