一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - .NET Core 實現定時抓取網站文章并發送到郵箱

.NET Core 實現定時抓取網站文章并發送到郵箱

2020-05-23 15:44曉晨Master ASP.NET教程

本片文章通過實例講述.NET Core 實現定時抓取博客園首頁文章信息并發送到郵箱這個功能,對此有興趣的朋友參考學習下。

前言

 

大家好,我是曉晨。許久沒有更新博客了,今天給大家帶來一篇干貨型文章,一個每隔5分鐘抓取博客園首頁文章信息并在第二天的上午9點發送到你的郵箱的小工具。比如我在2018年2月14日,9點來到公司我就會收到一封郵件,是2018年2月13日的博客園首頁的文章信息。寫這個小工具的初衷是,一直有看博客的習慣,但是最近由于各種原因吧,可能幾天都不會看一下博客,要是中途錯過了什么好文可是十分心疼的哈哈。所以做了個工具,每天歸檔發到郵箱,媽媽再也不會擔心我錯過好的文章了。為什么只抓取首頁?因為博客園首頁文章的質量相對來說高一些。

準備

 

作為一個持續運行的工具,沒有日志記錄怎么行,我準備使用的是NLog來記錄日志,它有個日志歸檔功能非常不錯。在http請求中,由于網絡問題吧可能會出現失敗的情況,這里我使用Polly來進行Retry。使用HtmlAgilityPack來解析網頁,需要對xpath有一定了解。下面是詳細說明:

 

組件名 用途 github
NLog 記錄日志 https://github.com/NLog/NLog
Polly 當http請求失敗,進行重試 https://github.com/App-vNext/Polly
HtmlAgilityPack 網頁解析 https://github.com/zzzprojects/html-agility-pack
MailKit 發送郵件 https://github.com/jstedfast/MailKit

有不了解的組件,可以通過訪問github獲取資料。

參考文章

http://m.ythuaji.com.cn/article/62642.html

獲取&解析博客園首頁數據

我是用的是HttpWebRequest來進行http請求,下面分享一下我簡單封裝的類庫:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
using System;
using System.IO;
using System.Net;
using System.Text;
 
namespace CnBlogSubscribeTool
{
 /// <summary>
 /// Simple Http Request Class
 /// .NET Framework >= 4.0
 /// Author:stulzq
 /// CreatedTime:2017-12-12 15:54:47
 /// </summary>
 public class HttpUtil
 {
 static HttpUtil()
 {
  //Set connection limit ,Default limit is 2
  ServicePointManager.DefaultConnectionLimit = 1024;
 }
 
 /// <summary>
 /// Default Timeout 20s
 /// </summary>
 public static int DefaultTimeout = 20000;
 
 /// <summary>
 /// Is Auto Redirect
 /// </summary>
 public static bool DefalutAllowAutoRedirect = true;
 
 /// <summary>
 /// Default Encoding
 /// </summary>
 public static Encoding DefaultEncoding = Encoding.UTF8;
 
 /// <summary>
 /// Default UserAgent
 /// </summary>
 public static string DefaultUserAgent =
  "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
  ;
 
 /// <summary>
 /// Default Referer
 /// </summary>
 public static string DefaultReferer = "";
 
 /// <summary>
 /// httpget request
 /// </summary>
 /// <param name="url">Internet Address</param>
 /// <returns>string</returns>
 public static string GetString(string url)
 {
  var stream = GetStream(url);
  string result;
  using (StreamReader sr = new StreamReader(stream))
  {
  result = sr.ReadToEnd();
  }
  return result;
 
 }
 
 /// <summary>
 /// httppost request
 /// </summary>
 /// <param name="url">Internet Address</param>
 /// <param name="postData">Post request data</param>
 /// <returns>string</returns>
 public static string PostString(string url, string postData)
 {
  var stream = PostStream(url, postData);
  string result;
  using (StreamReader sr = new StreamReader(stream))
  {
  result = sr.ReadToEnd();
  }
  return result;
 
 }
 
