百度图片爬虫实战:从原理到批量下载的完整方案
发布时间:2026/10/3 14:45:19 锦皓数字建站

1. 项目概述1.1 这个项目到底在解决什么问题先说结论所谓“爬取百度图片”核心要解决的并不是“怎么下载一张图”而是“怎么把搜索引擎里的批量图片结果变成你能自由使用的本地素材库”。我最早接触到这个需求是在做一次电商详情页素材整理的时候。运营那边给的图东一张西一张分辨率参差不齐风格也不统一整理到崩溃之后我第一反应就是与其手动翻百度图片一篇一篇存不如直接写个脚本把搜索关键词的图片结果批量拉下来再按尺寸、格式筛一遍。于是就有了这个项目。这个项目适合谁三类人比较刚需自媒体/新媒体运营需要大量配图素材但不想一张张手动下载数据爱好者/入门爬虫学习者想找一个“不涉及复杂登录、反爬门槛低、但又有真实工程细节”的练手项目设计师/电商从业者需要快速收集某个风格的参考图或同类竞品图做视觉调研。它解决的问题很朴素把“人工搜索→逐张保存→手动重命名”这条链路替换成“输入关键词→自动抓取→自动筛选→批量落地”。看起来只是省了鼠标点击实际上省掉的是你反复切换页面、整理文件、核对图片是否可用的隐性时间成本。1.2 技术栈与整体思路先给一个最常用的技术组合适合绝大多数场景组件选型说明语言Python 3.8生态成熟爬虫库和解析库最全请求库requests简单直接配合模拟UA即可解析库re / BeautifulSoup4百度图片反爬力度较弱正则提取很直接数据存储本地文件系统按关键词建目录文件有序命名并发控制ThreadPoolExecutor提升下载速度但要控制线程数整体思路也不复杂百度图片的页面本身是一个动态加载的网页真实的图片URL藏在它内部JavaScript变量里。我们不需要模拟浏览器去等它渲染直接请求页面源码然后用正则把那个变量里的URL列表抠出来再逐个下载。这个思路听起来简单但真正动手时你会发现里面有大量细节值得抠请求头少了Referer图片可能403、URL里的转义字符需要解码、返回的图片格式可能是webp需要转jpg……这些坑我在后面会一一展开。先往下看流程再讲实操。2. 核心原理拆解百度图片是如何加载图片的2.1 页面源码里的“藏宝库”很多人第一次写爬虫习惯用Selenium去模拟浏览器然后等图片懒加载完成后再去截取网页里的img标签。这样做不是说不行而是又慢又脆——每张图都要等浏览器渲染反爬稍微加强一点就会失效而且内存占用特别大。我实际测试后发现百度图片的搜索结果页在HTML源码里就有一段类似这样结构的数据img classmain_img src...>pip install requests beautifulsoup4 tqdm如果你严格跟我走正则路线BeautifulSoup其实可以不用但我还是建议装上因为后面你做数据清洗或者扩展别的功能时会用得上。tqdm是用来显示下载进度的批量下载几百上千张图时看着进度条慢慢往前走心里有底。3.2 第一版实现从网页提取图片URL直接上核心代码。我简化了一些细节保留最重要的逻辑import requests import re import os from urllib.parse import unquote def get_image_urls(keyword, page_num1): 从百度图片搜索页提取高清图片URL headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://image.baidu.com/ } # 百度新版搜索页URL url fhttps://image.baidu.com/search/index?tnbaiduimageword{keyword} resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 html resp.text # 提取data-imgurl字段中的图片地址 pattern re.compile(rdata-imgurl:(.*?)) img_urls pattern.findall(html) # 解码unicode转义和url编码 img_urls [unquote(url.replace(\\/, /)) for url in img_urls] # 去重、过滤不完整链接 img_urls list(set([url for url in img_urls if url.startswith(http)])) return img_urls这段代码的核心就三件事带Headers发起请求 → 正则匹配data-imgurl字段 → 解码并去重。为什么Headers里要带Referer因为百度图片的部分图源做了防盗链不带Referer直接请求图片服务器返回的会是一张“403禁止访问”的默认图这是我用第一版踩过最大的坑。3.3 实现批量下载与文件管理拿到URL列表之后下载就相对简单了。但这里也需要讲几个细节def download_images(img_urls, save_dir, keyword): 批量下载图片到本地 if not os.path.exists(save_dir): os.makedirs(save_dir) success_count 0 for idx, img_url in enumerate(img_urls): try: headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://image.baidu.com/ } img_resp requests.get(img_url, headersheaders, timeout15) # 判断响应是否为图片 if image not in img_resp.headers.get(Content-Type, ): continue # 根据content-type动态生成扩展名 ext img_resp.headers.get(Content-Type).split(/)[-1] if ext jpeg: ext jpg elif ext webp: ext jpg # 统一转为jpg方便后续使用 file_name f{keyword}_{idx1}.{ext} file_path os.path.join(save_dir, file_name) with open(file_path, wb) as f: f.write(img_resp.content) success_count 1 except Exception as e: print(f下载失败: {img_url}错误: {e}) continue return success_count下载器的逻辑同样不复杂但有三个容易被忽略的小细节值得单独拎出来说第一Content-Type校验。有些时候抓到的URL虽然以.jpg结尾但服务器实际上给你返回了一个HTML错误页。用Content-Type判断是“image”开头才保存可以避免一堆乱码文件落地。第二webp转jpg。百度图片里大量压缩图是webp格式如果你下载下来直接给PS或PR用很多老版本软件打不开。我的做法是直接改名虽然本质上没有转码但扩展名是.jpg后绝大多数看图软件都能正常识别webp本身兼容性已经很好这里主要是为了统一。第三文件命名。我用的是“关键词_序号.扩展名”的格式。这个格式丑是丑但做批量素材时关键词前置可以让你在文件夹里按名称排序时同一主题的图自然聚在一起。3.4 进阶版翻页与多线程并发上面两段代码属于“开箱即用”的版本但真正面对几百上千张图的需求单线程下载速度会让你怀疑人生。所以我在后续迭代里加了两个优化分页接口拉取 多线程下载。分页接口的核心代码如下def get_image_urls_by_api(keyword, total60): 通过后端接口分页获取更多图片 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://image.baidu.com/ } all_urls [] for pn in range(0, total, 30): api_url https://image.baidu.com/search/acjson params { tn: resultjson_com, word: keyword, pn: pn, rn: 30, } try: resp requests.get(api_url, paramsparams, headersheaders, timeout10) data resp.json() # 解析data字段获取每张图的thumbURL for item in data.get(data, []): if item.get(thumbURL): all_urls.append(item[thumbURL]) except Exception as e: print(f第{pn}条请求失败: {e}) continue return list(set(all_urls))多线程下载就更简单了Python的ThreadPoolExecutor一行就能实现from concurrent.futures import ThreadPoolExecutor def multithread_download(url_list, save_dir, keyword, max_workers10): with ThreadPoolExecutor(max_workersmax_workers) as executor: futures [] for i, url in enumerate(url_list): future executor.submit(download_single_image, url, i, save_dir, keyword) futures.append(future) for future in futures: future.result()这里提醒一句线程数不要贪多。我试过50个线程并发下载结果被百度图片服务器限流IP临时被封了几个小时。10个线程足够下载速度也已经远超手动操作完全是量级上的差异。3.5 主流程入口写完上面的函数主入口就非常清爽了if __name__ __main__: keyword 特斯拉 Model Y # 替换成你想要的关键词 save_dir f./images/{keyword} print(f开始爬取关键词: {keyword}) urls get_image_urls_by_api(keyword, total120) print(f共获取到 {len(urls)} 张图片URL) if urls: multithread_download(urls, save_dir, keyword, max_workers10) print(下载完成)整个项目跑起来后你会看到控制台刷出一行行下载记录进度条推进然后本地文件夹里出现一批以关键词为前缀的图片文件。说实话第一次完整跑通这个脚本那种“搜索引擎变成了我的素材库”的感觉比写很多复杂业务代码都爽。4. 常见问题与排查技巧实录4.1 爬取结果为空一张图都拿不到这是新手最容易遇到的问题。我把排查顺序列成一个速查表现象可能原因排查方法返回的URL列表为空页面结构变化正则匹配失效打开浏览器开发者工具查看搜索页HTML源码确认>def download_single_image(img_url, idx, save_dir, keyword, retries3): for attempt in range(retries): try: headers { User-Agent: Mozilla/5.0, Referer: https://image.baidu.com/ } resp requests.get(img_url, headersheaders, timeout15) if image not in resp.headers.get(Content-Type, ): return False # 保存逻辑... return True except Exception: time.sleep(2) # 间隔一下避免连续失败 continue return False4.3 URL里全是转义字符下载出来文件损坏这个问题主要出在百度搜索页的数据上有些图片地址是经过JS编码的比如\x26、\u002F这类。我处理的方法是先把字符串里的反斜杠替换成正斜杠再做一次URL解码from urllib.parse import unquote clean_url unquote(img_url.replace(\\/, /))如果你在解析时发现URL里还带其他转义符比如\u0026可以用clean_url clean_url.encode(utf-8).decode(unicode_escape)注意这行代码放在替换反斜杠之前还是之后顺序不同结果会有差异我推荐顺序是“先替换\\/→ 再解unicode_escape → 最后unquote”。你可以自己打印中间结果对比一下。4.4 下载速度太慢怎么办如果你已经用了多线程还是觉得慢可以考虑换思路只下载缩略图而非原图。百度图片的缩略图地址thumbURL通常带特定的尺寸参数比如w500h500你把参数调小图片体积会大幅下降下载速度自然就上来了。代价是分辨率不够高看你自己的用途取舍。5. 延伸场景与规范化建议5.1 爬下来的图片怎么归类管理我见过很多人爬完图就扔在文件夹里不管几个月后要用时根本找不到。这里分享一个我自己的管理习惯目录结构data/关键词/子分类/图片文件文件命名关键词_日期_序号.扩展名同步维护一个CSV清单记录每张图的来源URL、下载时间、尺寸、格式CSV清单可以用下面这个方式顺手生成import csv def generate_manifest(img_urls, save_dir, keyword): with open(f{save_dir}/manifest.csv, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow([序号, 文件名, 来源URL]) for idx, url in enumerate(img_urls): writer.writerow([idx1, f{keyword}_{idx1}, url])这样做的价值在于图片如果有版权纠纷或者需要溯源时你随时可以找到原始来源。爬取公开图片做个人学习参考是常见操作但如果用于商业项目务必逐张确认版权许可这是职业习惯也是底线。5.2 能不能扩展成通用图片爬虫这个项目的核心逻辑完全适用于其他同类搜索引擎或图库网站区别主要在于URL结构和数据字段的解析。比如有的网站图片数据存在>
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。