C#简化工作之实现网页爬虫获取数据

1、需求

想要获取网站上所有的气象信息,网站如下所示:

image-20231127193134632

目前总共有67页,随便点开一个如下所示:

image-20231127193254040

需要获取所有天气数据,如果靠一个个点开再一个个复制粘贴那么也不知道什么时候才能完成,这个时候就可以使用C#来实现网页爬虫获取这些数据。

2、效果

先来看下实现的效果,所有数据都已存入数据库中,如下所示:

image-20231127193726966

总共有4万多条数据。

3、具体实现

构建每一页的URL

第一页的网址如下所示:

image-20231127194211474

最后一页的网址如下所示:

image-20231127195622290

可以发现是有规律的,那么就可以先尝试构建出每个页面的URL

    // 发送 GET 请求
    string url = "https://cj.msa.gov.cn/xxgk/xxgkml/aqxx/qxyg/";
    HttpResponseMessage response = await httpClient.GetAsync(url);

    // 处理响应
    if (response.IsSuccessStatusCode)
    {
        string responseBody = await response.Content.ReadAsStringAsync();
        doc.LoadHtml(responseBody);
        //获取需要的数据所在的节点
        var node = doc.DocumentNode.SelectSingleNode("//div[@class=\"page\"]/script");
        string rawText = node.InnerText.Trim();
        // 使用正则表达式来匹配页数数据
        Regex regex = new Regex(@"\b(\d+)\b");
        Match match = regex.Match(rawText);
        if (match.Success)
        {
            string pageNumber = match.Groups[1].Value;
            Urls = GetUrls(Convert.ToInt32(pageNumber));
            MessageBox.Show($"获取每个页面的URL成功,总页面数为:{Urls.Length}");
        }

    }

 //构造每一页的URL
 public string[] GetUrls(int pageNumber)
 {
     string[] urls = new string[pageNumber];
     for (int i = 0; i < urls.Length; i++)
     {
         if (i == 0)
         {
             urls[i] = "https://cj.msa.gov.cn/xxgk/xxgkml/aqxx/qxyg/index.shtml";
         }
         else
         {
             urls[i] = $"https://cj.msa.gov.cn/xxgk/xxgkml/aqxx/qxyg/index_{i}.shtml";
         }
     }
     return urls;
 }

这里使用了HtmlAgilityPack

image-20231127195928840

HtmlAgilityPack(HAP)是一个用于处理HTML文档的.NET库。它允许你方便地从HTML文档中提取信息,修改HTML结构,并执行其他HTML文档相关的操作。HtmlAgilityPack 提供了一种灵活而强大的方式来解析和处理HTML,使得在.NET应用程序中进行网页数据提取和处理变得更加容易。

 // 使用HtmlAgilityPack解析网页内容
 var doc = new HtmlAgilityPack.HtmlDocument();
 doc.LoadHtml("需要解析的Html");
 //获取需要的数据所在的节点
var node = doc.DocumentNode.SelectSingleNode("XPath");

那么XPath是什么呢?

XPath(XML Path Language)是一种用于在XML文档中定位和选择节点的语言。它是W3C(World Wide Web Consortium)的标准,通常用于在XML文档中执行查询操作。XPath提供了一种简洁而强大的方式来导航和操作XML文档的内容。

构建每一天的URL

获取到了每一页的URL之后,我们发现在每一页的URL都可以获取关于每一天的URL信息,如下所示:

image-20231127201037439

可以进一步构建每一天的URL,同时可以根据a的文本获取时间,当然也可以通过其他方式获取时间,但是这种可以获取到11点或者17点。

