在日常学习和工作中,我们经常需要批量获取网络图片,比如做素材收集、数据集构建等。本文将以百度图片为例,手把手教你用 Python 实现图片批量爬取,从接口分析、反爬绕过到异常处理,完整覆盖爬虫开发的核心要点。

1. 核心依赖库

本次开发需要用到以下 Python 库,提前通过 pip 安装:

pip install requests beautifulsoup4
  • requests:发送 HTTP 请求,获取网页 / 接口数据
  • json:解析百度图片返回的 JSON 格式数据(Python 内置)
  • os:处理文件和目录(Python 内置)
  • re:正则表达式,修复 JSON 格式问题(Python 内置)
  • BeautifulSoup4:解析 HTML 页面(备用方案)
  • traceback:定位代码异常(Python 内置)

百度图片提供了 JSON 格式的接口(返回图片链接等信息)

  1. 构造请求参数,模拟浏览器发送请求
  2. 解析接口返回的 JSON 数据,提取图片 URL
  3. 批量下载图片并按关键词分类保存
  4. 处理各类异常(接口解析失败、图片链接失效、网络超时等

2.完整代码实现

# 需要安装第三库:
# pip install requests
# pip install beautifulsoup4 用 BeautifulSoup 解析 HTML 中的图片链接,这样更稳定

import requests
import os
import json
import traceback  #追踪信息

index = 1  #index初始值为1


def download(url, word):
    global index  # 声明使用全局变量index,确保函数内可以修改全局的序
    if not url:
        print("下载链接为空,跳过")
        return
    try:    # 异常捕获:处理下载过程中可能出现的错误(如网络超时、链接失效等)
        r = requests.get(url, timeout=15)
        r.raise_for_status()   # 检查响应状态码:如果不是200(成功),直接抛出异常
        # 确保路径存在
        save_dir = os.path.join('imgs', word)
        if not os.path.exists(save_dir):
            os.makedirs(save_dir, exist_ok=True)   # 如果保存目录不存在,则创建(exist_ok=True:目录已存在时不报错)
        name = os.path.join(save_dir, f'{index}.jpg')
        with open(name, 'wb') as f:  # wb写入二进制格式图片
            f.write(r.content)

        print(f"图片下载成功:{url},保存为:{name}")
        index += 1
    except Exception as e:
        print(f"下载失败:{url},错误:{str(e)}")


if not os.path.exists("imgs"):
    os.makedirs("imgs", exist_ok=True)
word = input("请输入下载关键字:")
num = int(input("请输入数量:"))

# 使用百度图片搜索API
for page in range(0, num):
    # 计算pn参数,每页30张图片
    pn = page * 30
    # 构建请求URL
    url = f"https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&fr=&word={word}&queryWord={word}&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=&latest=&copyright=&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&expermode=&nojc=&isAsync=&pn={pn}&rn=30&gsm=1e&1623456789012="

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36',
        'Referer': 'https://image.baidu.com/',
        'Accept': 'text/plain, */*; q=0.01'
    }

    try:
        print(f"\n请求第{page + 1}页数据...")
        html = requests.get(url, headers=headers, timeout=15)
        html.raise_for_status()
        print(f"响应状态码:{html.status_code}")

        # 预处理JSON数据,处理无效的转义字符
        import re

        # 修复无效的转义序列
        raw_text = html.text

        # 方法1:尝试使用更健壮的正则表达式修复无效转义
        try:
            # 替换无效的转义字符,只保留有效的转义序列
            # 有效的转义序列:\", \\, \b, \f, \n, \r, \t, \uXXXX
            raw_text = re.sub(r'(?<!\\)\\(?![\"\\/bfnrt]|u[0-9a-fA-F]{4})', r'\\\\', raw_text)
            # 解析JSON数据
            data = json.loads(raw_text)
        except json.JSONDecodeError as e:
            print(f"第一次JSON解析失败,尝试使用备用方法:{str(e)}")
            # 方法2:尝试使用更简单的方法,移除所有无效的转义字符
            try:
                # 移除所有反斜杠,除了有效的转义序列
                # 这是一个更激进的方法,可能会移除一些有用的转义序列,但可以解决大多数解析错误
                raw_text = html.text
                # 保留有效的转义序列
                valid_escapes = ['\\"', '\\\\', '\\b', '\\f', '\\n', '\\r', '\\t']
                # 临时替换有效的转义序列
                for i, escape in enumerate(valid_escapes):
                    raw_text = raw_text.replace(escape, f'__ESCAPE_{i}__')
                # 移除所有剩余的反斜杠
                raw_text = raw_text.replace('\\', '')
                # 恢复有效的转义序列
                for i, escape in enumerate(valid_escapes):
                    raw_text = raw_text.replace(f'__ESCAPE_{i}__', escape)
                # 解析JSON数据
                data = json.loads(raw_text)
            except json.JSONDecodeError as e2:
                print(f"第二次JSON解析失败,尝试使用备用API:{str(e2)}")
                # 方法3:使用备用的百度图片搜索API
                # 构建备用API URL
                backup_url = f"https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&sf=1&fmq=1707724050277_R&pv=&ic=&nc=1&z=&hd=&latest=&copyright=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&sid=&word={word}"
                print(f"使用备用API:{backup_url}")
                # 发送备用请求
                backup_response = requests.get(backup_url, headers=headers, timeout=15)
                backup_response.raise_for_status()
                # 从HTML中提取图片URL(简单方法)
                from bs4 import BeautifulSoup

                soup = BeautifulSoup(backup_response.text, 'html.parser')
                # 查找所有图片标签
                img_tags = soup.find_all('img')
                print(f"从HTML中找到{len(img_tags)}个图片标签")
                # 创建模拟的data结构
                data = {'data': []}
                for i, img in enumerate(img_tags[:10]):  # 只取前10张图片
                    img_url = img.get('src') or img.get('data-src')
                    if img_url and img_url.startswith('http'):
                        data['data'].append({'thumbURL': img_url})
                print(f"创建了{len(data['data'])}个模拟图片项")

        # 检查是否有data字段
        if 'data' in data:
            image_data = data['data']
            print(f"获取到{len(image_data)}个图片项")

            # 遍历图片数据
            for i, item in enumerate(image_data):
                if isinstance(item, dict):   # 检查当前项是否为字典类型(避免非字典数据导致报错)
                    # 尝试获取图片URL的多种可能字段
                    img_url = None
                    # 尝试所有可能的图片URL字段名
                    url_fields = ['objurl', 'thumburl', 'thumbURL', 'objURL', 'middleURL', 'middleurl', 'hoverURL',
                                  'hoverurl', 'url']
                    for field in url_fields:
                        if field in item:
                            img_url = item[field]
                            print(f"从{field}字段找到URL:{img_url}")
                            break
                    if img_url:
                        download(img_url, word)
                    else:
                        print(f"第{i + 1}个图片:未找到URL")
                else:
                    print(f"第{i + 1}个项:非字典类型,跳过")
        else:
            print("响应中没有data字段")

    except Exception as e:
        print(f"请求失败:{str(e)}")
        traceback.print_exc()   #追踪异常信息

