• C# 网络爬虫+HtmlAgilityPack+Xpath+爬虫工具类的封装的使用


    目录

    1 工具准备

    2 思路准备

    3 附加知识准备——XPath

    简述

    看看例子

    用XPath来寻找标签

    获取所有同名的标签

    获取指定标签

    一个实例

    最后的补充

    4 代码实现

    5 爬虫工具类的封装

    6 使用爬虫工具类爬虫


    1 工具准备

    1  Visual Studio 需要安装包 HtmlAgilityPack

     

     2 命名空间的引入

    在新建的程序头顶加入

    1. using HtmlAgilityPack;
    2. using HtmlDocument = HtmlAgilityPack.HtmlDocument;

    3 注备好一双可以复制粘贴的小手,和一个还能跑的电脑,咯咯~

    2 思路准备

    图我已经给各位画好了,请参看

     

    3 附加知识准备——XPath

    简述

    XPath 是一门在 XML 文档中查找信息的语言,虽然是被设计用来搜寻 XML 文档的,但是它也能应用于 HTML 文档,并且大部分浏览器也支持通过 XPath 来查询节点。在 Python 爬虫开发中,经常使用 XPath 查找提取网页中的信息,因此 XPath 非常重要。

    XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是沿着路径(path)或者步(steps)来选取的。接下来介绍如何选取节点,首先了解一下常用的路径表达式,来进行节点的选取,如下表所示:

    表达式描述
    nodename选取此节点的所有子节点
    /从根节点选取
    //选择任意位置的某个节点
    .选取当前节点
    ..选取当前节点的父节点
    @选取属性

    看看例子

    bookstore选取 bookstore 元素的所有子节点。
    /bookstore

    选取根元素 bookstore。

    注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!

    bookstore/book选取属于 bookstore 的子元素的所有 book 元素。
    //book选取所有 book 子元素,而不管它们在文档中的位置。
    bookstore//book选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
    //@lang选取名为 lang 的所有属性。

    用XPath来寻找标签

    获取所有同名的标签

    我们只需要用:  //标签名

    即可,比如看下图我们使用了//ul寻找到了所有名为ul的标签了

    获取指定标签

    那么我们想要去选取具体的那个标签怎么办那?

    有童鞋可能会想到,直接加下表访问但是!!在有时候是行不通的,

    很简单,我们先将获取的的所以ul块对象存在数组中,然后使用下标访问就好了

    只是这些我们要在C#中进行操作了,不能直接在网络控制台上进行操作了,咯咯~

    /html/body/div[1]/div[2]/ul

    一个实例

     我们使用这串Xpath代码就可以获取到所有在指定位置下的li标签

    /html/body/div[1]/div[2]/ul/li

    最后的补充

    XPath 在进行节点选取的时候可以使用通配符*匹配未知的元素,同时使用操作符|一次选取多条路径,使用示例如下表所示。

    XPath路径表达式含义
    /bookstore/*选取 bookstore 元素的所有子元素
    //*选取文档中的所有元素
    //title[@*]选取所有带有属性的 title 元素
    //book/title 丨 //book/price选取 book 元素的所有 title 和 price 元素
    //title 丨 //price选取文档中的所有 title 和 price 元素
    /bookstore/book/title 丨 //price选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素

    4 代码实现

    其实有了上面的基础知识,就可以自由发挥了,我这里抛砖引玉一下。

    我们以美女图片大全_高清美女图片_性感美女写真_极品美女图片 - 美图131为例对MV图片的源地址,以及标签进行提取。

    以下代码我们只用理解逻辑以及方法即可,可能也有很多漏洞与BUG,但这些不是重点滴!

    1. public void getdata()
    2. {
    3. //----全网首发----//
    4. //建立实例化htmlweb对象用于加载,处理网页
    5. HtmlWeb htmlWeb = new HtmlWeb();
    6. //设置为Encoding.UTF8编码,防止乱码
    7. htmlWeb.OverrideEncoding = Encoding.UTF8;
    8. //初始化网页地址
    9. String init_url = @"https://www.meitu131.com/meinv/";
    10. 加载网页返回值为HtmlDocument类型,用var也行,哈哈
    11. HtmlDocument htmlDoc = htmlWeb.Load(init_url);
    12. //打印一下网页的HTML文档,看看效果
    13. //Console.WriteLine(htmlDoc.Text);
    14. //使用XPath定位元素
    15. string xpath = "";
    16. string init_xpath = "/html/body/div[1]/div[2]/ul/li";
    17. //获取本页所有li的节点数目,这便是本页图集的个数
    18. int new_page_sum = htmlDoc.DocumentNode.SelectNodes(init_xpath).Count;
    19. //对每个li节点进行提取,并拼接图集的网址
    20. for (int a_ = 0; a_ < new_page_sum; a_++)
    21. {
    22. xpath = "/html/body/div[1]/div[2]/ul/li[" + (a_ + 1) + "]/div[1]/a";
    23. init_img_src.Add("https://www.meitu131.com" + htmlDoc.DocumentNode.SelectSingleNode(xpath).Attributes["href"].Value.ToString());
    24. }
    25. //对每一页进行遍历,获取
    26. for (int b_ = 0; b_ < new_page_sum; b_++)
    27. {
    28. string temp_src = @init_img_src[b_];
    29. string temp_paxth_ = "//*[@id='main-wrapper']/div[2]/p/a/img";
    30. string temp_paxth = "//*[@id='pages']/a[1]";
    31. HtmlDocument htmlDoc_1 = htmlWeb.Load(temp_src);
    32. string c = htmlDoc_1.DocumentNode.SelectSingleNode(temp_paxth).InnerHtml.ToString();
    33. //获取当前图集所有页数page_sum[1]
    34. String[] page_sum = c.Split('/');
    35. for (int c_ = 0; c_ < 1; c_++)
    36. {
    37. string temp_url;
    38. if (c_ == 0)
    39. {
    40. temp_url = temp_src + "index.html";
    41. }
    42. else
    43. {
    44. temp_url = temp_src + "index_" + (c_ + 1) + ".html";
    45. }
    46. HtmlDocument htmlDoc_2 = htmlWeb.Load(temp_url);
    47. end_img_url.Add(htmlDoc_1.DocumentNode.SelectSingleNode(temp_paxth_).Attributes["src"].Value.ToString());
    48. end_img_name.Add(htmlDoc_1.DocumentNode.SelectSingleNode(temp_paxth_).Attributes["alt"].Value.ToString());
    49. }
    50. }
    51. textBox1.AppendText("数据获取完成,开始保存文件......");
    52. }

    5 爬虫工具类的封装

    为了更方便的爬虫,我对常用的方法进一步进行了封装,开箱即用。

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using System.Net;
    7. using System.IO;
    8. using System.Diagnostics;
    9. using HtmlAgilityPack;
    10. using System.Net.Http;
    11. namespace 爬虫
    12. {
    13. public static class pc_Help
    14. {
    15. ///
    16. /// Url网络资源下载
    17. ///
    18. /// 下载地址
    19. /// 资源名列表
    20. /// 资源Url列表
    21. /// 下载文件后缀(不加.)
    22. /// 毫秒运行时间 float
    23. public static string Download_Url(string Download_Path, List<string> resource_name, List<string> resource_url, string Download_Type)
    24. {
    25. try
    26. {
    27. string Download_Path_ = Download_Path;
    28. Stopwatch sw = new Stopwatch();
    29. sw.Start();
    30. int len = resource_name.Count;
    31. int num = 0;
    32. WebClient wb = new WebClient();
    33. DirectoryInfo info = new DirectoryInfo(Download_Path_);
    34. if (!info.Exists)
    35. {
    36. Directory.CreateDirectory(Download_Path_);
    37. }
    38. for (int d_ = 0; d_ < len; d_++)
    39. {
    40. Download_Path = $@"{Download_Path_}\{(num + 1)}{resource_name[d_]}.{Download_Type}";
    41. num++;
    42. wb.DownloadFile(resource_url[d_], Download_Path);
    43. }
    44. sw.Stop();
    45. return $"文件保存完成!耗时:{sw.ElapsedMilliseconds/1000}s\r\n";
    46. }
    47. catch (Exception e)
    48. {
    49. throw new Exception("保存数据出错", e);
    50. }
    51. }
    52. ///
    53. /// 从Url地址下载HTML页面
    54. ///
    55. ///
    56. ///
    57. public async static ValueTask LoadHtmlFromUrlAsync(string url)
    58. {
    59. //如果web不是空就异步下载html文档
    60. HtmlWeb web = new HtmlWeb();
    61. web.OverrideEncoding = Encoding.UTF8;
    62. return await web?.LoadFromWebAsync(url);
    63. }
    64. ///
    65. /// 获取单个节点的扩展方法
    66. ///
    67. /// 文档对象
    68. /// xPath路径
    69. ///
    70. public static HtmlNode GetSingleNode(this HtmlDocument htmlDocument, string xPath)
    71. {
    72. return htmlDocument?.DocumentNode?.SelectSingleNode(xPath);
    73. }
    74. ///
    75. /// 获取单个节点扩展方法
    76. ///
    77. /// 文档对象
    78. /// xPath路径
    79. ///
    80. public static HtmlNode GetSingleNode(this HtmlNode htmlNode, string xPath)
    81. {
    82. return htmlNode?.SelectSingleNode(xPath);
    83. }
    84. ///
    85. /// 获取多个节点扩展方法
    86. ///
    87. /// 文档对象
    88. /// xPath路径
    89. /// 一个列表
    90. public static HtmlNodeCollection GetNodes(this HtmlDocument htmlDocument, string xPath)
    91. {
    92. return htmlDocument?.DocumentNode?.SelectNodes(xPath);
    93. }
    94. ///
    95. /// 获取多个节点扩展方法
    96. ///
    97. /// 文档对象
    98. /// xPath路径
    99. /// 一个列表
    100. public static HtmlNodeCollection GetNodes(this HtmlNode htmlNode, string xPath)
    101. {
    102. return htmlNode?.SelectNodes(xPath);
    103. }
    104. ///
    105. /// 下载图片
    106. ///
    107. /// 地址
    108. /// 文件路径
    109. /// 存在即覆盖
    110. ///
    111. //同步完成时的ValueTask,<>里可以是任何类型
    112. public async static ValueTask<bool> DownloadImg(string url, string filpath)
    113. {
    114. HttpClient hc = new HttpClient();
    115. try
    116. {
    117. //字节流异步写入
    118. var bytes = await hc.GetByteArrayAsync(url);
    119. //存在即覆盖
    120. using (FileStream fs = File.Create(filpath))
    121. {
    122. fs.Write(bytes, 0, bytes.Length);
    123. }
    124. return File.Exists(filpath);
    125. }
    126. catch (Exception ex)
    127. {
    128. throw new Exception("下载图片异常", ex);
    129. }
    130. }
    131. }
    132. }

    6 使用爬虫工具类爬虫

    这边给个例子,还是以我们以美女图片大全_高清美女图片_性感美女写真_极品美女图片 - 美图131为例

    以下代码我们只用理解逻辑以及方法即可,可能也有很多漏洞与BUG,但这些不是重点滴!

    1. public async ValueTask<bool> getdata_()
    2. {
    3. try
    4. {
    5. HtmlDocument html_3 = await pc_Help.LoadHtmlFromUrlAsync(init_url);
    6. string stack_all_page_sum_string = pc_Help.GetSingleNode(html_3, "//*[@id='pages']/a[11]").Attributes["href"].Value.ToString();
    7. //正则匹配全站所有图集的个数
    8. Regex regex = new Regex(@"\d{2}");
    9. int stack_all_page_sum_int = Convert.ToInt32(regex.Match(stack_all_page_sum_string).ToString());
    10. if (pagesum < 24)
    11. {
    12. HtmlDocument htmlDoc;
    13. htmlDoc = await pc_Help.LoadHtmlFromUrlAsync(init_url);
    14. string xpath = "";
    15. //获取此页面上(具有多个图集封面的页面)的所有图集的初始页的数目
    16. int new_page_sum = pc_Help.GetNodes(htmlDoc, "/html/body/div[1]/div[2]/ul/li").Count;
    17. //拼接当前页面上(具有多个图集封面的页面)的所有图集的初始页的地址
    18. for (int a_ = 0; a_ < pagesum; a_++)
    19. {
    20. xpath = "/html/body/div[1]/div[2]/ul/li[" + (a_ + 1) + "]/div[1]/a";
    21. init_img_src.Add("https://www.meitu131.com" + pc_Help.GetSingleNode(htmlDoc, xpath).Attributes["href"].Value.ToString());
    22. }
    23. for (int b_ = 0; b_ < pagesum; b_++)
    24. {
    25. string temp_src = @init_img_src[b_];
    26. string temp_paxth_ = "//*[@id='main-wrapper']/div[2]/p/a/img";
    27. string temp_paxth = "//*[@id='pages']/a[1]";
    28. HtmlDocument htmlDoc_1 = await pc_Help.LoadHtmlFromUrlAsync(temp_src);
    29. string c = pc_Help.GetSingleNode(htmlDoc_1, temp_paxth).InnerHtml.ToString();
    30. //获取本图集所有页数page_sum[1]
    31. string[] page_sum = c.Split('/');
    32. //Convert.ToInt32(page_sum[1])
    33. //每个图集页面的拼接,与请求保存图片
    34. for (int c_ = 0; c_ < length; c_++)
    35. {
    36. string temp_url;
    37. if (c_ == 0)
    38. {
    39. temp_url = temp_src + "index.html";
    40. }
    41. else
    42. {
    43. temp_url = temp_src + "index_" + (c_ + 1) + ".html";
    44. }
    45. HtmlDocument htmlDoc_2 = await pc_Help.LoadHtmlFromUrlAsync(temp_url);
    46. string src = pc_Help.GetSingleNode(htmlDoc_2, temp_paxth_).Attributes["src"].Value.ToString();
    47. string alt = pc_Help.GetSingleNode(htmlDoc_2, temp_paxth_).Attributes["alt"].Value.ToString();
    48. end_img_url.Add(src);
    49. end_img_name.Add(alt);
    50. num++;
    51. textBox1.AppendText($"{num}-->{alt}-->{src}\r\n");
    52. }
    53. }
    54. }
    55. else
    56. {
    57. for (int x_ = 0; x_ < pagesum; x_++)
    58. {
    59. }
    60. ero = false;
    61. MessageBox.Show("待实现中....");
    62. }
    63. if (ero)
    64. {
    65. textBox1.AppendText("数据获取完成,正在保存文件......\r\n");
    66. }
    67. else {
    68. textBox1.AppendText("数据获取失败......\r\n");
    69. }
    70. return true;
    71. }
    72. catch (Exception ex)
    73. {
    74. throw new Exception("数据获取异常", ex);
    75. }
    76. }

  • 相关阅读:
    开发必备Liunx常用的几个命令
    ubuntu server 22.x 连接无线网络
    【吴恩达机器学习笔记】三、矩阵
    主成分分析法(数学建模)教授先生
    容器环境下php进程与bash进程树关系
    nginx学习(4)Nginx 配置高可用集群(主从配置)
    【Qt】Qt5.15、Qt6在线安装(使用国内源)
    二叉树题目:填充每个结点的下一个右侧结点指针 II
    图第三遍补充(各种算法与力扣)
    Idea中重构从精通到陌生
  • 原文地址:https://blog.csdn.net/qq_53679247/article/details/127117926