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. 常见问题与解决方案

在实际使用中可能会遇到这些问题:

  1. 返回数据为空
  • 检查请求头是否完整
  • 尝试更换User-Agent
  • 确认关键词没有特殊字符
  1. 下载速度慢
  • 增加线程数(但不要过多)
  • 使用更快的代理服务器
  • 降低超时时间设置
  1. 图片损坏
  • 添加下载完整性校验
  • 实现自动重试机制
  • 检查存储空间是否充足
  1. 被封禁IP
  • 降低请求频率
  • 使用代理轮换
  • 添加随机延迟

我在实际项目中发现,控制请求频率在每秒2-3次,配合随机User-Agent,可以稳定运行很长时间不被封禁。另外建议在非高峰时段运行爬虫,成功率会更高。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