.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实现简单的定时爬虫的更多相关文章

  1. windows下建立netcore控制台程序,然后传送到centos7下的docker容器里运行

    1.首先,在window下用vs2017开发netcore控制台项目. 2.把建立好的项目传送到centos7下面的容器里. docker cp sharefoldersforwindows/ 359 ...

  2. C#控制台程序的参数解析类库 CommandLine简单使用说明

    前言 C#开发的控制台程序,默认接收string[] args参数.如果有多个参数需要输入时,可以按照顺序依次输入:但如果有些参数不是必选的,或者有些参数中间需要有空格比如时间“2016-05-18 ...

  3. 控制台程序的参数解析类库 CommandLine

    C#控制台程序的参数解析类库 CommandLine简单使用说明 前言 C#开发的控制台程序,默认接收string[] args参数.如果有多个参数需要输入时,可以按照顺序依次输入:但如果有些参数不是 ...

  4. .NET CORE与Spring Boot编写控制台程序应有的优雅姿势

    本文分别说明.NET CORE与Spring Boot 编写控制台程序应有的“正确”方法,以便.NET程序员.JAVA程序员可以相互学习与加深了解,注意本文只介绍用法,不会刻意强调哪种语言或哪种框架写 ...

  5. .NET CORE编写控制台程序应有的优雅姿势(转载)

    原文地址:https://www.cnblogs.com/zuowj/p/11107243.html 本文所说的编写控制台程序应有的“正确”方法,我把正确二字加上引号,因为没有绝对的正确,因人而异,因 ...

  6. Mac/Windows开发跨平台.NET Core 控制台程序

    自从微软开始在Github上开源搞.NET Core后,.NET的跨平台逐渐就成真了.多年使用各种语言,说实话还是csharp用起来最舒服.不过现在的工作环境里使用它的机会比较少,大部分时候只是用来写 ...

  7. 使用.NetCore 控制台演示 熔断 降级(polly)

    1.熔断降级的概念: 熔断:我这里有一根长度一米的钢铁,钢铁的熔点1000度(假设),现在我想用力把这根钢铁折弯,但是人的力有限达不到折弯的点,然后我使用火给钢铁加热,每隔一段时间我就会尝试一下是否能 ...

  8. mac 发布.net Core2.0 控制台程序

    安装.net core2.0 环境,略 新建文件夹 TestA, 存放项目 TestA 在 TestA 文件夹下,创建控制台程序: dotnet new console(会自动生成 TestA.csp ...

  9. Net Core 控制台程序使用Nlog 输出到log文件

    using CoreImportDataApp.Common; using Microsoft.Extensions.Configuration; using Microsoft.Extensions ...

随机推荐

  1. wdcp的一个安全漏洞 2015 9 月

    wdcp的一个安全漏洞,非常严重,请大家及时升级和检查 在九月份的时候,wdcp出了一个很严重的安全漏洞,当时也出了补丁更新,具体可看http://www.wdlinux.cn/bbs/thread- ...

  2. 上班从换一张桌面壁纸开始——开源小工具Bing每日壁纸

    发布一个自用的开源小软件,Bing每日壁纸,使用c# winform开发.该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 功能特性 自动获取Bing最新图片 ...

  3. 讲讲Java8的Optional类

    前言 Java 8中引入了 Optional 类来解决 NullPointerException 与繁琐的 null 检查,该类首次出现在 Guava.Java 8 才成为类库中的一部分. 入门 Op ...

  4. oracle 11.2.0.1.0 升级 11.2.0.4.0 并 patch 到11.2.0.4.7

    升级步骤: (1)    备份数据库 (2)    运行patchset,升级oracle 软件 (3)    准备新的ORACLE_HOME (4)    运行dbua 或者脚本升级实例 (5)   ...

  5. PAT天梯赛练习 L3-003 社交集群 (30分) DFS搜索

    题目分析: 一共有N个编号为1~1000的人,以及一共有编号为1~1000种不同的兴趣,在题目给出1~N编号的人员每个人喜欢的兴趣的id后,要求统计出不同的人员集合的个数以及每个人员几个的人数从大到小 ...

  6. 【Oracle】查看当前连接数和最大连接数

    查看当前数据库连接数 select count(*) from v$session where username is not null; select count(*) from v$process ...

  7. leetcode 31. Next Permutation (下一个排列,模拟,二分查找)

    题目链接 31. Next Permutation 题意 给定一段排列,输出其升序相邻的下一段排列.比如[1,3,2]的下一段排列为[2,1,3]. 注意排列呈环形,即[3,2,1]的下一段排列为[1 ...

  8. 安装newman error:package exports for 'c:\nmp\node_modules\newman\node_module 解决办法

    一.场景描述: 通过npm安装newman时,一直失败. 尝试了很多安装命令: npm install -g newman npm install -g newman --registry=http: ...

  9. AmoebaNet:经费在燃烧,谷歌提出基于aging evolution的神经网络搜索 | AAAI 2019

    论文提出aging evolution,一个锦标赛选择的变种来优化进化算法,在NASNet搜索空间上,对比强化学习和随机搜索,该算法足够简洁,而且能够更快地搜索到更高质量的模型,论文搜索出的Amoeb ...

  10. mount: /dev/sdxx already mounted or /xxxx busy解决方法

    异常现象: 解决方法: 1.    輸入root的密碼,進入單用戶2.    重新掛載/目錄,使其變為可讀可寫 # mount –o rw,remount / 3.    修改/etc/fstab文件 ...