 /// <summary>
 /// Create Response
 /// </summary>
 /// <param name="url"></param>
 /// <param name="post">Is post Request</param>
 /// <param name="postData">Post request data</param>
 /// <returns></returns>
 public static WebResponse CreateResponse(string url, bool post, string postData = "")
 {
  var httpWebRequest = WebRequest.CreateHttp(url);
  httpWebRequest.Timeout = DefaultTimeout;
  httpWebRequest.AllowAutoRedirect = DefalutAllowAutoRedirect;
  httpWebRequest.UserAgent = DefaultUserAgent;
  httpWebRequest.Referer = DefaultReferer;
  if (post)
  {
 
  var data = DefaultEncoding.GetBytes(postData);
  httpWebRequest.Method = "POST";
  httpWebRequest.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
  httpWebRequest.ContentLength = data.Length;
  using (var stream = httpWebRequest.GetRequestStream())
  {
   stream.Write(data, 0, data.Length);
  }
  }
 
  try
  {
  var response = httpWebRequest.GetResponse();
  return response;
  }
  catch (Exception e)
  {
  throw new Exception(string.Format("Request error,url:{0},IsPost:{1},Data:{2},Message:{3}", url, post, postData, e.Message), e);
  }
 }
 
 /// <summary>
 /// http get request
 /// </summary>
 /// <param name="url"></param>
 /// <returns>Response Stream</returns>
 public static Stream GetStream(string url)
 {
  var stream = CreateResponse(url, false).GetResponseStream();
  if (stream == null)
  {
 
  throw new Exception("Response error,the response stream is null");
  }
  else
  {
  return stream;
 
  }
 }
 
 /// <summary>
 /// http post request
 /// </summary>
 /// <param name="url"></param>
 /// <param name="postData">post data</param>
 /// <returns>Response Stream</returns>
 public static Stream PostStream(string url, string postData)
 {
  var stream = CreateResponse(url, true, postData).GetResponseStream();
  if (stream == null)
  {
 
  throw new Exception("Response error,the response stream is null");
  }
  else
  {
  return stream;
 
  }
 }
 
 
 }
}

獲取首頁數據

?
1
string res = HttpUtil.GetString(<a rel="external nofollow" href="https://www.cnblogs.com">https://www.cnblogs.com</a>);

.NET Core 實現定時抓取網站文章并發送到郵箱

解析數據

我們成功獲取到了html,但是怎么提取我們需要的信息(文章標題、地址、摘要、作者、發布時間)呢。這里就亮出了我們的利劍HtmlAgilityPack,他是一個可以根據xpath來解析網頁的組件。

載入我們前面獲取的html:

?
1
2
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);

.NET Core 實現定時抓取網站文章并發送到郵箱

從上圖中,我們可以看出,每條文章所有信息都在一個class為post_item的div里,我們先獲取所有的class=post_item的div

?
1
2
//獲取所有文章數據項
var itemBodys = doc.DocumentNode.SelectNodes("//div[@class='post_item_body']");

我們繼續分析,可以看出文章的標題在class=post_item_body的div下面的h3標簽下的a標簽,摘要信息在class=post_item_summary的p標簽里面,發布時間和作者在class=post_item_foot的div里,分析完畢,我們可以取出我們想要的數據了:

  1. foreach (var itemBody in itemBodys) 
  2.  //標題元素 
  3.  var titleElem = itemBody.SelectSingleNode("h3/a"); 
  4.  //獲取標題 
  5.  var title = titleElem?.InnerText; 
  6.  //獲取url 
  7.  var url = titleElem?.Attributes["href"]?.Value; 
  8.  
  9.  //摘要元素 
  10.  var summaryElem = itemBody.SelectSingleNode("p[@class='post_item_summary']"); 
  11.  //獲取摘要 
  12.  var summary = summaryElem?.InnerText.Replace(" """).Trim(); 
  13.  
  14.  //數據項底部元素 
  15.  var footElem = itemBody.SelectSingleNode("div[@class='post_item_foot']"); 
  16.  //獲取作者 
  17.  var author = footElem?.SelectSingleNode("a")?.InnerText; 
  18.  //獲取文章發布時間 
  19.  var publishTime = Regex.Match(footElem?.InnerText, "\d+-\d+-\d+ \d+:\d+").Value; 
  20.  Console.WriteLine($"標題:{title}"); 
  21.  Console.WriteLine($"網址:{url}"); 
  22.  Console.WriteLine($"摘要:{summary}"); 
  23.  Console.WriteLine($"作者:{author}"); 
  24.  Console.WriteLine($"發布時間:{publishTime}"); 
  25.  Console.WriteLine("--------------華麗的分割線---------------"); 

