NetCore控制台程序-使用HostService和HttpClient实现简单的定时爬虫
.NetCore承载系统
.NetCore的承载系统, 可以将长时间运行的服务承载于托管进程中, AspNetCore应用其实就是一个长时间运行的服务, 启动AspNetCore应用后, 它就会监听网络请求, 也就是开启了一个监听器, 监听器会将网络请求传递给管道进行处理, 处理后得到Http响应返回
有很多场景都会有服务承载的需求, 比如这篇博文要做的, 定时抓取华为论坛的文章点赞数
爬取文章点赞数
分析
比如这个链接 https://developer.huawei.com/consumer/cn/forum/topicview?tid=0201308791792470245&fid=23 , 点进去不难发现这是用angular做的一个页面, 既然是Angular, 那说明前后端分离了, 浏览器F12查看网络请求

找到对应api请求方法:
POST https://developer.huawei.com/consumer/cn/forum/mid/partnerforumservice/v1/open/getTopicDetail? HTTP/1.1
Host: developer.huawei.com
Content-Type: application/json
Content-Length: 33
{"topicId":"0201302923811480141"}
这里经过我的测试, Content-Type和Content-Length必须上面那样的值, 还有body, 你多一个空格请求都会失败
使用HttpClient请求数据
直接看代码吧, 这里使用了依赖注入来注入HttpClientFactory, 还可以使用强类型的HttpClient, 具体可以看文档和dudu博客的这篇博文
工厂参观记:.NET Core 中 HttpClientFactory 如何解决 HttpClient 臭名昭著的问题
private readonly IHttpClientFactory _httpClientFactory;
public async Task<int> Crawl(string link)
{
using (var httpClient = _httpClientFactory.CreateClient())
{
var uri = new Uri(link);
uri.TryReadQueryAsJson(out var queryParams);
var topicId = queryParams["tid"].ToString();
int likeCount = -1;
if (!string.IsNullOrEmpty(topicId))
{
var body = JsonConvert.SerializeObject(
new { topicId },
Formatting.None);
uri = new Uri(_baseUrl);
var jsonContentType = "application/json";
var requestMessage = new HttpRequestMessage
{
RequestUri = uri,
Headers =
{
{ "Host", uri.Host }
},
Method = HttpMethod.Post,
Content = new StringContent(body)
};
requestMessage.Content.Headers.ContentType = new MediaTypeWithQualityHeaderValue(jsonContentType);
requestMessage.Content.Headers.ContentLength = body.Length;
var response = await httpClient.SendAsync(requestMessage);
if (response.StatusCode == HttpStatusCode.OK)
{
dynamic data = await response.Content.ReadAsAsync<dynamic>();
likeCount = data.result.likes;
}
}
return likeCount;
}
}
这里有更简洁的的写法, 使用_httpClient.PostAsJsonAsync(), 但是考虑到可能需要自定义Content-Type这些请求头, 所以先这样写;
配置承载系统
class Program
{
static void Main()
{
new HostBuilder()
.ConfigureServices(services =>
{
services.AddHttpClient();
services.AddHostedService<LikeCountCrawler>();
})
.Build()
.Run();
}
}
LikeCountCrawler实现了IHostedService接口
IHostedService接口
public interface IHostedService
{
/// <summary>
/// Triggered when the application host is ready to start the service.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
Task StartAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// </summary>
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
Task StopAsync(CancellationToken cancellationToken);
}
LikeCountCrawler在StartAsync方法中, 设置开启了一个定时器, 定时器每次溢出, 都执行一次爬虫逻辑
private readonly Timer _timer = new Timer();
private readonly IEnumerable<string> _links = new string[]
{
"https://developer.huawei.com/consumer/cn/forum/topicview?tid=0201308791792470245&fid=23",
"https://developer.huawei.com/consumer/cn/forum/topicview?tid=0201303654965850166&fid=18",
"https://developer.huawei.com/consumer/cn/forum/topicview?tid=0201294272503450453&fid=24",
"https://developer.huawei.com/consumer/cn/forum/topicview?tid=0201294189025490019&fid=17"
};
private readonly string _baseUrl = "https://developer.huawei.com/consumer/cn/forum/mid/partnerforumservice/v1/open/getTopicDetail";
...
public Task StartAsync(CancellationToken cancellationToken)
{
_timer.Interval = 5 * 60 * 1000;
_timer.Elapsed += OnTimer;
_timer.AutoReset = true;
_timer.Enabled = true;
_timer.Start();
OnTimer(null, null);
return Task.CompletedTask;
}
public async Task Crawl(IEnumerable<string> links)
{
await Task.Run(() =>
{
Parallel.ForEach(links, async link =>
{
Console.WriteLine($"Crawling link:{link}, ThreadId:{Thread.CurrentThread.ManagedThreadId}");
var likeCount = await Crawl(link);
Console.WriteLine($"Succeed crawling likecount - {likeCount}, ThreadId:{Thread.CurrentThread.ManagedThreadId}");
});
});
}
private void OnTimer(object sender, ElapsedEventArgs args)
{
_ = Crawl(_links);
}
...
运行效果:

