Python爬虫:用Requests库优雅地下载图片
2024-10-03 07:23:34
在 Python 爬虫的世界里,下载图片是一个家常便饭般的操作。你可能早已习惯了 urllib2
的简单直接,一个 urlopen
加上 read
就能轻松搞定。但随着时代发展,requests
库凭借其优雅的 API 设计和强大的功能,逐渐成为了主流选择。这时,你或许也想尝试用它来下载图片,却发现直接套用 urllib2
的思路却行不通。别担心,本文将为你详细讲解如何使用 requests
库正确地下载并保存图片,并分析其中的一些关键点,助你顺利完成图片下载任务。
我们先来看一个常见的错误示例。在使用 urllib2
时,代码非常简洁,直接打开 URL,读取内容,写入文件,一气呵成。但在 requests
的代码中,如果直接使用 r.raw.read()
来读取响应内容,就会遇到麻烦。
这是因为 requests
库的设计理念与 urllib2
不同。r.raw
属性返回的是一个原始响应体,它是一个类似于文件对象的流,可以直接读取二进制数据。然而,直接使用 r.raw.read()
会带来一些问题。首先,它会一次性将整个响应体加载到内存中,如果图片很大,你的程序内存占用可能会瞬间飙升。其次,r.raw
不会自动解码压缩数据,如果服务器返回的是 gzip 压缩的图片,你读取到的就只是一堆乱码,无法正常显示。
那么,如何正确地使用 requests
下载图片呢?下面介绍两种常用的方法。
方法一:使用 content
属性
requests
的 content
属性返回的是响应体的字节流,它已经自动解码了压缩数据,并且支持分块读取,避免一次性将整个响应体加载到内存。我们可以使用如下代码下载图片:
import requests
r = requests.get(image_url, stream=True)
if r.status_code == 200:
with open(file_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk: # 过滤掉 keep-alive 的空块
f.write(chunk)
f.flush()
这里需要注意以下几点:
- 我们在
requests.get()
中添加了stream=True
参数,告诉requests
不要一次性下载整个响应体,而是采用流式下载的方式。 - 我们使用
r.iter_content()
方法来迭代读取响应体,每次读取 1024 字节,可以根据实际情况调整块大小。 - 在写入文件时,我们使用了
wb
模式,表示以二进制模式写入,确保图片数据不会被错误地编码。 - 每次写入数据后,我们调用
f.flush()
方法将缓冲区的数据立即写入磁盘,避免程序意外终止导致数据丢失。
方法二:使用 shutil
模块
除了手动分块写入,我们还可以借助 Python 标准库中的 shutil
模块来简化代码:
import requests
import shutil
r = requests.get(image_url, stream=True)
if r.status_code == 200:
with open(file_path, 'wb') as f:
r.raw.decode_content = True # 启用自动解码压缩数据
shutil.copyfileobj(r.raw, f)
这里,我们仍然使用了 stream=True
参数开启流式下载。然后,我们将 r.raw.decode_content
设置为 True
,告诉 requests
自动解码压缩数据,例如 gzip 压缩。最后,我们使用 shutil.copyfileobj()
方法将 r.raw
的内容复制到文件中, shutil
模块会高效地处理数据复制过程。
两种方法都能达到下载图片的目的,选择哪种方法取决于你的个人喜好和实际需求。如果图片比较小,或者你对内存占用不敏感,那么使用 content
属性会更简洁一些。如果图片很大,或者你希望更精细地控制下载过程,那么使用 shutil
模块会更灵活一些。
总结一下,使用 requests
下载图片的关键在于:
- 使用
stream=True
参数开启流式下载,避免一次性加载整个响应体到内存。 - 使用
content
属性或shutil
模块来处理响应体,前者简洁易用,后者功能更强大。 - 注意以二进制模式写入文件,确保图片数据不会被错误地编码。
希望本文能够帮助你更好地理解如何使用 requests
下载图片,并在实际应用中避免一些常见的错误,让你的爬虫程序更加健壮高效。
常见问题解答
1. 为什么下载的图片无法打开?
这可能是因为文件写入模式错误,没有使用二进制模式 wb
,导致图片数据被错误地编码。也可能是因为服务器返回的不是图片数据,你需要检查响应状态码和响应内容类型。
2. 如何下载大尺寸图片?
建议使用 shutil
模块和流式下载,避免一次性将整个图片加载到内存,导致内存溢出。
3. 如何处理下载过程中出现的网络错误?
可以使用 try...except
语句捕获网络异常,例如 requests.exceptions.RequestException
,并在异常处理代码中进行重试或记录错误信息。
4. 如何下载需要登录才能访问的图片?
你需要先使用 requests
库模拟登录,获取登录后的 cookies 或 token,然后在下载图片的请求中带上这些身份验证信息。
5. 如何批量下载图片?
可以使用循环遍历图片 URL 列表,并对每个 URL 应用下载图片的代码。可以使用多线程或异步编程技术来提高下载速度。