C#实现网页爬虫获取数据(c#抓取网页数据)
itomcoil 2025-06-10 15:44 7 浏览
1、需求
想要获取网站上所有的气象信息,网站如下所示:
目前总共有67页,随便点开一个如下所示:
需要获取所有天气数据,如果靠一个个点开再一个个复制粘贴那么也不知道什么时候才能完成,这个时候就可以使用C#来实现网页爬虫获取这些数据。
2、效果
先来看下实现的效果,所有数据都已存入数据库中,如下所示:
总共有4万多条数据。
3、具体实现
构建每一页的URL
第一页的网址如下所示:
最后一页的网址如下所示:
可以发现是有规律的,那么就可以先尝试构建出每个页面的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(#34;获取每个页面的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] = #34;https://cj.msa.gov.cn/xxgk/xxgkml/aqxx/qxyg/index_{i}.shtml";
}
}
return urls;
}
这里使用了HtmlAgilityPack
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信息,如下所示:
可以进一步构建每一天的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(#34;在{name}处,判断不符合要求");
}
}
}
}
// 将数据存入SQLite数据库
db.Insertable(dayUrlList.OrderBy(x => x.Date).ToList()).ExecuteCommand();
MessageBox.Show(#34;获取每天的URL成功,共有{dayUrlList.Count}条");
}
在这一步骤需要注意的是XPath的书写,以及每一天URL的构建,以及时间的获取。
XPath的书写:
var nodes = doc.DocumentNode.SelectNodes("//div[@class=\"lie\"]/ul/li");
表示一个类名为"lie"的div下的ul标签下的所有li标签,如下所示:
构建每一天的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标签,如下所示:
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(#34;获取每天的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的效果如下所示:
获取温度数据
需要获取的内容如下:
设计对应的类如下:
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(" ", "");
wd8.Weather = tds[1].InnerText.Trim().Replace(" ", "");
wd8.Tem_Low = tds[2].InnerText.Trim().Replace(" ", "");
wd8.Tem_High = tds[3].InnerText.Trim().Replace(" ", "");
wd8.Wind = tds[4].InnerText.Trim().Replace(" ", "");
wd8.Visibility_Low = tds[5].InnerText.Trim().Replace(" ", "");
wd8.Visibility_High = tds[6].InnerText.Trim().Replace(" ", "");
wd8.Fog = tds[7].InnerText.Trim().Replace(" ", "");
wd8.Date = list[i].Date;
weatherDataList.Add(wd8);
break;
case 9:
var wd9 = new WeatherData();
wd9.StationName = tds[0].InnerText.Trim().Replace(" ", "");
wd9.Weather = tds[1].InnerText.Trim().Replace(" ", "");
wd9.Tem_Low = tds[2].InnerText.Trim().Replace(" ", "");
wd9.Tem_High = tds[3].InnerText.Trim().Replace(" ", "");
wd9.Wind = tds[4].InnerText.Trim().Replace(" ", "");
wd9.Visibility_Low = tds[5].InnerText.Trim().Replace(" ", "");
wd9.Visibility_High = tds[6].InnerText.Trim().Replace(" ", "");
wd9.Fog = tds[7].InnerText.Trim().Replace(" ", "");
wd9.Haze = tds[8].InnerText.Trim().Replace(" ", "");
wd9.Date = list[i].Date;
weatherDataList.Add(wd9);
break;
default:
break;
}
}
}
else
{
}
}
// 输出进度提示
Debug.WriteLine(#34;已处理完成第{i}个URL");
}
// 将数据存入SQLite数据库
db.Insertable(weatherDataList.OrderBy(x => x.Date).ToList()).ExecuteCommand();
MessageBox.Show(#34;获取天气数据成功,共有{weatherDataList.Count}条");
}
这里使用swith case是因为网页的格式并不是一层不变的,有时候少了一列,没有霾的数据。
wd9.StationName = tds[0].InnerText.Trim().Replace(" ", "");
这里对文本进行这样处理是因为原始的数据是“\n内容 \n”,C#中String.Trim()方法会删除字符串前后的空白,string.Replace("a","b")方法会将字符串中的a换成b。
效果如下所示:
将数据全部都存入数据库中了。
4、最后
相关推荐
- 字节三面:MySQL数据同步ES的4种方法!你能想到几种?
-
如何进行数据同步MySQL是一种流行的关系型数据库,而Elasticsearch是一个强大的搜索引擎和分析平台。将MySQL数据同步到Elasticsearch中可以帮助我们更方便地搜索和分析数据。在...
- Java 连接 MySQL 数据库(java连接mysql课设)
-
一、环境准备1.1依赖管理(Maven)在项目的pom.xml中添加MySQL驱动依赖:<dependency><groupId>mysql</gro...
- Spring Boot 连接 MySQL 数据库(spring boot配置数据库连接)
-
一、环境准备1.1依赖管理(Maven)<!--方案1:JdbcTemplate--><dependency><groupId>org.sprin...
- java连接mysql数据库达成数据查询详细教程
-
前言:本篇文章适用于所有前后端开发者众所周知,只要是编程,那肯定是需要存储数据的,无论是c语言还是java,都离不开数据的读写,数据之间传输不止,这也就形成了现代互联网的一种相互存在关系!而读写存储的...
- 既然有MySQL了,为什么还要有MongoDB?
-
大家好,我是哪吒,最近项目在使用MongoDB作为图片和文档的存储数据库,为啥不直接存MySQL里,还要搭个MongoDB集群,麻不麻烦?让我们一起,一探究竟,了解一下MongoDB的特点和基本用法,...
- 用 JSP 连接 MySQL 登入注册项目实践(JSP + HTML + CSS + MySQL)
-
目录一、写在前面二、效果图三、实现思路四、实现代码1、login总界面2、registercheck总代码3、logoutcheck总代码4、amendcheck总代码相关文章一、写在前面哈喽~大家好...
- MySQL关联查询时,为什么建议小表驱动大表?这样做有什么好处
-
在SQL数据库中,小表驱动大表是一种常见的优化策略。这种策略在涉及多表关联查询的情况下尤其有效。这是因为数据库查询引擎会尽可能少的读取和处理数据,这样能极大地提高查询性能。"小表驱动大表&...
- mysql8驱动兼容规则(mysql8.0驱动)
-
JDBC版本:Connector/J8.0支持JDBC4.2规范.如果Connector/J8.0依赖于更高版本的jdbclib,对于调用只有更高版本特定的方法会抛出SQLFea...
- mysql数据表如何导入MSSQL中(mysql怎样导入数据)
-
本案例演示所用系统是windowsserver2012.其它版本windows操作系统类似。1,首先需要下载mysqlodbc安装包。http://dev.mysql.com/downloa...
- MySQL 驱动中虚引用 GC 耗时优化与源码分析
-
本文要点:一种优雅解决MySQL驱动中虚引用导致GC耗时较长问题的解决方法虚引用的作用与使用场景MySQL驱动源码中的虚引用分析背景在之前文章中写过MySQLJDBC驱动中的虚引用导致...
- ExcelVBA 连接 MySQL 数据库(vba 连接sqlserver)
-
上期分享了ExcelVBA连接sqlite3数据库,今天给大家分享ExcelVBA连接另一个非常流行的MySQL数据库。一、环境win10Microsoftoffice2010(...
- QT 5.12.11 编译MySQL 8 驱动教程- 1.01版
-
安装编译环境:qt5.12.11mysql8.0.28修改mysql.pro工程文件,编译生成动态库mysql.pro文件位置:D:\Alantop_Dir\alantop_sde\Qt\Qt5....
- 「Qt入门第22篇」 数据库(二)编译MySQL数据库驱动
-
导语在上一节的末尾我们已经看到,现在可用的数据库驱动只有两类3种,那么怎样使用其他的数据库呢?在Qt中,我们需要自己编译其他数据库驱动的源码,然后当做插件来使用。下面就以现在比较流行的MySQL数据库...
- (干货)一级注册计量师第五版——第四章第三节(三)
-
计量标准的建立、考核及使用(三)PS:内容都是经过个人学习而做的笔记。如有错误的地方,恳请帮忙指正!计量标准考核中有关技术问题1检定或校准结果的重复性重复性是指在一组重复性测量条件下的测量精密度。检定...
- 声学测量基础知识分享(声学测量pdf)
-
一、声学测量的分类和难点1.声学测量的分类声学测量按目的可分为:声学特性研究(声学特性研究、媒质特性研究、声波发射与接收的研究、测量方法与手段的研究、声学设备的研究),声学性能评价和改善(声学特性评价...
- 一周热门
- 最近发表
-
- 字节三面:MySQL数据同步ES的4种方法!你能想到几种?
- Java 连接 MySQL 数据库(java连接mysql课设)
- Spring Boot 连接 MySQL 数据库(spring boot配置数据库连接)
- java连接mysql数据库达成数据查询详细教程
- 既然有MySQL了,为什么还要有MongoDB?
- 用 JSP 连接 MySQL 登入注册项目实践(JSP + HTML + CSS + MySQL)
- MySQL关联查询时,为什么建议小表驱动大表?这样做有什么好处
- mysql8驱动兼容规则(mysql8.0驱动)
- mysql数据表如何导入MSSQL中(mysql怎样导入数据)
- MySQL 驱动中虚引用 GC 耗时优化与源码分析
- 标签列表
-
- ps图案在哪里 (33)
- super().__init__ (33)
- python 获取日期 (34)
- 0xa (36)
- super().__init__()详解 (33)
- python安装包在哪里找 (33)
- linux查看python版本信息 (35)
- python怎么改成中文 (35)
- php文件怎么在浏览器运行 (33)
- eval在python中的意思 (33)
- python安装opencv库 (35)
- python div (34)
- sticky css (33)
- python中random.randint()函数 (34)
- python去掉字符串中的指定字符 (33)
- python入门经典100题 (34)
- anaconda安装路径 (34)
- yield和return的区别 (33)
- 1到10的阶乘之和是多少 (35)
- python安装sklearn库 (33)
- dom和bom区别 (33)
- js 替换指定位置的字符 (33)
- python判断元素是否存在 (33)
- sorted key (33)
- shutil.copy() (33)