Python自动化爬取百度图片搜索结果并批量下载
1. 为什么需要自动化下载百度图片
在日常工作和学习中,我们经常需要批量获取特定主题的图片素材。比如设计师需要收集灵感参考图,自媒体作者需要配图,或者研究人员需要建立图像数据集。手动一张张保存不仅效率低下,而且容易遗漏。这时候用Python实现自动化下载就能大大提高效率。
百度图片作为国内最大的图片搜索引擎,收录了海量的图片资源。但它的搜索结果页采用了动态加载技术,传统爬虫方法很难直接获取所有图片。我们需要分析其背后的数据接口,才能实现完整抓取。
我最近帮一个做电商的朋友批量下载了上千张产品展示图,整个过程从手动操作需要几小时缩短到几分钟完成。这种自动化方法特别适合需要大量图片素材的场景,比如:
- 建立产品图片库
- 收集竞品分析素材
- 获取训练机器学习模型的数据集
- 批量下载壁纸或表情包
2. 理解百度图片的加载机制
2.1 分析网页请求
打开浏览器开发者工具(F12),切换到Network面板并选择XHR过滤器。当你在百度图片搜索"周杰伦"并滚动页面时,会发现浏览器不断发送AJAX请求获取新图片。
这些请求的URL都有相似的结构,关键参数包括:
- word:搜索关键词
- pn:页码(每页30张图)
- gsm:似乎是随机生成的校验值
通过对比多个请求,我发现只需要修改pn参数就能获取不同批次的图片数据。这为我们批量下载提供了突破口。
2.2 解析返回数据
每个XHR请求返回的是JSON格式数据,包含30张图片的信息。其中最重要的字段是:
- displayNum:总图片数量
- middleURL:图片的中等尺寸URL
- thumbURL:缩略图URL
- hoverURL:鼠标悬停时显示的URL
实际测试发现,middleURL提供的图片质量适中,适合大多数用途。我们可以从这个字段提取图片的真实下载地址。
3. 搭建Python爬虫环境
3.1 安装必要库
这个项目需要以下几个Python库:
- requests:发送HTTP请求
- re:正则表达式处理
- urllib:URL编码处理
- os:文件操作
- fake_useragent:生成随机User-Agent
安装命令:
pip install requests fake_useragent
3.2 配置请求头
百度对爬虫有一定防护,我们需要设置合理的请求头:
headers = {
'User-Agent': UserAgent().random,
'Referer': 'https://image.baidu.com/',
'Accept': 'application/json'
}
特别注意要包含Referer字段,否则可能被拒绝访问。随机User-Agent可以避免请求过于规律而被封禁。
4. 实现核心爬取逻辑
4.1 构建请求URL
通过分析,我们得出百度图片API的基本URL结构:
base_url = 'https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&word={}&pn={}&rn=30&gsm={}'
其中需要动态填充的三个参数:
- {}:搜索关键词(需要URL编码)
- {}:起始位置(每页30张,所以第二页是30,第三页是60...)
- {}:随机生成的8位十六进制数
4.2 获取图片总数
首先发送一个初始请求获取总图片数:
def get_total_count(keyword):
url = base_url.format(parse.quote(keyword), 0, '1e')
response = requests.get(url, headers=headers)
data = response.json()
return int(data['displayNum'])
这个数字决定了我们需要发起多少次分页请求才能获取所有图片。
4.3 分页下载图片
根据总数量计算需要请求的页数,然后循环获取每页的图片URL:
def download_images(keyword, total):
pages = (total + 29) // 30 # 向上取整
for page in range(pages):
url = base_url.format(parse.quote(keyword), page*30, '1e')
response = requests.get(url, headers=headers)
data = response.json()
for img_info in data['data']:
if 'middleURL' in img_info:
download_single_image(img_info['middleURL'], keyword)
每获取到一个图片URL,就调用下载函数保存到本地。
5. 图片下载与存储优化
5.1 单张图片下载
实现单张图片下载的函数:
def download_single_image(img_url, keyword):
try:
response = requests.get(img_url, headers=headers, timeout=10)
if response.status_code == 200:
filename = os.path.join(keyword, f"{hash(img_url)}.jpg")
with open(filename, 'wb') as f:
f.write(response.content)
except Exception as e:
print(f"下载失败:{img_url}, 错误:{e}")
这里使用图片URL的哈希值作为文件名,避免重复和非法字符问题。
5.2 创建分类目录
在下载前创建以关键词命名的目录:
if not os.path.exists(keyword):
os.makedirs(keyword)
如果目录已存在则直接使用,避免重复创建。
5.3 异常处理与重试
网络请求可能会失败,需要添加重试机制:
def safe_request(url, max_retry=3):
for i in range(max_retry):
try:
response = requests.get(url, headers=headers, timeout=10)
return response
except:
if i == max_retry - 1:
raise
time.sleep(2)
这样即使偶尔网络波动,也能保证爬虫继续运行。
6. 完整代码实现
将上述各部分组合起来,完整的爬虫代码如下:
import os
import time
import hashlib
from urllib import parse
import requests
from fake_useragent import UserAgent
class BaiduImageSpider:
def __init__(self):
self.base_url = 'https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&word={}&pn={}&rn=30&gsm={}'
self.headers = {
'User-Agent': UserAgent().random,
'Referer': 'https://image.baidu.com/'
}
def get_total_count(self, keyword):
url = self.base_url.format(parse.quote(keyword), 0, '1e')
response = requests.get(url, headers=self.headers)
return response.json().get('displayNum', 0)
def download_images(self, keyword):
total = self.get_total_count(keyword)
if not total:
print("未找到相关图片")
return
if not os.path.exists(keyword):
os.makedirs(keyword)
pages = (total + 29) // 30
for page in range(pages):
url = self.base_url.format(parse.quote(keyword), page*30, '1e')
data = requests.get(url, headers=self.headers).json()
for img in data.get('data', []):
if 'middleURL' in img:
self.download_single_image(img['middleURL'], keyword)
print(f"已完成第{page+1}/{pages}页下载")
def download_single_image(self, img_url, keyword):
try:
response = requests.get(img_url, headers=self.headers, timeout=10)
if response.status_code == 200:
filename = os.path.join(keyword, f"{hashlib.md5(img_url.encode()).hexdigest()}.jpg")
with open(filename, 'wb') as f:
f.write(response.content)
except Exception as e:
print(f"下载失败:{img_url}")
if __name__ == '__main__':
spider = BaiduImageSpider()
keyword = input("请输入搜索关键词:")
spider.download_images(keyword)
7. 高级技巧与优化建议
7.1 使用代理IP
如果需要大规模爬取,建议使用代理IP池:
proxies = {
'http': 'http://your_proxy:port',
'https': 'http://your_proxy:port'
}
response = requests.get(url, headers=headers, proxies=proxies)
7.2 多线程加速
使用concurrent.futures实现并行下载:
from concurrent.futures import ThreadPoolExecutor
def batch_download(urls, keyword):
with ThreadPoolExecutor(max_workers=5) as executor:
for url in urls:
executor.submit(download_single_image, url, keyword)
7.3 增量爬取
记录已下载的图片,避免重复下载:
downloaded = set()
if img_url not in downloaded:
download_single_image(img_url, keyword)
downloaded.add(img_url)
8. 常见问题与解决方案
在实际使用中可能会遇到这些问题:
- 返回数据为空
- 检查请求头是否完整
- 尝试更换User-Agent
- 确认关键词没有特殊字符
- 下载速度慢
- 增加线程数(但不要过多)
- 使用更快的代理服务器
- 降低超时时间设置
- 图片损坏
- 添加下载完整性校验
- 实现自动重试机制
- 检查存储空间是否充足
- 被封禁IP
- 降低请求频率
- 使用代理轮换
- 添加随机延迟
我在实际项目中发现,控制请求频率在每秒2-3次,配合随机User-Agent,可以稳定运行很长时间不被封禁。另外建议在非高峰时段运行爬虫,成功率会更高。
更多推荐


所有评论(0)