3行代码搞定网页资源下载:requests-html文件保存终极指南
你是否还在为网页资源下载写几十行代码?还在纠结相对路径处理、二进制流保存、异步下载等复杂问题?本文将带你用requests-html库的3个核心功能,轻松实现图片、文档、视频等各类资源的批量下载,让Python文件下载效率提升10倍。
为什么选择requests-html下载文件
requests-html作为"面向人类的Pythonic HTML解析库",在文件下载方面有三大优势:
- 一体化流程:从网页请求、元素解析到资源下载全程无感知衔接
- 智能路径处理:自动处理相对URL转换,无需手动拼接requests_html.py
- 异步支持:原生支持异步下载,大幅提升多文件获取效率requests_html.py
项目官方文档详细说明了这些特性:docs/source/index.rst,而完整的实现逻辑可以在主模块中查看:requests_html.py
核心功能解析:3个关键方法
1. absolute_links:获取完整资源URL
在下载文件前,首先需要获取完整的资源链接。requests-html的absolute_links属性会自动将页面中的相对链接转换为绝对URL:
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('https://example.com/gallery')
# 获取所有图片的绝对URL
image_urls = [img.attrs['src'] for img in r.html.find('img') if 'src' in img.attrs]
# 或者直接获取所有链接
all_links = r.html.absolute_links
这个功能的核心实现位于requests_html.py的absolute_links方法,它通过_make_absolute函数处理各种URL情况:requests_html.py
2. HTMLSession:同步文件下载
获取资源URL后,使用HTMLSession的get方法即可下载文件。以下是一个完整的图片下载示例:
from requests_html import HTMLSession
import os
session = HTMLSession()
r = session.get('https://example.com/images')
# 创建保存目录
os.makedirs('downloads', exist_ok=True)
# 下载所有图片
for img in r.html.find('img'):
try:
img_url = img.attrs['src']
# 获取图片响应
img_response = session.get(img_url)
# 提取文件名
filename = os.path.basename(img_url)
# 保存图片
with open(f'downloads/{filename}', 'wb') as f:
f.write(img_response.content)
print(f"下载成功: {filename}")
except Exception as e:
print(f"下载失败: {e}")
这个过程中,session.get()方法返回的HTMLResponse对象包含了文件的二进制内容,通过content属性即可获取:requests_html.py
3. AsyncHTMLSession:异步批量下载
对于需要下载多个文件的场景,异步下载能显著提高效率。requests-html的AsyncHTMLSession完美支持这一需求:
from requests_html import AsyncHTMLSession
import os
import asyncio
async def download_file(session, url, save_path):
try:
response = await session.get(url)
with open(save_path, 'wb') as f:
f.write(response.content)
return f"成功: {os.path.basename(save_path)}"
except Exception as e:
return f"失败: {url}, 错误: {e}"
async def main():
session = AsyncHTMLSession()
r = await session.get('https://example.com/documents')
os.makedirs('pdf_downloads', exist_ok=True)
# 获取所有PDF链接
pdf_links = [link for link in r.html.absolute_links if link.endswith('.pdf')]
# 创建下载任务
tasks = []
for url in pdf_links:
filename = os.path.basename(url)
tasks.append(download_file(session, url, f'pdf_downloads/{filename}'))
# 执行所有任务
results = await asyncio.gather(*tasks)
for result in results:
print(result)
if __name__ == '__main__':
session = AsyncHTMLSession()
session.run(main)
异步功能的核心实现位于requests_html.py的arender方法,它使用了Python的asyncio库来处理并发请求。
实战案例:电商网站图片批量下载器
下面我们将综合运用上述功能,创建一个电商网站图片批量下载工具,支持按商品分类保存图片:
from requests_html import HTMLSession
import os
import re
def sanitize_filename(name):
"""清理文件名中的非法字符"""
return re.sub(r'[\\/*?:"<>|]', '', name)
def download_product_images(category_url, save_dir='products'):
session = HTMLSession()
r = session.get(category_url)
# 获取所有商品卡片
products = r.html.find('.product-card')
for product in products:
try:
# 获取商品名称
name = product.find('.product-title', first=True).text
sanitized_name = sanitize_filename(name)
product_dir = os.path.join(save_dir, sanitized_name)
os.makedirs(product_dir, exist_ok=True)
# 获取商品图片
images = product.find('.product-image img')
for i, img in enumerate(images):
if 'src' in img.attrs:
img_url = img.attrs['src']
# 处理可能的缩略图URL,获取原图
img_url = img_url.replace('thumb_', '')
# 下载图片
img_response = session.get(img_url)
img_ext = os.path.splitext(img_url)[1] or '.jpg'
img_path = os.path.join(product_dir, f'image_{i}{img_ext}')
with open(img_path, 'wb') as f:
f.write(img_response.content)
print(f"已保存: {img_path}")
except Exception as e:
print(f"处理商品时出错: {e}")
continue
# 使用示例
if __name__ == '__main__':
categories = {
'手机': 'https://example.com/category/phones',
'笔记本电脑': 'https://example.com/category/laptops',
'智能手表': 'https://example.com/category/smartwatches'
}
for category, url in categories.items():
print(f"===== 开始下载 {category} 图片 =====")
download_product_images(url, save_dir=f'products/{category}')
这个案例展示了如何结合requests-html的选择器功能requests_html.py和文件下载功能,实现结构化的数据获取与保存。
高级技巧:自定义下载器配置
对于复杂的下载需求,我们可以扩展HTMLSession类,创建自定义下载器,添加超时控制、重试机制和进度显示:
from requests_html import HTMLSession
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import time
from tqdm import tqdm
class DownloadSession(HTMLSession):
def __init__(self, retries=3, backoff_factor=0.3, timeout=10):
super().__init__()
# 配置重试策略
retry_strategy = Retry(
total=retries,
backoff_factor=backoff_factor,
status_forcelist=[429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.mount("https://", adapter)
self.mount("http://", adapter)
self.timeout = timeout
def download_with_progress(self, url, save_path):
"""带进度条的文件下载"""
try:
response = self.get(url, stream=True, timeout=self.timeout)
total_size = int(response.headers.get('content-length', 0))
with open(save_path, 'wb') as f, tqdm(
desc=os.path.basename(save_path),
total=total_size,
unit='iB',
unit_scale=True,
unit_divisor=1024,
) as bar:
for data in response.iter_content(chunk_size=1024):
size = f.write(data)
bar.update(size)
return True
except Exception as e:
print(f"下载失败: {e}")
return False
这个增强版下载器使用了requests的会话适配器功能,实现了网络错误自动重试,同时添加了进度条显示,让大文件下载过程更加可控。
常见问题解决方案
1. 处理JavaScript动态加载的资源
有些网站使用JavaScript动态加载图片等资源,这时需要使用requests-html的渲染功能:
r = session.get(url)
# 渲染JavaScript
r.html.render(scrolldown=3, sleep=1) # 模拟滚动加载更多内容
# 之后再获取图片链接
images = r.html.find('img')
渲染功能的实现位于requests_html.py的render方法,它使用Pyppeteer库来模拟浏览器行为。
2. 解决反爬限制
对于有反爬机制的网站,可以通过设置User-Agent和Cookie来绕过限制:
session = HTMLSession()
# 设置请求头
session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': 'https://www.google.com/'
})
# 添加Cookie
session.cookies.set('sessionid', 'your_session_id_here')
r = session.get(url)
3. 处理大型文件下载
对于GB级别的大型文件,建议使用流式下载,避免占用过多内存:
with session.get(large_file_url, stream=True) as r:
with open('large_file.iso', 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
if chunk: # 过滤掉保持连接的空块
f.write(chunk)
f.flush()
性能优化指南
为了进一步提升下载效率,可以从以下几个方面进行优化:
1. 连接池复用
通过设置适配器参数,复用HTTP连接:
from requests.adapters import HTTPAdapter
session = HTMLSession()
# 设置连接池大小
adapter = HTTPAdapter(pool_connections=10, pool_maxsize=10)
session.mount('http://', adapter)
session.mount('https://', adapter)
2. 异步并发控制
在异步下载时,控制并发数量避免过载:
async def bounded_gather(tasks, limit=5):
"""限制并发数量的gather"""
semaphore = asyncio.Semaphore(limit)
async def sem_task(task):
async with semaphore:
return await task
return await asyncio.gather(*[sem_task(task) for task in tasks])
3. 断点续传
对于不稳定的网络环境,实现断点续传功能:
def resume_download(url, save_path, chunk_size=1024):
if os.path.exists(save_path):
file_size = os.path.getsize(save_path)
headers = {'Range': f'bytes={file_size}-'}
else:
file_size = 0
headers = {}
with session.get(url, headers=headers, stream=True) as r:
total_size = int(r.headers.get('content-length', 0)) + file_size
with open(save_path, 'ab') as f, tqdm(
initial=file_size,
total=total_size,
unit='iB',
unit_scale=True
) as bar:
for data in r.iter_content(chunk_size=chunk_size):
size = f.write(data)
bar.update(size)
总结与扩展学习
通过本文的介绍,你已经掌握了使用requests-html下载网页资源的核心方法和高级技巧。这个强大的库不仅简化了文件下载流程,还提供了丰富的HTML解析功能,让网页数据提取变得前所未有的简单。
想要深入学习更多功能,可以参考以下资源:
- 官方测试用例:tests/test_requests_html.py展示了各种功能的使用示例
- 异步编程指南:requests_html.py中的异步迭代器实现
- 渲染功能:requests_html.py的
render方法支持复杂JavaScript页面
项目的完整文档可以在docs/source/index.rst找到,而所有源代码都位于项目根目录中。现在,你已经具备了处理各种网页资源下载任务的能力,快去用这3行代码的魔力简化你的工作流吧!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



