基于C#的百度图片批量下载工具
随着互联网视觉内容的爆发式增长,批量下载图片成为许多开发者、设计师和内容运营人员的常见需求。百度图片作为国内最大的图片搜索引擎之一,提供了海量的素材。本文详细介绍如何利用C#语言构建一个稳定的百度图片批量下载工具,涵盖从API分析到多线程下载的完整实现思路与关键代码。
一、工具功能与设计目标
该工具的核心功能包括:
输入关键词和页码,自动获取百度图片搜索结果
解析图片的真实下载链接(而非缩略图)
支持多线程并行下载,提高效率
自动处理反爬机制,如UserAgent、Referer、IP代理池等
下载进度显示与错误重试
设计上采用分层架构,分为请求模块、解析模块、下载模块和配置模块,便于维护和扩展。
二、百度图片接口分析
百度图片的搜索结果通过Ajax动态加载。通过浏览器开发者工具(F12)观察网络请求,可以发现其核心接口为:
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=0&hd=&latest=©right=&word={关键词}&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn={页码起始}&rn=30&gsm=1e&1584609814081=参数说明:pn表示本页第一张图的序号(通常从0开始,每页30张),rn表示每页条数(默认为30)。返回的JSON数据中包含data数组,每个元素包含thumbURL(缩略图)和middleURL(中图)等字段,但真正的原图链接通常隐藏在replaceUrl或hoverURL中,或需要通过objURL进行解码。
根据最新接口(2023年后),百度图片对原图链接做了进一步的混淆,常见方式为:objURL属性中包含了带转义字符的URL,需要将/替换为/,且部分URL中包含中文等特殊字符,需进行UrlDecode处理。因此解析步骤至关重要。
三、C#实现思路
整体流程如下:
根据关键词和页码构造请求URL
发送HTTP GET请求,携带合适的请求头(UserAgent、Referer等)
将返回的JSON字符串反序列化为对象模型
遍历
data数组,提取每个图片的objURL字段对
objURL进行URL解码和转义处理,得到真实下载链接使用多线程下载器并发下载图片到本地文件夹
下载失败时自动重试,并记录错误日志
四、关键代码实现
4.1 请求与解析模块
以下代码演示如何获取JSON并提取图片URL。注意需处理objURL中的反斜杠转义和中文编码。
using System;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
public class BaiduImageFetcher
{
private static readonly string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
private static readonly string Referer = "https://image.baidu.com/";
public static List<string> FetchImageUrls(string keyword, int page)
{
List<string> urls = new List<string>();
int pn = page * 30;
string url = $"https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord={Uri.EscapeDataString(keyword)}&cl=2&lm=-1&ie=utf-8&oe=utf-8&word={Uri.EscapeDataString(keyword)}&pn={pn}&rn=30&gsm=1e";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.UserAgent = UserAgent;
request.Referer = Referer;
request.Accept = "application/json, text/plain, */*";
request.Method = "GET";
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (System.IO.Stream stream = response.GetResponseStream())
using (System.IO.StreamReader reader = new System.IO.StreamReader(stream, Encoding.UTF8))
{
string jsonString = reader.ReadToEnd();
JObject obj = JObject.Parse(jsonString);
JArray dataArray = (JArray)obj["data"];
foreach (JToken item in dataArray)
{
if (item["objURL"] != null)
{
string rawUrl = item["objURL"].ToString();
// 处理反斜杠转义:将/替换为/
rawUrl = rawUrl.Replace("\/", "/");
// 处理中文等特殊字符
rawUrl = Uri.UnescapeDataString(rawUrl);
urls.Add(rawUrl);
}
}
}
return urls;
}
}4.2 多线程下载模块
使用Parallel.ForEach实现并行下载,并添加重试机制。注意控制并发数以避免被封。
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net;
using System.Threading.Tasks;
public class ImageDownloader
{
private static readonly string DownloadPath = Environment.CurrentDirectory + "\downloads\";
private static readonly int MaxRetry = 3;
private static readonly int MaxParallelism = 8;
public static void DownloadImages(List<string> urls)
{
if (!Directory.Exists(DownloadPath))
Directory.CreateDirectory(DownloadPath);
object lockObj = new object();
int total = urls.Count;
int completed = 0;
Parallel.ForEach(urls, new ParallelOptions { MaxDegreeOfParallelism = MaxParallelism }, (url, state, index) =>
{
string fileName = $"image_{index + 1}.jpg";
string filePath = Path.Combine(DownloadPath, fileName);
for (int retry = 0; retry < MaxRetry; retry++)
{
try
{
using (WebClient client = new WebClient())
{
client.Headers.Add("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");
client.Headers.Add("Referer", "https://image.baidu.com/");
client.DownloadFile(url, filePath);
}
lock (lockObj)
{
completed++;
Console.WriteLine($"[{completed}/{total}] 下载完成: {fileName}");
}
break; // 下载成功,跳出重试循环
}
catch (Exception ex)
{
Console.WriteLine($"下载失败 [{url}]: {ex.Message},剩余重试次数: {MaxRetry - retry - 1}");
if (retry == MaxRetry - 1)
{
lock (lockObj)
{
Console.WriteLine($"放弃下载: {url}");
}
}
System.Threading.Thread.Sleep(1000 * (retry + 1)); // 递增等待
}
}
});
Console.WriteLine("所有下载任务完成。");
}
}4.3 主入口控制台程序
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
Console.Write("请输入搜索关键词: ");
string keyword = Console.ReadLine();
Console.Write("请输入要下载的页数(每页约30张): ");
int pages = int.Parse(Console.ReadLine());
List<string> allUrls = new List<string>();
for (int page = 0; page < pages; page++)
{
Console.WriteLine($"正在获取第{page + 1}页图片链接...");
var urls = BaiduImageFetcher.FetchImageUrls(keyword, page);
allUrls.AddRange(urls);
System.Threading.Thread.Sleep(2000); // 请求间隔,避免被封
}
Console.WriteLine($"共获取到{allUrls.Count}张图片链接,开始下载...");
ImageDownloader.DownloadImages(allUrls);
Console.WriteLine("按任意键退出...");
Console.ReadKey();
}
}五、注意事项与优化建议
反爬策略:百度图片对频繁请求有IP限制,建议每页间隔至少1~2秒,并可添加代理IP池随机切换。
URL解码:部分
objURL可能包含%2F等编码字符,使用Uri.UnescapeDataString即可。文件命名:为避免重复,可使用URL的MD5值作为文件名,或保留原始链接中的文件名。
异常处理:网络波动可能导致下载失败,务必备份重试机制,并将错误URL记录到日志文件。
并发控制:建议并发数不超过10,否则容易触发IP黑名单。同时可使用
SemaphoreSlim精确控制并发数量。图片格式检测:部分图片链接返回的Content-Type可能为gif、png等,可根据响应头动态设置文件扩展名。
六、常见问题FAQ
| 问题 | 原因与解决方法 |
|---|---|
| JSON解析报错“Unexpected character” | 可能是返回内容被反爬机制干扰(如弹验证码)。尝试更换Cookie或使用Selenium模拟浏览器 |
| 下载的图片无法打开 | URL可能指向了缩略图或防盗链图片。检查objURL处理后是否完整,以及是否缺少Referer头 |
| 下载速度很慢 | 网络限制或服务器限流。可尝试减小并发数,或使用多线程同时下载多个任务并设置合理的超时时间 |
| 获取到的URL数量远小于预期 | 百度图片分页有实际限制,部分页面可能返回空数据。可尝试调整pn参数偏移量 |
七、完整源码与扩展
上述代码已构成一个可运行的C#控制台应用程序。若需图形界面,可使用WinForms或WPF包装。另外,为应对百度接口变更,建议定期抓包验证objURL的解析规则。在实际项目中,可考虑将配置(如UserAgent、延迟时间、代理列表)写入app.config或JSON文件,实现灵活配置。
本文提供的工具仅用于学习和研究目的,请遵守百度图片的robots.txt协议和相关法律法规,不要对系统造成过度负担。