最近头条上经常给我推荐一些老照片,虽然说以前也下载过,想想都是手工下载的,现在再遇到照片集怎么办了,当然是通过软件一次性把页面上的全部下载回来啦,懂的自然懂。
用C#来实现一下,首先使用HttpClient来获取网页的html,在获取前得给HttpClient加个cookie,不然无法打开页面,然后再使用HtmlAgilityPack解析出网页中的图片地址,最后通过多线程来把图片下载到本地,速度还是不错的。
主要是可以实现显示网页而获取到网页html,比显示出来再下载要快很多。
界面设计
界面按下图拖放就行了,一个输入网页的地方,点击打开按钮后会把获取到的图片地址保存在表格中,然后就可以进行下载了。
这里通过元素ID来进行筛选,只提取元素ID下的图片地址,有些图片地址是src,有些则不是,需要自己根据实际情况进行调整。
你要打开的网页可能需要cookie才能打开,这里例举一个最简单的cookie,包含一个名称和值,填入后在打开网页时就自动带上了。
软件会获取网页的标题来做为文件夹的名称,也可以手动设置文件夹名称。
imgHelp类
用来处理一些琐事的函数,功能很简单。
///
/// 获取扩展名
///
///
///
public static string GetExtType(string contentType)
{
switch (contentType)
{
case "image/jpeg":
return "jpg";
case "image/png":
return "png";
case "image/gif":
return "gif";
case "image/bmp":
return "bmp";
case "image/webp":
return "webp";
default:
return string.Empty; // 未知类型
}
}
///
/// 正则表达式判断是否是一个网址
///
///
///
public static bool IsValidUrl(string input)
{
// 正则表达式匹配常见的网址格式
string pattern = @"^(https?|ftp):\/\/([^\s\/$.?#].[^\s]*)$";
return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase);
}
///
/// 正则表达式:匹配中文字符
///
///
///
public static MatchCollection MatchChineseCharacters(string input)
{
// 正则表达式:匹配中文字符
string pattern = @"[\u4e00-\u9fa5]";
return Regex.Matches(input, pattern);
}
///
/// 提取标题,去除字符串中的符号、括号内内容,返回长度不超过10个字符的标题
///
///
///
public static string GetTitle(string input)
{
StringBuilder sb = new StringBuilder();
bool inBracket = false; // 标记是否在括号内
foreach (char c in input)
{
if (c == '(')
{
inBracket = true; // 进入括号内
}
else if (c == ')')
{
inBracket = false; // 离开括号内
}
else if (c == '【')
{
inBracket = true; // 进入括号内
}
else if (c == '】')
{
inBracket = false; // 离开括号内
}
else if (c == '(')
{
inBracket = true; // 进入括号内
}
else if (c == ')')
{
inBracket = false; // 离开括号内
}
else if (c == '[')
{
inBracket = true; // 进入括号内
}
else if (c == ']')
{
inBracket = false; // 离开括号内
}
else if (c == '-')
{
inBracket = true; // 进入括号内
}
else if (!inBracket)
{
sb.Append(c); // 仅在不在括号内时添加字符到结果中
}
}
string pattern = @"[^\u4e00-\u9fa5]";
string result = Regex.Replace(sb.ToString(), pattern, "");
if (result.Length > 15)
{
result = result.Substring(0, 15);
}
return result;
}
打开网页
打开网页时,先给设置一个cookie,一般新闻类网页不需要cookie就能浏览,获取到html代码后交给HtmlAgilityPack进行解析,并保存到表格中
string url = txt_url.Text.Trim().ToString();
string ele_id=txt_ysid.Text.Trim().ToString();
string ele_src=txt_src.Text.Trim().ToString();
string cookie_name=txt_cookie_name.Text.Trim().ToString();
string cookie_value=txt_cookie_value.Text.Trim().ToString();
DG_show.Rows.Clear();
btn_down.Enabled = false;
txt_title.Text = "";
if (ImgHelp.IsValidUrl(url))
{
alert1.Text = "打开网页,图片获取中...";
// 创建 CookieContainer
var cookieContainer = new CookieContainer();
// 创建一个 Cookie 并设置过期时间
var cookie = new Cookie
{
Name = cookie_name,
Value = cookie_value,
Domain = new Uri(url).Host,
Path = "/",
Expires = DateTime.Now.AddDays(1)// 设置过期时间为 1 天后
};
// 将 Cookie 添加到 CookieContainer
cookieContainer.Add(cookie);
// 创建 HttpClient 并绑定 CookieContainer
var handler = new HttpClientHandler
{
CookieContainer = cookieContainer
};
using (HttpClient client = new HttpClient(handler))
{
// 发送 GET 请求
HttpResponseMessage response = await client.GetAsync(url);
// 确保请求成功
response.EnsureSuccessStatusCode();
// 读取响应内容
string htmlContent = await response.Content.ReadAsStringAsync();
// 使用 HtmlAgilityPack 解析 HTML
HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
htmlDoc.LoadHtml(htmlContent);
//获取页面的标题
var titleNode = htmlDoc.DocumentNode.SelectSingleNode("//title");
if (titleNode != null)
{
string s=titleNode.InnerText;
Title=ImgHelp.GetTitle(s).Trim();
}
// 获取指定 id 的元素
var containerNode = htmlDoc.GetElementbyId(ele_id);
if (containerNode != null)
{
// 获取该元素下的所有
标签
var imgNodes = containerNode.SelectNodes(".//img");
if (imgNodes != null)
{
// 遍历所有
标签并输出 src 属性
foreach (var imgNode in imgNodes)
{
string imgSrc = imgNode.GetAttributeValue(ele_src, string.Empty);
if (!string.IsNullOrEmpty(imgSrc))
{
DG_show.Rows.Add(imgSrc, "");
}
}
}
}
}
alert1.Text = "获取完成";
txt_title.Text = Title;
btn_down.Enabled = true;
}
else
{
MessageBox.Show("输入的似乎不是一个正确的网址,需要包含http部分!", "错误提醒!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
批量下载图片
//下载图片
btn_down.Enabled = false;
int num = DG_show.Rows.Count;
if (num < 1)
{
MessageBox.Show("表格中没有需要下载的图片内容!", "错误提醒!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
//检查目录是否存在,不存在则创建一个
Title = txt_title.Text.Trim().ToString();
string img_path = imgSavePath.Trim() + "\\" + Title.Trim();
openPath = img_path;
if (!Directory.Exists(img_path))
{
Directory.CreateDirectory(img_path);
}
////启用线程池,进行多线程下载
string url = "";
Task[] tasks = new Task[num];
for (int i = 0; i < num; i++)
{
ImageDown imgdown = new ImageDown();
url = DG_show.Rows[i].Cells["图片地址"].Value.ToString();
imgdown.url = url;
imgdown.index = i;
tasks[i] = Task.Run(() => DownloadImageAsync(imgdown));
}
await Task.WhenAll(tasks);
MessageBox.Show($"图片已经下载完成,默认保存在{imgSavePath}目录下!", "完成提醒!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
btn_down.Enabled = true;
private async Task DownloadImageAsync(ImageDown imageDown)
{
string imageUrl = imageDown.url;
int index = imageDown.index;
string img_path = imgSavePath.Trim() + "\\" + Title.Trim();
try
{
if (imageUrl.StartsWith("data:image"))
{
}
else
{
string cookie_name = txt_cookie_name.Text.Trim().ToString();
string cookie_value = txt_cookie_value.Text.Trim().ToString();
// 创建 CookieContainer
var cookieContainer = new CookieContainer();
// 创建一个 Cookie 并设置过期时间
var cookie = new Cookie
{
Name = cookie_name,
Value = cookie_value,
Domain = new Uri(imageUrl).Host,
Path = "/",
Expires = DateTime.Now.AddDays(1)// 设置过期时间为 1 小时后
};
// 将 Cookie 添加到 CookieContainer
cookieContainer.Add(cookie);
// 创建 HttpClient 并绑定 CookieContainer
var handler = new HttpClientHandler
{
CookieContainer = cookieContainer
};
// 处理普通图片,但是图片下载不下来,试着用另一个方法实现
using (HttpClient client = new HttpClient(handler))
{
//获取扩展名
var headResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Head, imageUrl));
string contentType = headResponse.Content.Headers.ContentType.MediaType;
string ext = ImgHelp.GetExtType(contentType);
byte[] imageData = await client.GetByteArrayAsync(imageUrl);
HttpResponseMessage response = await client.GetAsync(imageUrl);
response.EnsureSuccessStatusCode(); // 确保请求成功
string fileName = index.ToString() + "." + ext;
string savePath = Path.Combine(img_path, fileName).Trim();
using (FileStream fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write))
{
await response.Content.CopyToAsync(fileStream);
}
updateGridView(index, Color.GreenYellow); //更新DataGridView
}
}
}
catch (Exception ex)
{
updateGridView(index, Color.Red);//更新DataGridView
Console.WriteLine(ex.Message.ToString());
}
}
private void updateGridView(int i, Color c)
{
if (DG_show.InvokeRequired)
{
DG_show.BeginInvoke(new Action(updateGridView), i, c);
}
else
{
DG_show.Rows[i].DefaultCellStyle.BackColor = c;
}
}
运行效果
输入一个搜狐的新闻网页进行测试,发现内容那一块的元素没有ID,全是class,好在最外端有个ID,直接填入,然后点击打开。
打开网页后获取到了一些图片的地址,并且也获取到了网页的标题,然后下载图片吧。
红色的图片是下载失败的,绿色的是成功的,这是因为有些图片地址不是标准的url格式导致的。