C#实现网页爬虫获取数据(c#爬虫全站链接)
liuian 2025-02-04 15:05 42 浏览
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慢查询优化:从explain到索引,DBA手把手教你提升10倍性能
-
数据库性能是应用系统的生命线,而慢查询就像隐藏在系统中的定时炸弹。某电商平台曾因一条未优化的SQL导致订单系统响应时间从200ms飙升至8秒,最终引发用户投诉和订单流失。今天我们就来系统学习MySQL...
- 一文读懂SQL五大操作类别(DDL/DML/DQL/DCL/TCL)的基础语法
-
在SQL中,DDL、DML、DQL、DCL、TCL是按操作类型划分的五大核心语言类别,缩写及简介如下:DDL(DataDefinitionLanguage,数据定义语言):用于定义和管理数据库结构...
- 闲来无事,学学Mysql增、删,改,查
-
Mysql增、删,改,查1“增”——添加数据1.1为表中所有字段添加数据1.1.1INSERT语句中指定所有字段名语法:INSERTINTO表名(字段名1,字段名2,…)VALUES(值1...
- 数据库:MySQL 高性能优化规范建议
-
数据库命令规范所有数据库对象名称必须使用小写字母并用下划线分割所有数据库对象名称禁止使用MySQL保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来)数据库对象的命名要能做到见名识意,...
- 下载工具合集_下载工具手机版
-
迅雷,在国内的下载地位还是很难撼动的,所需要用到的地方还挺多。缺点就是不开会员,软件会限速。EagleGet,全能下载管理器,支持HTTP(S)FTPMMSRTSP协议,也可以使用浏览器扩展检测...
- mediamtx v1.15.2 更新详解:功能优化与问题修复
-
mediamtxv1.15.2已于2025年10月14日发布,本次更新在功能、性能优化以及问题修复方面带来了多项改进,同时也更新了部分依赖库并提升了安全性。以下为本次更新的详细内容:...
- 声学成像仪:泄露监测 “雷达” 方案开启精准防控
-
声学成像仪背景将声像图与阵列上配装的摄像实所拍的视频图像以透明的方式叠合在一起,就形成了可直观分析被测物产生状态。这种利用声学、电子学和信息处理等技术,变换成人眼可见的图像的技术可以帮助人们直观地认识...
- 最稳存储方案:两种方法将摄像头接入威联通Qu405,录像不再丢失
-
今年我家至少被4位邻居敲门,就是为了查监控!!!原因是小区内部监控很早就停止维护了,半夜老有小黄毛掰车门偷东西,还有闲的没事划车的,车主损失不小,我家很早就配备监控了,人来亮灯有一定威慑力,不过监控设...
- 离岗检测算法_离岗检查内容
-
一、研发背景如今社会许多岗位是严禁随意脱离岗位的,如塔台、保安室、监狱狱警监控室等等,因为此类行为可能会引起重大事故,而此类岗位监督管理又有一定困难,因此促生了智能视频识别系统的出现。二、产品概述及工...
- 消防安全通道占用检测报警系统_消防安全通道占用检测报警系统的作用
-
一、产品概述科缔欧消防安全通道占用检测报警系统,是创新行业智能监督管理方式、完善监管部门动态监控及预警预报体系的信息化手段,是实现平台远程监控由“人为监控”向“智能监控”转变的必要手段。产品致力于设...
- 外出住酒店、民宿如何使用手机检测隐藏的监控摄像头
-
最近,一个家庭在他们的民宿收到了一个大惊喜:客厅里有一个伪装成烟雾探测器的隐藏摄像头,监视着他们的一举一动。隐藏摄像头的存在如果您住在酒店或民宿,隐藏摄像头不应再是您的担忧。对于民宿,房东应报告所有可...
- 基于Tilera众核平台的流媒体流量发生系统的设计
-
曾帅,高宗彬,赵国锋(重庆邮电大学通信与信息工程学院,重庆400065)摘要:设计了一种基于Tilera众核平台高强度的流媒体流量发生系统架构,其主要包括:系统界面管理模块、服务承载模块和流媒体...
- 使用ffmpeg将rtsp流转流实现h5端播放
-
1.主要实现rtsp转tcp协议视频流播放ffmpeg下载安装(公认业界视频处理大佬)a、官网地址:www.ffmpeg.org/b、gitHub:github.com/FFmpeg/FFmp…c、推...
- 将摄像头视频流从Rtsp协议转为websocket协议
-
写在前面很多通过摄像头拿到的视频流格式都是Rtsp协议的,比如:海康威视摄像头。在现代的浏览器中,已经不支持直接播放Rtsp视频流,而且,海康威视提供的本身的webSdk3.3.0视频插件有很多...
- 华芸科技推出安全监控中心2.1 Beta测试版
-
全球独家支持hdmi在线实时监看摄像机画面,具单一、循环或同时监看四频道视频影像,可透过华芸专用红外线遥控器、airemote或是键盘鼠标进行操作,提供摄像机频道增购服务,满足用户弹性扩增频道需...
- 一周热门
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