代码如下所示:

    for (int i = 0; i < Urls.Length; i++)
    {
        // 发送 GET 请求
        string url2 = Urls[i];
        HttpResponseMessage response2 = await httpClient.GetAsync(url2);
        // 处理响应
        if (response2.IsSuccessStatusCode)
        {
            string responseBody2 = await response2.Content.ReadAsStringAsync();
            doc.LoadHtml(responseBody2);
            var nodes = doc.DocumentNode.SelectNodes("//div[@class=\"lie\"]/ul/li");
            for (int j = 0; j < nodes.Count; j++)
            {
                var name = nodes[j].ChildNodes[3].InnerText;
                //只有name符合下面的格式才能成功转换为时间,所以这里需要有一个判断
                if (name != "" && name.Contains("气象预告"))
                {
                    var dayUrl = new DayUrl();
                    //string format;
                    //DateTime date;
                    // 定义日期时间格式
                    string format = "yyyy年M月d日H点气象预告";
                    // 解析字符串为DateTime
                    DateTime date = DateTime.ParseExact(name, format, null);
                    var a = nodes[j].ChildNodes[3];
                    string urlText = a.GetAttributeValue("href", "");
                    string newValue = "https://cj.msa.gov.cn/xxgk/xxgkml/aqxx/qxyg/";
                    string realUrl = "";
                    realUrl = newValue + urlText.Substring(1);
                    dayUrl.Date = date;
                    dayUrl.Url = realUrl;
                    dayUrlList.Add(dayUrl);
                }
                else
                {
                    Debug.WriteLine($"在{name}处,判断不符合要求");
                }

            }
        }
    }
    // 将数据存入SQLite数据库
    db.Insertable(dayUrlList.OrderBy(x => x.Date).ToList()).ExecuteCommand();
    MessageBox.Show($"获取每天的URL成功,共有{dayUrlList.Count}条");
}

在这一步骤需要注意的是XPath的书写,以及每一天URL的构建,以及时间的获取。

XPath的书写:

 var nodes = doc.DocumentNode.SelectNodes("//div[@class=\"lie\"]/ul/li");

表示一个类名为"lie"的div下的ul标签下的所有li标签,如下所示:

image-20231127201558734

构建每一天的URL:

 var a = nodes[j].ChildNodes[3];
 string urlText = a.GetAttributeValue("href", "");
 string newValue = "https://cj.msa.gov.cn/xxgk/xxgkml/aqxx/qxyg/";
 string realUrl = "";
 realUrl = newValue + urlText.Substring(1);

这里获取li标签下的a标签,如下所示:

image-20231127201814284

string urlText = a.GetAttributeValue("href", "");

这段代码获取a标签中href属性的值,这里是./202311/t20231127_3103490.shtml。

 string urlText = a.GetAttributeValue("href", "");
 string newValue = "https://cj.msa.gov.cn/xxgk/xxgkml/aqxx/qxyg/";
 string realUrl =  newValue + urlText.Substring(1);

这里是在拼接每一天的URL。

var name = nodes[j].ChildNodes[3].InnerText;
 // 定义日期时间格式
string format = "yyyy年M月d日H点气象预告";
 // 解析字符串为DateTime
DateTime date = DateTime.ParseExact(name, format, null);

这里是从文本中获取时间,比如文本的值也就是name的值为:“2023年7月15日17点气象预告”,name获得的date就是2023-7-15 17:00。

    // 将数据存入SQLite数据库
    db.Insertable(dayUrlList.OrderBy(x => x.Date).ToList()).ExecuteCommand();
    MessageBox.Show($"获取每天的URL成功,共有{dayUrlList.Count}条");

这里是将数据存入数据库中,ORM使用的是SQLSugar,类DayUrl如下:

internal class DayUrl
{
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public string Url { get; set; }
}

最后获取每一天URL的效果如下所示:

image-20231127202711471

获取温度数据

需要获取的内容如下:

image-20231127202852536

设计对应的类如下:

internal class WeatherData
{
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    public string? StationName { get; set; }
    public string? Weather {  get; set; }
    public string? Tem_Low {  get; set; }
    public string? Tem_High { get; set; }
    public string? Wind {  get; set; }
    public string? Visibility_Low { get; set; }
    public string? Visibility_High { get; set; }
    public string? Fog { get; set; }
    public string? Haze { get; set; }
    public DateTime Date { get; set; }
}

