NET Core实现了定期抓取网站文章并发送到邮箱
你好,我是陈晓。好久没更新博客了。今天给大家带来一篇干巴巴的文章,一个每5分钟抓取一次博客主页信息,第二天早上9: 00发到你邮箱的小工具。比如2018年2月14日9: 00来公司,收到一封邮件,是2018年2月13日博客公园首页的文章信息。写这个小工具的初衷是一直有看博客的习惯,但是最近因为各种原因,可能好几天都不看博客了。如果错过了什么好文章,我觉得很心疼。于是我做了一个工具,每天归档在邮箱里,妈妈就再也不用担心我错过好文章了。为什么只抢第一页?因为博客公园首页的文章质量相对较高。
前言
作为一个连续运行的工具,没有日志我做不到。我准备用NLog来日志,它有非常好的日志归档功能。在http请求中,可能会因为网络问题而失败。这里我使用波利重试。使用HtmlAgilityPack解析网页需要对xpath有一定的了解。以下是详细描述:组件名称用法github NLog日志记录https://github.com/NLog/NLog波利当http请求失败时,再试一次。https://github.com/App-vNext/Polly html agilitypack网页解析https://github.com/zzzprojects/html-agility-pack.的mailkit发送的邮件https://github.com/jstedfast/MailKit有未知的组件。你可以通过访问github获取信息。
参考文章
//www . JB 51 . net/article/112595 . htm
获取并分析博客公园主页数据
我使用httpWebRequest来发出Http请求。让我们分享我的简单封装类库:
使用系统;使用系统IO;使用系统。净值;使用系统。文字;命名空间cnblogsubscribetol {///summary///Simple Http Request Class///.NET Framework=4.0////作者: stulzq///createdtimes :2017-12-12 15:54:47////摘要公共类HttpUtil { static HttpUtil(){//设置连接限制,默认限制为2服务点管理器.DefaultConnectionLimit=1024 }///summary///默认超时20s////summary public static int默认超时=20000;///summary////是Auto Redirect////summary public static bool defaultallaututoredirect=true;///摘要///默认编码////摘要公共静态编码默认编码=编码.UTF8///summary///默认用户代理////summary公共静态字符串defaultuser代理=' Mozilla/5.0(Windows NT 6.1;win 64x 64)applebwebkit/537.36(KHTML,像壁虎)Chrome/62。0 .3202 .94 Safari/537.36 ';///摘要///默认推荐人////摘要公共静态字符串DefaultReferer=///summary///http get request///summary///param name=' URL ' internet Address/param///返回string/返回公共静态字符串GetString(字符串URL){ var stream=GetStream(URL);字符串结果;使用(流读取器Sr=新的流读取器(流)){结果=Sr . ReadToEnd();}返回结果;}///summary///http set request///summary///param name=' URL ' internet Address/param///param name=' post data ' post request data/param///returns string/返回公共静态字符串PostString(字符串网址,字符串poststrade数据){ var stream=post street(URL,poststrade数据);字符串结果;使用(流读取器Sr=新的流读取器(流)){结果=Sr . ReadToEnd();}返回结果;}///summary////Create Response///summary///param name=' URL '/param///param name=' post '是post Request/param///param name=' post data ' post Request data/param///returns/returns public static WebResponse Create Response(字符串网址,博客帖子,字符串发布数据=' '){ var Httpwebrequest=WebRequest .创建Http(URL);httpWebRequest .超时=默认超时;httpWebRequest .allowteredirect=defaultallaututoredirect;httpWebRequest .用户代理=默认用户代理httpWebRequest .refer=DefaultReferif(post){ var data=Defaultencoding .GetBytes(PostDATa);httpWebRequest .方法=' POSThttpWebRequest .内容类型=' application/x-www-form-URL编码;charset=utf-8 ';httpWebRequest .ContentLength=数据。长度;使用(var stream=httpWebRequest .GetRequestStream()) { stream .写入(数据,0,数据。长度);} }尝试{ var response=httpWebRequest .GetResponse();返回响应;}捕获(异常e){ 0抛出新的异常(字符串。格式('请求错误,url:{0},IsPost:{1},Data:{2},Message:{3},url,Post,postData,e.Message),e);} }///summary////http get request////summary///param name=' URL '/param///返回响应Stream/返回公共静态流GetStream(字符串网址){变量流=创建响应(网址,假).GetResponseStream();if(stream==null){ 0抛出新的异常('响应错误,响应流为null’);} else {返回流;} }///summary////http post request////summary///param name=' URL '/param///param name=' post data ' post data/param///返回响应Stream/返回公共静态流PostStream(字符串网址,字符串发布数据){ var Stream=创建响应(URL,true,发布数据).GetResponseStream();if(stream==null){ 0抛出新的异常('响应错误,响应流为null’);} else {返回流;} } }}获取首页数据
字符串res=HttpUtil .getStrIng(www . https://。cn博客。com);
解析数据
我们成功获取到了html,但是怎么提取我们需要的信息(文章标题、地址、摘要、作者、发布时间)呢。这里就亮出了我们的利剑HtmlAgilityPack,他是一个可以根据语言来解析网页的组件。
载入我们前面获取的html:
HTMl文档=新建HTMl文档();医生.加载HTMl(HTMl);
从上图中,我们可以看出,每条文章所有信息都在一个班级为发布项目的差异里,我们先获取所有的class=post_item的差异
//获取所有文章数据项var itemBodys=文档.文档节点。select nodes('//div[@ class=' post _ item _ body ']');我们继续分析,可以看出文章的标题在class=post_item_body的差异下面的h3标签下的a标签,摘要信息在class=post_item_summary的p标签里面,发布时间和作者在class=post_item_foot的差异里,分析完毕,我们可以取出我们想要的数据了:
foreach(项目正文中的var项目正文){//标题元素var titleElem=itemBody .选择单个节点(“H3/a”);//获取标题var title=titleElem?InnerText//获取url var url=titleElem?属性['href']?价值;//摘要元素var summaryElem=itemBody .选择单个节点(' p[@ class=' post _ item _ summary ']');//获取摘要var summary=summaryElem?InnerText。替换(' \r\n ',' ').trim();//数据项底部元素var footElem=itemBody .选择单个节点(' div[@ class=' post _ item _ foot ']');//获取作者定义变量作者=footElem?选择单个节点(' a ')?InnerText//获取文章发布时间var publishTime=Regex .匹配(footElem?InnerText,' \\d -\\d -\\d \\d :\\d ').价值;控制台WriteLine($)标题:{ title } ');控制台WriteLine($)网址:{ URL } ');控制台WriteLine($)摘要:{ summary } ');控制台WriteLine($)作者:{ author } ');控制台WriteLine($)发布时间:{ PublishTiME } ');控制台WriteLine(' -华丽的分割线- ');}运行一下:
我们成功的获取了我们想要的信息。现在我们定义一个博客对象将它们装起来。
公开课博客{ ///摘要///标题////摘要公共字符串标题{ get设置;} ///摘要///博文URl/摘要公共字符串Url { get设置;} ///摘要///摘要////摘要公共字符串摘要{ get设置;} ///摘要///作者////摘要公共字符串作者{ get设置;} ///摘要///发布时间////汇总公共日期时间发布时间{获取设置;}}http请求失败重试
我们使用波莉在我们的超文本传送协议(超文本传输协议的缩写)请求失败时进行重试,设置为重试3次。
//初始化重试器_ retryTwoTimesPolicy=Policy .HandleException().重试(3,(例如,计数)={ _logger .错误('执行失败!重试{0} ',计数);_记录器.错误("{0}"中的执行选项,例如GetType().名称);});测试一下:
可以看到当遇到例外是波莉会帮我们重试三次,如果三次重试都失败了那么会放弃。
发送邮件
使用邮件套件来进行邮件发送,它支持IMAP,POP3和简单邮件传输协议协议,并且是跨平台的十分优秀。下面是根据前面园友的分享自己封装的一个类库:
使用系统。集合。通用;使用CnBlogSubscribeTool .配置使用邮件包.Net。简单邮件传输协议使用MimeKit命名空间cnblogsubscribetol {///summary///send email///summary public class MailUtil { private static bool SendMail(MiMemessage,Mailconfig config){ try { var smtpClient=new smtpClient();smtpClient .超时=10 * 1000;//设置超时时间smtpClient .连接(配置。主机,配置。港口,邮件箱安全。安全检查。无);//连接到远程简单邮件传输协议服务器smtpClient .验证(配置。地址,配置。密码);smtpClient .发送(邮件信息);//发送邮件smtpClient .断开连接(真);返回真;}接球{投掷;} } ///摘要///发送邮件////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///返回/返回公共静态bool SendMail(Mailconfig配置,Liststring receives,string sender,string subject,string body,byte[] attachments=null,string fileName=' '){ var FromMailAddress=新邮箱地址(config .名称、配置。地址);var mail message=new MiMe message();邮件信息。发件人。添加(发件人邮件地址);foreach(var add in receive){ var to邮箱地址=新邮箱地址(add);邮件信息到。添加(到邮件地址);} if(!字符串IsNullOrEmpty(发件人)){ var replyTo=new MailboxAddress(配置。姓名、发件人);邮件信息。回复添加(回复);} var健美运动员=新健美运动员(){ html正文=body };//附件如果(附件!=null) { if(字符串IsNullOrEmpty(FIlename)){ FIlename='未命名文件. txt ';} var附件=健美运动员。附件。添加(文件名,附件);//解决中文文件名乱码var charset=' GB18030附件内容类型。参数。clear();附件内容处置。参数。clear();附件内容类型。参数。添加(字符集,"名称",文件名);附件内容处置。参数。添加(字符集,' filename ',FIlename);//解决文件名不能超过41字符附件中的变量参数。参数)参数。编码方法=参数编码方法Rfc2047附件中的变量参数。参数)参数。编码方法=参数编码方法Rfc2047}邮件信息正文=健美运动员. TomessageBody();邮件信息。主语=主语;返回SendMail(邮件消息,配置);} }}测试一下:
说明
关于抓取数据和发送邮件的调度,程序异常退出的数据处理等等,在此我就不详细说明了,有兴趣的看源码(文末有开源代码库地址)
抓取数据是增量更新的。不用简易资讯聚合订阅的原因是简易资讯聚合更新比较慢。
完整的程序运行截图:
每发送一次邮件,程序就会将记录时间调整到今天的9点,然后每次抓取数据之后就会判断当前时间减去记录时间是否大于等于24小时,如果符合就发送邮件并且更新记录时间。
收到的邮件截图:
截图中的邮件标题为13日但是邮件内容为14日,是因为我为了演示效果,将今天(14日)的数据复制到了13日的数据里面,不要被误导了。
还提供一个附件便于收集整理:
好了介绍完毕,我自己已经将这个小工具部署到服务器,想要享受这个服务的可以在评论留下邮箱(手动滑稽)。
源码分享:https://github。com/stulzq/cnblogsubscribetol
版权声明:NET Core实现了定期抓取网站文章并发送到邮箱是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。