新手爬百度图片时,最容易遇到“只能爬30张”“保存的图模糊/打不开”“爬几次就被封”这3个问题——看似简单的批量下载,实则因为百度图片的动态加载+反爬机制+缩略图陷阱,让很多新手卡壳。
本文针对性解决这3个核心坑,用 requests 模拟接口请求、json 解析数据,改完代码就能批量爬取高清原图并保存本地,全程带避坑说明和完整代码,新手也能10分钟上手!
先明确:百度图片的爬取核心逻辑
百度图片不是静态网页,而是下滑时通过API接口动态加载图片数据(返回JSON格式),而非直接嵌入HTML。新手的误区的是“爬HTML找img标签”,正确思路是“抓包找API接口→模拟请求→提取原图链接→下载保存”。
新手常见3个错(附解决方案)
错1:只爬静态HTML,最多拿到30张(动态加载未处理)
表现:
运行代码后只保存了30-50张图片,下滑百度图片页面看到的更多图片爬不到。
原因:
百度图片首页的HTML只加载了“初始一屏”的图片(约30张),后续图片需要下滑时触发AJAX请求,从API接口获取更多数据。新手直接用 requests.get 爬首页HTML,自然拿不到后续图片。
解决:
抓包找到百度图片的数据接口(关键!);分析接口参数,循环请求不同“页码”,批量获取图片数据。
如何抓包找接口?(新手也会)
打开百度图片(https://image.baidu.com/),搜索关键词(如“猫咪”);按F12打开开发者工具→切换到「Network」→刷新页面→筛选「XHR/ Fetch」(动态请求);找到名称含「acjson」的请求(这就是图片数据接口),点击它→「Headers」查看请求URL和参数,「Response」查看返回的JSON数据(包含图片链接)。
接口示例(已简化):
https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=猫咪&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=1&latest=0©right=0&word=猫咪&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=30&rn=60&gsm=1e&1731234567890=
核心参数说明(必须正确设置):
word:搜索关键词(如“猫咪”,需URL编码);pn:已加载的图片总数(分页关键!第1页pn=0,第2页pn=60,第3页pn=120…每次加60);rn:每页返回的图片数量(最大60,设置多了会被限制);hd:是否高清(1=高清,0=标清)。
错2:爬的是缩略图,保存后模糊/失效(原图链接提取错误)
表现:
保存的图片只有几百KB,模糊不清,甚至打开提示“文件损坏”。
原因:
新手直接提取JSON中thumbURL(缩略图链接)或HTML中img标签的src,这些都是百度用于预览的缩略图,不仅画质差,还可能带时效性(过期失效)。
解决:
提取JSON中的原图链接字段,优先级:replaceUrl[0].ObjURL > ObjURL > hoverURL(这些是高清原图链接,无时效限制)。
错3:请求太“硬”,被百度反爬封禁(无反爬措施)
表现:
前几次能爬,后来报错403 Forbidden,甚至本地网络访问百度图片都变慢(IP被临时限制)。
原因:
未设置请求头,被识别为“爬虫”;请求频率太快(毫秒级请求,不像人类操作);同一IP反复请求,触发百度反爬机制。
解决:
完善请求头(模拟真实浏览器,必带User-Agent和Referer);限速爬取(每请求1页暂停1-3秒,随机时间更安全);可选:使用代理IP池(避免单一IP被封);随机化请求参数(如时间戳1731234567890,每次请求生成新值)。
改完就能用:批量爬百度图片完整代码(避坑版)
核心功能:
支持自定义关键词、爬取数量、保存路径;自动获取高清原图,避免缩略图;带反爬措施,不易被封;自动创建文件夹,批量保存本地,跳过失效链接。
import requests
import json
import os
import random
import time
from urllib.parse import quote # URL编码(处理关键词中的中文)
from loguru import logger
# ---------------------- 配置参数(新手可直接修改这部分) ----------------------
KEYWORD = "猫咪" # 搜索关键词(可替换为其他,如“风景”“动漫”)
SAVE_DIR = f"./百度图片_{KEYWORD}" # 图片保存目录(自动创建)
TOTAL_NUM = 300 # 目标爬取数量(最大建议≤1000,避免触发反爬)
PER_PAGE = 60 # 每页爬取数量(最大60,百度限制)
TIMEOUT = 10 # 请求超时时间(秒)
DELAY = (1.5, 3) # 每页爬取间隔(1.5-3秒随机,模拟人类操作)
# 完善请求头(模拟浏览器,关键反爬措施)
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
"Referer": "https://image.baidu.com/", # 表示请求来源是百度图片,必带!
"Accept": "text/plain, */*; q=0.01",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"X-Requested-With": "XMLHttpRequest" # 标识为AJAX请求,符合百度接口预期
}
# ---------------------- 工具函数(无需修改) ----------------------
def create_save_dir():
"""创建图片保存目录,不存在则新建"""
if not os.path.exists(SAVE_DIR):
os.makedirs(SAVE_DIR)
logger.info(f"✅ 创建保存目录:{SAVE_DIR}")
else:
logger.info(f"✅ 保存目录已存在:{SAVE_DIR}")
def get_image_urls(page_pn):
"""请求百度图片API,获取当前页的原图链接列表"""
# 百度图片API接口(核心!)
api_url = "https://image.baidu.com/search/acjson"
# 构造请求参数(动态生成时间戳,避免重复)
params = {
"tn": "resultjson_com",
"ipn": "rj",
"ct": 201326592,
"is": "",
"fp": "result",
"queryWord": KEYWORD,
"cl": 2,
"lm": -1,
"ie": "utf-8",
"oe": "utf-8",
"adpicid": "",
"st": -1,
"z": "",
"ic": "",
"hd": 1, # 1=高清图,0=标清图
"latest": 0,
"copyright": 0,
"word": quote(KEYWORD), # 关键词URL编码(处理中文/特殊字符)
"s": "",
"se": "",
"tab": "",
"width": "",
"height": "",
"face": 0,
"istype": 2,
"qc": "",
"nc": 1,
"fr": "",
"expermode": "",
"force": "",
"pn": page_pn, # 已加载图片总数(分页关键)
"rn": PER_PAGE, # 每页返回数量
"gsm": "1e",
str(random.randint(1000000000000, 9999999999999)): "" # 随机时间戳参数,反爬
}
try:
# 发送请求(关闭SSL验证,避免部分环境报错)
response = requests.get(
url=api_url,
headers=HEADERS,
params=params,
timeout=TIMEOUT,
verify=False
)
response.raise_for_status() # 状态码非200则抛异常
# 解析JSON数据(百度返回的JSON可能带多余字符,需处理)
# 解决:用try-except捕获,若解析失败则清理多余字符
try:
data = response.json()
except json.decoder.JSONDecodeError:
# 清理JSON字符串前后的多余字符(如百度有时会加")]}'")
clean_text = response.text.lstrip(")]}'").strip()
data = json.loads(clean_text)
# 提取原图链接(优先级:replaceUrl[0].ObjURL > ObjURL > hoverURL)
image_urls = []
for item in data.get("data", []):
if not item: # 过滤空数据
continue
# 按优先级获取原图链接
if "replaceUrl" in item and len(item["replaceUrl"]) > 0:
img_url = item["replaceUrl"][0]["ObjURL"]
elif "ObjURL" in item:
img_url = item["ObjURL"]
elif "hoverURL" in item:
img_url = item["hoverURL"]
else:
continue
# 过滤无效链接(仅保留http/https开头的图片链接)
if img_url.startswith(("http://", "https://")):
image_urls.append(img_url)
logger.info(f"✅ 第{page_pn//PER_PAGE + 1}页获取到{len(image_urls)}个有效图片链接")
return image_urls
except requests.exceptions.RequestException as e:
logger.error(f"❌ 第{page_pn//PER_PAGE + 1}页请求失败:{str(e)}")
return []
def download_image(img_url, img_index):
"""下载单张图片到本地,返回是否下载成功"""
# 提取图片后缀(优先从URL获取,默认.jpg)
suffix = img_url.split(".")[-1].lower()
if suffix not in ["jpg", "jpeg", "png", "gif", "bmp"]:
suffix = "jpg"
# 图片保存路径(避免重名,用“关键词+序号”命名)
img_path = os.path.join(SAVE_DIR, f"{KEYWORD}_{img_index:04d}.{suffix}")
try:
# 下载图片(添加请求头,避免部分网站拒绝爬虫请求)
img_headers = HEADERS.copy()
img_headers["Referer"] = img_url # 部分图片需要Referer验证
response = requests.get(
url=img_url,
headers=img_headers,
timeout=TIMEOUT,
stream=True, # 流式下载,适合大文件
verify=False
)
response.raise_for_status()
# 保存图片到本地(二进制写入)
with open(img_path, "wb") as f:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
logger.success(f"📥 下载成功:{img_path}")
return True
except requests.exceptions.RequestException as e:
logger.warning(f"❌ 下载失败(索引{img_index}):{str(e)}")
return False
# ---------------------- 核心爬取逻辑(无需修改) ----------------------
def crawl_baidu_images():
"""完整爬取流程:创建目录→循环请求→下载图片→统计结果"""
# 1. 创建保存目录
create_save_dir()
# 2. 初始化变量
total_downloaded = 0 # 已下载图片数量
page_pn = 0 # 初始页码参数(pn=0表示第1页)
# 3. 循环爬取,直到达到目标数量或无更多图片
while total_downloaded < TOTAL_NUM:
# 获取当前页图片链接
image_urls = get_image_urls(page_pn)
if not image_urls:
logger.warning(f"⚠️ 未获取到更多图片,爬取可能提前结束")
break
# 下载当前页图片
for img_url in image_urls:
if total_downloaded >= TOTAL_NUM:
break # 达到目标数量,停止下载
# 下载图片(索引从1开始)
success = download_image(img_url, total_downloaded + 1)
if success:
total_downloaded += 1
# 单张图片下载间隔(0.3-0.8秒,避免高频请求)
time.sleep(random.uniform(0.3, 0.8))
# 每页爬取完成后,暂停随机时间(反爬关键)
time.sleep(random.uniform(*DELAY))
# 更新下一页的pn参数(每次加PER_PAGE)
page_pn += PER_PAGE
# 4. 爬取完成,统计结果
logger.info("="*50)
logger.info(f"🎉 爬取结束!")
logger.info(f"📊 目标爬取:{TOTAL_NUM}张")
logger.info(f"📊 成功下载:{total_downloaded}张")
logger.info(f"📊 保存路径:{os.path.abspath(SAVE_DIR)}")
logger.info("="*50)
# ---------------------- 启动爬取(运行这里即可) ----------------------
if __name__ == "__main__":
# 关闭requests的SSL警告(避免控制台刷屏)
requests.packages.urllib3.disable_warnings()
# 启动爬取
crawl_baidu_images()
代码运行步骤(新手友好)
安装依赖(终端输入):
pip install requests loguru -i https://pypi.tuna.tsinghua.edu.cn/simple
修改配置参数(KEYWORD 改关键词,TOTAL_NUM 改爬取数量);运行代码:python 百度图片爬虫.py(保存代码时用这个文件名);查看结果:代码所在目录会生成 百度图片_关键词 文件夹,图片已批量保存。
额外避坑:新手容易忽略的4个细节
关键词URL编码:必须用 quote(KEYWORD) 处理中文关键词(如“猫咪”→%E7%8C%AB%E5%92%AA),否则请求会返回空数据;JSON解析失败:百度返回的JSON可能带多余字符(如 )]}'"),代码中已加 clean_text 处理,避免解析报错;图片后缀处理:有些链接没有明确后缀,默认保存为 .jpg,避免文件格式错误;SSL验证警告:代码中 verify=False 关闭SSL验证(部分环境会因证书问题报错),同时用 disable_warnings() 屏蔽警告,不影响运行。
常见问题排查(爬取失败必看)
1. 爬不到数据,返回空链接?
原因:关键词未编码、请求头缺失 Referer、pn 参数设置错误;解决:确保 word=quote(KEYWORD)、Referer="https://image.baidu.com/"、pn 每次加 PER_PAGE(如60)。
2. 下载失败,提示“403 Forbidden”?
原因:图片链接需要 Referer 验证,或IP被限制;解决:代码中已给图片下载添加 img_headers["Referer"] = img_url,若仍失败,暂停10分钟再爬,或更换IP。
3. 爬取数量达不到目标?
原因:百度对单关键词爬取数量有限制(一般≤1000张),或部分链接失效;解决:降低 TOTAL_NUM(如改为500),或换关键词爬取。
4. 图片打开模糊?
原因:hd 参数设为0(标清);解决:确保 params["hd"] = 1(高清图),且提取的是 replaceUrl[0].ObjURL 等原图链接。
合规性说明(重要!)
遵守百度 robots.txt 协议:百度图片允许非商业、个人学习用途的爬取,禁止大规模、高频次爬取;控制爬取频率:代码中已加限速,请勿删除 time.sleep 相关代码,避免给百度服务器造成压力;数据用途:爬取的图片仅用于个人学习、研究,不得用于商业用途、二次创作或非法传播(注意图片版权!)。
总结
新手爬百度图片的核心是“绕开静态HTML,抓准API接口+处理反爬+提取原图”,改完本文提到的3个核心错,就能稳定批量保存高清图片。
这套思路可复用在其他图片网站(如必应图片、360图片),只需修改API接口和参数即可。如果想进一步优化,可添加代理IP池、多线程下载(新手不建议,容易触发反爬)、图片去重等功能!