運行一下:

.NET Core 實現定時抓取網站文章并發送到郵箱

我們成功的獲取了我們想要的信息。現在我們定義一個Blog對象將它們裝起來。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Blog
{
 /// <summary>
 /// 標題
 /// </summary>
 public string Title { get; set; }
 
 /// <summary>
 /// 博文url
 /// </summary>
 public string Url { get; set; }
 
 /// <summary>
 /// 摘要
 /// </summary>
 public string Summary { get; set; }
 
 /// <summary>
 /// 作者
 /// </summary>
 public string Author { get; set; }
 
 /// <summary>
 /// 發布時間
 /// </summary>
 public DateTime PublishTime { get; set; }
}

http請求失敗重試

我們使用Polly在我們的http請求失敗時進行重試,設置為重試3次。

?
1
2
3
4
5
6
7
8
9
//初始化重試器
_retryTwoTimesPolicy =
 Policy
 .Handle<Exception>()
 .Retry(3, (ex, count) =>
 {
  _logger.Error("Excuted Failed! Retry {0}", count);
  _logger.Error("Exeption from {0}", ex.GetType().Name);
 });

測試一下:

.NET Core 實現定時抓取網站文章并發送到郵箱

可以看到當遇到exception是Polly會幫我們重試三次,如果三次重試都失敗了那么會放棄。

發送郵件

使用MailKit來進行郵件發送,它支持IMAP,POP3和SMTP協議,并且是跨平臺的十分優秀。下面是根據前面園友的分享自己封裝的一個類庫:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using System.Collections.Generic;
using CnBlogSubscribeTool.Config;
using MailKit.Net.Smtp;
using MimeKit;
 
namespace CnBlogSubscribeTool
{
 /// <summary>
 /// send email
 /// </summary>
 public class MailUtil
 {
 private static bool SendMail(MimeMessage mailMessage,MailConfig config)
 {
  try
  {
  var smtpClient = new SmtpClient();
  smtpClient.Timeout = 10 * 1000; //設置超時時間
  smtpClient.Connect(config.Host, config.Port, MailKit.Security.SecureSocketOptions.None);//連接到遠程smtp服務器
  smtpClient.Authenticate(config.Address, config.Password);
  smtpClient.Send(mailMessage);//發送郵件
  smtpClient.Disconnect(true);
  return true;
 
  }
  catch
  {
  throw;
  }
 
 }
 