增加了一个时间,方便以后根据时间获取。

获取温度数据的代码如下:

    var list = db.Queryable<DayUrl>().ToList();
    for (int i = 0; i < list.Count; i++)
    {
        HttpResponseMessage response = await httpClient.GetAsync(list[i].Url);
        // 处理响应
        if (response.IsSuccessStatusCode)
        {
            string responseBody2 = await response.Content.ReadAsStringAsync();
            doc.LoadHtml(responseBody2);
            var nodes = doc.DocumentNode.SelectNodes("//table");
            if (nodes != null)
            {
                var table = nodes[5];
                var trs = table.SelectNodes("tbody/tr");
                for (int j = 1; j < trs.Count; j++)
                {
                    var tds = trs[j].SelectNodes("td");
                    switch (tds.Count)
                    {
                        case 8:
                            var wd8 = new WeatherData();
                    wd8.StationName = tds[0].InnerText.Trim().Replace("&nbsp;", "");
                            wd8.Weather = tds[1].InnerText.Trim().Replace("&nbsp;", "");
                            wd8.Tem_Low = tds[2].InnerText.Trim().Replace("&nbsp;", "");
                            wd8.Tem_High = tds[3].InnerText.Trim().Replace("&nbsp;", "");
                            wd8.Wind = tds[4].InnerText.Trim().Replace("&nbsp;", "");
                            wd8.Visibility_Low = tds[5].InnerText.Trim().Replace("&nbsp;", "");
                            wd8.Visibility_High = tds[6].InnerText.Trim().Replace("&nbsp;", "");
                            wd8.Fog = tds[7].InnerText.Trim().Replace("&nbsp;", "");
                            wd8.Date = list[i].Date;
                            weatherDataList.Add(wd8);
                            break;
                        case 9:
                            var wd9 = new WeatherData();
                            wd9.StationName = tds[0].InnerText.Trim().Replace("&nbsp;", "");
                            wd9.Weather = tds[1].InnerText.Trim().Replace("&nbsp;", "");
                            wd9.Tem_Low = tds[2].InnerText.Trim().Replace("&nbsp;", "");
                            wd9.Tem_High = tds[3].InnerText.Trim().Replace("&nbsp;", "");
                            wd9.Wind = tds[4].InnerText.Trim().Replace("&nbsp;", "");
                            wd9.Visibility_Low = tds[5].InnerText.Trim().Replace("&nbsp;", "");
                            wd9.Visibility_High = tds[6].InnerText.Trim().Replace("&nbsp;", "");
                            wd9.Fog = tds[7].InnerText.Trim().Replace("&nbsp;", "");
                            wd9.Haze = tds[8].InnerText.Trim().Replace("&nbsp;", "");
                            wd9.Date = list[i].Date;
                            weatherDataList.Add(wd9);
                            break;
                        default:
                            break;
                    }


                }
            }
            else
            {
                
              
            }
        }
        // 输出进度提示
        Debug.WriteLine($"已处理完成第{i}个URL");
    }
    // 将数据存入SQLite数据库
    db.Insertable(weatherDataList.OrderBy(x => x.Date).ToList()).ExecuteCommand();
    MessageBox.Show($"获取天气数据成功,共有{weatherDataList.Count}条");

}

这里使用swith case是因为网页的格式并不是一层不变的,有时候少了一列,没有霾的数据。

 wd9.StationName = tds[0].InnerText.Trim().Replace("&nbsp;", "");

这里对文本进行这样处理是因为原始的数据是“\n内容&nbsp\n”,C#中String.Trim()方法会删除字符串前后的空白,string.Replace(“a”,“b”)方法会将字符串中的a换成b。

效果如下所示:

image-20231127204509544

image-20231127204629667

将数据全部都存入数据库中了。

4、最后

通过这个实例说明了其实C#也是可以实现网页爬虫的,对于没有反爬的情况下是完全适用的,再配合linq做数据处理也是可以的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/219558.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Isaac Sim教程06 OmniGraph图编程