NetCore控制台程序-使用HostService和HttpClient实现简单的定时爬虫的更多相关文章
- windows下建立netcore控制台程序,然后传送到centos7下的docker容器里运行
1.首先,在window下用vs2017开发netcore控制台项目. 2.把建立好的项目传送到centos7下面的容器里. docker cp sharefoldersforwindows/ 359 ...
- C#控制台程序的参数解析类库 CommandLine简单使用说明
前言 C#开发的控制台程序,默认接收string[] args参数.如果有多个参数需要输入时,可以按照顺序依次输入:但如果有些参数不是必选的,或者有些参数中间需要有空格比如时间“2016-05-18 ...
- 控制台程序的参数解析类库 CommandLine
C#控制台程序的参数解析类库 CommandLine简单使用说明 前言 C#开发的控制台程序,默认接收string[] args参数.如果有多个参数需要输入时,可以按照顺序依次输入:但如果有些参数不是 ...
- .NET CORE与Spring Boot编写控制台程序应有的优雅姿势
本文分别说明.NET CORE与Spring Boot 编写控制台程序应有的“正确”方法,以便.NET程序员.JAVA程序员可以相互学习与加深了解,注意本文只介绍用法,不会刻意强调哪种语言或哪种框架写 ...
- .NET CORE编写控制台程序应有的优雅姿势(转载)
原文地址:https://www.cnblogs.com/zuowj/p/11107243.html 本文所说的编写控制台程序应有的“正确”方法,我把正确二字加上引号,因为没有绝对的正确,因人而异,因 ...
- Mac/Windows开发跨平台.NET Core 控制台程序
自从微软开始在Github上开源搞.NET Core后,.NET的跨平台逐渐就成真了.多年使用各种语言,说实话还是csharp用起来最舒服.不过现在的工作环境里使用它的机会比较少,大部分时候只是用来写 ...
- 使用.NetCore 控制台演示 熔断 降级(polly)
1.熔断降级的概念: 熔断:我这里有一根长度一米的钢铁,钢铁的熔点1000度(假设),现在我想用力把这根钢铁折弯,但是人的力有限达不到折弯的点,然后我使用火给钢铁加热,每隔一段时间我就会尝试一下是否能 ...
- mac 发布.net Core2.0 控制台程序
安装.net core2.0 环境,略 新建文件夹 TestA, 存放项目 TestA 在 TestA 文件夹下,创建控制台程序: dotnet new console(会自动生成 TestA.csp ...
- Net Core 控制台程序使用Nlog 输出到log文件
using CoreImportDataApp.Common; using Microsoft.Extensions.Configuration; using Microsoft.Extensions ...
随机推荐
- vue踩坑记,持续更新中......
1.运行项目报错 you may use special comments to disable some waring. use //eslint-disable-next-line.....吧啦吧 ...
- Go语言快速安装手册
Go 是一个开源的编程语言,它能让构造简单.可靠且高效的软件变得容易. Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ia ...
- react项目中实现搜索关键字呈现高亮状态(一)
最近有个需求,在一个react项目中,实现搜索关键字呈现高亮状态.这个在普通的html文件中还好操作些,在react项目中有点懵逼了,因为react项目中很少操作dom,有点无从下手.但最后还是实现了 ...
- springboot源码解析-管中窥豹系列之Initializer(四)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- spring boot 邮件服务
引入依赖 添加spring-boot-starter-mail包引用 <dependency> <groupId>org.springframework.boot</gr ...
- PHP 插件资源
PHP jsonRPC 百度云网盘地址 https://pan.baidu.com/s/1itCIhrdd5bPGJMefNUuKvw 提取码 : ax4d PHP Excel 百度云网盘 ...
- Rancher首席架构师解读Fleet:它何以管理百万集群?
作者简介 Darren Shepherd,Rancher Labs联合创始人及首席架构师.在加入Rancher之前,Darren是Citrix的高级首席工程师,他在那里从事CloudStack.Ope ...
- 【Spring】Spring JdbcTemplate
Spring JdbcTemplate 文章源码 JdbcTemplate 概述 它是 Spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装.Spring 框架提供了很多的操 ...
- 【JS学习】var let const声明变量的异同点
[JS学习]var let const声明变量的异同点 前言: 本博客系列为学习后盾人js教程过程中的记录与产出,如果对你有帮助,欢迎关注,点赞,分享.不足之处也欢迎指正,作者会积极思考与改正. 总述 ...
- rename 表名
rename table 旧表名1 to 新表名1,旧表名2 to 新表名2;