Python图片爬取案例:从零到一的完整编写指南
目录导读
图片爬取的核心原理与法律边界
什么是图片爬取?
图片爬取是指通过自动化程序(通常用Python编写)从网页中获取图片资源,并将其保存到本地设备的过程,其本质是模拟浏览器向服务器发送HTTP请求,解析响应内容中的图片链接,然后下载图片文件。
关键法律提醒:
根据《网络安全法》与《信息网络传播权保护条例》,爬取图片需注意:
- 仅爬取公开、无版权限制的图片(如免费图库、公开作品)
- 尊重网站的
robots.txt协议 - 控制请求频率,避免对服务器造成压力
- 不得用于商业盈利或侵犯他人肖像权
推荐合法目标网站:Unsplash、Pexels、Pixabay等免版权图库。
环境搭建与必备库安装
基础环境要求:
- Python 3.8+(推荐3.10及以上版本)
- 代码编辑器(VSCode / PyCharm / Sublime)
核心依赖库自动安装:
pip install requests beautifulsoup4 lxml pillow
requests:发送HTTP请求获取网页内容beautifulsoup4:解析HTML/XML文档,提取图片链接lxml:高效的HTML解析器(解析速度比默认解析器快5倍)Pillow:验证下载的图片是否损坏
可选增强库:
pip install aiohttp fake-useragent retrying
aiohttp:异步爬取,速度提升3-10倍fake-useragent:随机生成User-Agent,规避反爬检测retrying:自动重试失败的请求
基础案例:单页图片批量下载
目标网站:Unsplash搜索页(公开API)
实现功能:下载首页所有预览图
完整代码:
import requests
from bs4 import BeautifulSoup
import os
from urllib.parse import urljoin
# 配置参数
url = "https://unsplash.com/s/photos/nature"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
save_dir = "images/unsplash"
os.makedirs(save_dir, exist_ok=True)
# 1. 获取网页内容
response = requests.get(url, headers=headers, timeout=10)
response.encoding = "utf-8"
# 2. 解析HTML提取图片链接
soup = BeautifulSoup(response.text, "lxml")
img_tags = soup.find_all("img", {"src": True})
for idx, img in enumerate(img_tags):
img_url = img.get("src")
# 处理相对路径
if not img_url.startswith("http"):
img_url = urljoin(url, img_url)
# 3. 下载图片(仅jpg/png)
if img_url.endswith((".jpg", ".png", ".jpeg")):
try:
img_data = requests.get(img_url, headers=headers, timeout=5).content
filename = f"{save_dir}/img_{idx}.jpg"
with open(filename, "wb") as f:
f.write(img_data)
print(f"✅ 已下载: {filename}")
except Exception as e:
print(f"❌ 下载失败: {img_url},错误: {e}")
运行效果:约10秒内可下载20-30张预览图,文件保存在images/unsplash文件夹内。
进阶案例:多线程加速与反爬规避
痛点:单线程爬取100张图片需30秒,多线程可缩短至5秒。
解决方案:使用concurrent.futures线程池 + 随机请求头。
代码优化:
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
from fake_useragent import UserAgent
import time
# 生成随机User-Agent
ua = UserAgent()
def download_single_image(img_url, idx, save_dir):
"""单个图片下载函数"""
try:
headers = {"User-Agent": ua.random}
img_data = requests.get(img_url, headers=headers, timeout=8).content
filename = f"{save_dir}/img_{idx:04d}.jpg"
with open(filename, "wb") as f:
f.write(img_data)
return f"✅ 成功: {filename}"
except Exception as e:
return f"❌ 失败: {img_url},错误: {e}"
# 批量下载(多线程)
def batch_download(img_urls, save_dir, max_workers=10):
start = time.time()
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {
executor.submit(download_single_image, url, idx, save_dir): idx
for idx, url in enumerate(img_urls)
}
for future in as_completed(futures):
print(future.result())
print(f"⏱️ 耗时: {time.time() - start:.2f}秒")
# 准备图片链接列表(示例)
all_img_urls = [
"https://images.unsplash.com/photo-1",
"https://images.unsplash.com/photo-2",
# ... 可添加100个链接
]
batch_download(all_img_urls, "images/multi_thread")
反爬增强技巧:
- 每次请求更换
User-Agent(如上代码) - 添加
Referer头部模拟从主站跳转 - 每次请求后随机休眠0.5-1秒
- 使用代理IP池(付费或免费代理)
常见报错与调试技巧
| 错误类型 | 典型错误信息 | 解决策略 |
|---|---|---|
| SSL证书 | SSLError: CERTIFICATE_VERIFY_FAILED |
添加verify=False参数,或升级certifi库 |
| 403禁止 | HTTP 403 Forbidden |
添加有效User-Agent和Cookie |
| 连接超时 | TimeoutError |
增加timeout值至10-15秒,或使用重试机制 |
| 图片损坏 | IOError | 下载后使用Pillow验证:Image.open(filename).verify() |
| 动态加载 | 只能爬取到js文件 | 改用selenium或分析XHR请求真实接口 |
调试口诀:
“先看状态码,再查Headers;动态内容用Selenium,静态解析BeautifulSoup;反爬层叠不放弃,重试休眠最管用。”
问答环节:开发者最常遇到的5个问题
Q1:为什么爬取的图片是空白的或损坏的?
A:最常见的原因是URL指向的是“缩略图”而非原始图片,解决方案:查看网页源代码,查找data-original、srcset或data-src属性,这些通常指向高清原图。
Q2:如何处理懒加载(Lazy Load)的图片?
A:懒加载的图片src属性通常为placeholder,真实链接存储在data-src或data-original中,修改提取代码:
img_url = img.get("data-src") or img.get("data-original") or img.get("src")
Q3:爬取速度很慢,如何优化?
A:三种方案:
- 使用
concurrent.futures多线程(提升3-5倍) - 改用异步库
aiohttp(提升10倍) - 下载前先用
HEAD请求检查图片是否存在,避免无效下载
Q4:如何绕过Cloudflare或CAPTCHA验证?
A:Cloudflare等CDN通常需要渲染JavaScript,推荐方案:
- 使用
cloudscraper库(模拟浏览器指纹) - 或
splash无头浏览器实例 - 或购买付费代理API服务
Q5:爬取的图片太大,导致内存溢出怎么解决?
A:使用流式下载(chunk),避免一次性加载全部内容:
response = requests.get(img_url, stream=True)
with open(filename, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
总结与扩展建议
通过本文的案例,你已经掌握:
- 使用
requests+BeautifulSoup构建基础爬虫 - 用多线程提升爬取效率
- 规避常见反爬手段
下一站学习方向:
- 使用
scrapy框架构建分布式爬虫 - 配合
opencv自动分类下载的图片 - 构建图片去重系统(基于MD5或感知哈希)
代码只是工具,合理合法使用才是技术人的担当,如有任何问题,欢迎在技术社区与我交流。
标签: Python案例