Isaac Sim OmniGraph图编程 版权信息 Copyright 2023 Herman YeAuromix. All rights reserved.This course and all of its associated content, including but not limited to text, images, videos, and any other materials, are protected by copyright law. The autho…

mac shortcut keys cheat sheet【mac 快捷键清单】

文章目录 剪切、拷贝、粘贴和其他常用快捷键访达和系统快捷键 Mac 键盘快捷键 Command&#xff08;或 Cmd&#xff09;⌘ Shift ⇧ Option&#xff08;或 Alt&#xff09;⌥ Control&#xff08;或 Ctrl&#xff09;⌃ Caps Lock ⇪ Fn 剪切、拷贝、粘贴和其他常用快捷…

【数据结构与算法】JavaScript实现二叉搜索树

文章目录 一、二叉搜索树的封装1.插入数据2.遍历数据2.1.先序遍历2.2.中序遍历2.3.后续遍历 3.查找数据3.1.查找最大值&最小值3.2.查找特定值 4.删除数据4.1.情况1&#xff1a;没有子节点4.2.情况2&#xff1a;有一个子节点4.3.情况3&#xff1a;有两个子节点4.4.完整实现 …

【性能测试】LR录制回放事务检查点

前言 上一次推文我们分享了性能测试分类和应用领域&#xff0c;今天带大家学习性能测试工作原理、事务、检查点&#xff01;后续文章都会系统分享干货&#xff0c;带大家从0到1学会性能测试&#xff0c;另外还有教程等同步资料&#xff0c;文末免费获取~ 01、LR工作原理 ​通…

CSS特效026:扇骨打开关闭的动画

CSS常用示例100专栏目录 本专栏记录的是经常使用的CSS示例与技巧&#xff0c;主要包含CSS布局&#xff0c;CSS特效&#xff0c;CSS花边信息三部分内容。其中CSS布局主要是列出一些常用的CSS布局信息点&#xff0c;CSS特效主要是一些动画示例&#xff0c;CSS花边是描述了一些CSS…

Vue项目图片预览v-viewer插件使用,图片预览,图片查看;antdesign+vue2+v-viewer实现图片查看器并可删除图片

Vue项目图片预览v-viewer插件使用 1. 安装 v-viewer 你可以使用 npm 或者 yarn 来安装 v-viewer&#xff1a; npm install v-viewer 或者 yarn add v-viewer 2. 导入和配置 v-viewer 在你的 Vue 项目中&#xff0c;你需要在入口文件&#xff08;通常是 main.js&#xff09…

做一个类似东郊到家的上门服务类系统有哪些功能?

上门服务系统是一款便捷的技师接单、上门提供理疗服务的软件。我们拥有优秀的开发团队&#xff0c;为您量身定制解决方案&#xff0c;价格合理&#xff0c;用心服务。 预约上门&#xff1a;该功能是预约上门推拿理疗按摩系统软件小程序APP的核心功能。消费者通过系统预约下单&a…

python打包exe,打包好后,启动exe报错找不到paddleocr

目录 1、安装pyinstaller 2、生成脚本文件的.spce文件 3、资源文件配置 4、生成exe文件 5、使用了paddleocr启动exe后报错 6、配置.spce文件 7、重新生成exe文件 8、关于图片找不到的问题 参考&#xff1a;PaddleOCR打包exe--Pyinstaller_paddleocr 打包exe_mjiansun的博…

签名应用APP分发平台的微服务化部署是什么?其有哪些优势?

在信息技术的世界里&#xff0c;软件开发和部署的模式不断演进。从单体架构到服务化&#xff0c;再到今日备受瞩目的微服务架构。微服务化部署作为一种新兴的软件架构风格&#xff0c;正被越来越多的企业采用。它使得应用可以被分解成一套相互独立的最小服务单元。而“分发平台…

Web安全-初识SQL注入(一)

