C#中使用HtmlAgilityPack提取网站的图片并下载

最近头条上经常给我推荐一些老照片,虽然说以前也下载过,想想都是手工下载的,现在再遇到照片集怎么办了,当然是通过软件一次性把页面上的全部下载回来啦,懂的自然懂

用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格式导致的。

原文链接:,转发请注明来源!