 /// <summary>
 ///發送郵件
 /// </summary>
 /// <param name="config">配置</param>
 /// <param name="receives">接收人</param>
 /// <param name="sender">發送人</param>
 /// <param name="subject">標題</param>
 /// <param name="body">內容</param>
 /// <param name="attachments">附件</param>
 /// <param name="fileName">附件名</param>
 /// <returns></returns>
 public static bool SendMail(MailConfig config,List<string> receives, string sender, string subject, string body, byte[] attachments = null,string fileName="")
 {
  var fromMailAddress = new MailboxAddress(config.Name, config.Address);
  
  var mailMessage = new MimeMessage();
  mailMessage.From.Add(fromMailAddress);
  
  foreach (var add in receives)
  {
  var toMailAddress = new MailboxAddress(add);
  mailMessage.To.Add(toMailAddress);
  }
  if (!string.IsNullOrEmpty(sender))
  {
  var replyTo = new MailboxAddress(config.Name, sender);
  mailMessage.ReplyTo.Add(replyTo);
  }
  var bodyBuilder = new BodyBuilder() { HtmlBody = body };
 
  //附件
  if (attachments != null)
  {
  if (string.IsNullOrEmpty(fileName))
  {
   fileName = "未命名文件.txt";
  }
  var attachment = bodyBuilder.Attachments.Add(fileName, attachments);
 
  //解決中文文件名亂碼
  var charset = "GB18030";
  attachment.ContentType.Parameters.Clear();
  attachment.ContentDisposition.Parameters.Clear();
  attachment.ContentType.Parameters.Add(charset, "name", fileName);
  attachment.ContentDisposition.Parameters.Add(charset, "filename", fileName);
 
  //解決文件名不能超過41字符
  foreach (var param in attachment.ContentDisposition.Parameters)
   param.EncodingMethod = ParameterEncodingMethod.Rfc2047;
  foreach (var param in attachment.ContentType.Parameters)
   param.EncodingMethod = ParameterEncodingMethod.Rfc2047;
  }
 
  mailMessage.Body = bodyBuilder.ToMessageBody();
  mailMessage.Subject = subject;
 
  return SendMail(mailMessage, config);
 
 }
 }
}

測試一下:

.NET Core 實現定時抓取網站文章并發送到郵箱

說明

關于抓取數據和發送郵件的調度,程序異常退出的數據處理等等,在此我就不詳細說明了,有興趣的看源碼(文末有github地址)

抓取數據是增量更新的。不用RSS訂閱的原因是RSS更新比較慢。

完整的程序運行截圖:

.NET Core 實現定時抓取網站文章并發送到郵箱

每發送一次郵件,程序就會將記錄時間調整到今天的9點,然后每次抓取數據之后就會判斷當前時間減去記錄時間是否大于等于24小時,如果符合就發送郵件并且更新記錄時間。

收到的郵件截圖:

.NET Core 實現定時抓取網站文章并發送到郵箱

截圖中的郵件標題為13日但是郵件內容為14日,是因為我為了演示效果,將今天(14日)的數據copy到了13日的數據里面,不要被誤導了。

還提供一個附件便于收集整理:

.NET Core 實現定時抓取網站文章并發送到郵箱

好了介紹完畢,我自己已經將這個小工具部署到服務器,想要享受這個服務的可以在評論留下郵箱(手動滑稽)。

源碼分享:https://github.com/stulzq/CnBlogSubscribeTool

原文鏈接:https://www.cnblogs.com/stulzq/p/8448183.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 无码国产成人777爽死在线观看 | 国产区1 | 日韩欧美一区二区三区 | 1024亚洲精品国产 | 嫩草精品 | 国产亚洲欧美成人久久片 | 欧美靠逼 | 日本精品一区二区在线播放 | 亚洲日本va午夜中文字幕 | 亚洲精品一区二区三区在线看 | 亚洲欧美在线观看一区二区 | 免费国产在线视频 | 歪歪漫画a漫入口 | 无限资源在线观看8 | 午夜人妻理论片天堂影院 | 好大好湿好硬好爽好深免费视频 | 男女车车好快的车车免费网站 | 国产午夜免费不卡精品理论片 | 超级乱淫变态伦短篇小说全集 | 日本爽p大片免费观看 | 国产欧美一区二区三区免费 | 青草草在线 | 色综合欧美色综合七久久 | 俄罗斯美女尿尿 | japanhd粗暴video | 国产成人免费片在线视频观看 | 性关系视频免费网站在线观看 | 九九精品影院 | 好大好深视频 | 色橹| 超逼网 | 日韩欧免费一区二区三区 | 96日本xxxxxxxxx70 95在线观看精品视频 | 波多野给衣一区二区三区 | 国产免费色视频 | 国产日产在线观看 | 草莓视频网站18勿进 | 青柠影视在线播放观看高清 | 99久久er这里只有精品17 | 99青青青精品视频在线 | 四虎影库紧急大通知 |