1、初识SQL注入 1.1、什么是注入&#xff1f; 将不受信任的数据作为命令或查询的一部分发送到解析器时&#xff0c;会产生诸如SQL注入、NoSQL注入、OS 注入和LDAP注入的注入缺陷。攻击者的恶意数据可以诱使解析器在没有适当授权的情况下执行非预期命令或访问数据。 注入能导…

基于 springboot + vue 健身房管理系统 毕业设计-附源码

qq&#xff08;2829419543&#xff09;获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;springboot 前端&#xff1a;采用vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xf…

Temu数据软件:如何使用Temu数据软件优化您的Temu店铺运营

在如今竞争激烈的电商市场中&#xff0c;了解市场趋势、优化产品和店铺运营、了解竞争对手等方面的数据分析变得至关重要。为了帮助Temu平台上的商家更好地了解市场和消费者需求&#xff0c;提高运营效果&#xff0c;Temu数据软件成为了一项强大的工具。本文将介绍一些建议的Te…

【Tomcat】java.net.BindException “Address already in use: NET_Bind“

问题 17:37 Error running Tomcat 7.0.76: Unable to open debugger port (127.0.0.1:14255): java.net.BindException "Address already in use: NET_Bind"调整 把14255 改成 49963就正常了 附件 netstat -aon|findstr "49963" taskkill -f -pid xxx…

WiFi模块ESP8266(超详细)---(含固件库、AP、STA、原子云使用)

写在前面&#xff1a;本节我们学习使用一个常见的模块--WIFI模块&#xff0c;在前面我们学习了蓝牙&#xff08;HC--08&#xff09;模块&#xff0c;为学习WIFI模块打下了一定的基础&#xff1b;但是在学习WIFI模块的时候&#xff0c;我也遇到了很多的问题&#xff0c;查阅了很…

常见的分布式事务解决方案,你会几种?

今天我们来聊一聊分布式事务&#xff0c;在传统的单体应用中&#xff0c;事务的控制非常简单&#xff0c;Spring框架都为我们做了封装&#xff0c;我们只需简单地使用Transactional注解就能进行事务的控制&#xff0c;然而在分布式应用中&#xff0c;传统的事务方案就出现了极大…

通信标准化协会,信通院及量子信息网络产业联盟调研玻色量子,共绘实用化量子未来!

8月14日&#xff0c;中国通信标准化协会&#xff0c;信通院标准所及量子信息网络产业联盟等单位领导走访调研北京玻色量子科技有限公司&#xff08;以下简称“玻色量子”&#xff09;&#xff0c;参观了玻色量子公司及自建的十万颗粒洁净度的光量子信息技术实验室&#x1f517;…

ROS2教程02 ROS2的安装、配置和测试

ROS2的安装和配置 版权信息 Copyright 2023 Herman YeAuromix. All rights reserved.This course and all of its associated content, including but not limited to text, images, videos, and any other materials, are protected by copyright law. The author holds a…

python超详细基础文件操作【建议收藏】

文章目录 前言1 文件操作1.1 文件打开与关闭1.1.1 打开文件1.1.2 关闭文件 1.2 访问模式及说明 2 文件读写2.1 写数据&#xff08;write&#xff09;2.2 读数据&#xff08;read&#xff09;2.3 读数据&#xff08;readlines&#xff09;2.3 读数据&#xff08;readline&#x…

机器学习-ROC曲线:技术解析与实战应用

本文全面探讨了ROC曲线&#xff08;Receiver Operating Characteristic Curve&#xff09;的重要性和应用&#xff0c;从其历史背景、数学基础到Python实现以及关键评价指标。文章旨在提供一个深刻而全面的视角&#xff0c;以帮助您更好地理解和应用ROC曲线在模型评估中的作用。…

CSS中 设置文字下划线 的几种方法

在网页设计和开发中&#xff0c;我们经常需要对文字进行样式设置&#xff0c;包括字体,颜色&#xff0c;大小等&#xff0c;其中&#xff0c;设置文字下划线是一种常见需求 一 、CSS种使用 text-decoration 属性来设置文字的装饰效果&#xff0c;包括下划线。 常用的取值&…