3.核心知识点解析

①反爬绕过技巧

百度会识别非浏览器的请求,我们通过headers模拟浏览器:

  • User-Agent:标识浏览器类型和版本
  • Referer:告诉服务器请求来自百度图片主页,模拟正常跳转
  • 超时设置:timeout=15避免请求卡死

②JSON 解析容错处理

百度返回的 JSON 数据可能包含无效转义字符,导致解析失败,这里做了三层容错:

  1. 正则修复无效转义(保留有效转义序列)
  2. 激进移除无效反斜杠(备用方案)
  3. 降级到 HTML 解析(最终兜底)

③图片 URL 字段兼容

        百度图片接口返回的图片链接字段不固定,我们遍历所有可能的字段名(如thumbURLobjURLmiddleURL),确保能找到有效链接。

④异常处理

代码中覆盖了以下异常场景:

  • 网络超时 / 请求失败(requests.get超时 +raise_for_status
  • 图片链接失效(下载函数的异常捕获)
  • JSON 解析失败(三层降级方案)
  • 目录创建失败(os.makedirsexist_ok=True

运行代码

  • 按照提示输入:
    • 下载关键字(如 “树叶”“花朵”)
    • 下载页数(每页 30 张图片)
  • 下载的图片会保存在imgs/关键词/目录下,按序号命名。

  • 爬取频率:不要过于频繁请求(建议每页间隔 1-2 秒),避免 IP 被封禁
  • 合规性:仅用于学习和个人非商用场景,遵守网站 robots 协议
  • 扩展优化:可添加以下功能:
    • 图片格式过滤(只下载 jpg/png)
    • 断点续传(记录已下载序号)
    • 多线程下载(提升效率)
    • 代理 IP 池(避免 IP 封禁)

        你可以基于此代码扩展更多功能,比如添加进度条、支持多关键词批量爬取、图片去重等,进一步提升爬虫的实用性。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