基于 abp vNext 和 .NET Core 开发博客项目 - 定时任务最佳实战(三)
上一篇(https://www.cnblogs.com/meowv/p/12974439.html)完成了全网各大平台的热点新闻数据的抓取,本篇继续围绕抓取完成后的操作做一个提醒。当每次抓取完数据后,自动发送邮件进行提醒。
在开始正题之前还是先玩一玩之前的说到却没有用到的一个库PuppeteerSharp
。
PuppeteerSharp
:Headless Chrome .NET API ,它运用最多的应该是自动化测试和抓取异步加载的网页数据,更多介绍可以看GitHub:https://github.com/hardkoded/puppeteer-sharp 。
我这里主要来试试它的异步抓取功能,同时它还能帮我们生成网页截图或者PDF。
如果没有安装可以先安装一下,在.BackgroundJobs
层安装PuppeteerSharp
:Install-Package PuppeteerSharp
在Jobs文件夹下新建一个PuppeteerTestJob.cs
,继承IBackgroundJob
,同样是在ExecuteAsync()
方法中执行操作。
//PuppeteerTestJob.cs
using System;
using System.Threading.Tasks;
namespace Meowv.Blog.BackgroundJobs.Jobs.PuppeteerTest
{
public class PuppeteerTestJob : IBackgroundJob
{
public async Task ExecuteAsync()
{
throw new NotImplementedException();
}
}
}
使用 await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
第一次检测到没有浏览器文件会默认帮我们下载 chromium 浏览器。
DownloadAsync(...)
可以指定 Chromium 版本,BrowserFetcher.DefaultRevision
下载当前默认最稳定的版本。
然后配置浏览器启动的方式。
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new string[] { "--no-sandbox" }
});
感兴趣的可以自己看看LaunchOptions
有哪些参数,我这里指定了Headless = true
以无头模式运行浏览器,然后加了一个启动参数 "--no-sandbox"。针对Linux环境下,如果是运行在 root 权限下,在启动 Puppeteer 时要添加 "--no-sandbox" 参数,否则 Chromium 会启动失败。
我们打开一个异步加载的网页,然后获取到页面加载完后的HTML,以我个人博客中的某个单页为例:https://meowv.com/wallpaper 。
//PuppeteerTestJob.cs
using PuppeteerSharp;
using System.Threading.Tasks;
namespace Meowv.Blog.BackgroundJobs.Jobs.PuppeteerTest
{
public class PuppeteerTestJob : IBackgroundJob
{
public async Task ExecuteAsync()
{
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new string[] { "--no-sandbox" }
});
using var page = await browser.NewPageAsync();
await page.SetViewportAsync(new ViewPortOptions
{
Width = 1920,
Height = 1080
});
var url = "https://meowv.com/wallpaper";
await page.GoToAsync(url, WaitUntilNavigation.Networkidle0);
var content = await page.GetContentAsync();
}
}
}
page.SetViewportAsync()
设置网页预览大小,page.GoToAsync()
语法打开网页,WaitUntilNavigation.Networkidle0
等待网页加载完毕,使用page.GetContentAsync()
获取到HTML。
新建扩展方法,调用这个PuppeteerTestJob
的ExecuteAsync()
方法,调试看看效果。
HTML已经出来了,此时该干嘛就干嘛就可以了。
第一次运行可能会很慢,因为如果你本地不存在 Chromium 是会去帮我们下载的,因为网络原因可能会下载的很慢,所以推荐大家手动下载。
可以使用淘宝的源:https://npm.taobao.org/mirrors/chromium-browser-snapshots/ 。
要注意的是,下载完成后的解压的路径不能出错,默认下载地址是在启动目录下面。
Windows:..\.local-chromium\Win64-706915\chrome-win
、 Linux:../.local-chromium/Linux-706915/chrome-linux
接下来试试生成PDF和保存图片功能,使用方式也很简单。
await page.PdfAsync("meowv.pdf",new PdfOptions { });
await page.ScreenshotAsync("meowv.png", new ScreenshotOptions
{
FullPage = true,
Type = ScreenshotType.Png
});
这里只做简单的展示,page.PdfAsync()
直接生成PDF文件,同时还有很多方法可以自己调用page.
试试,PdfOptions
选项中可以设置各种参数。
page.ScreenshotAsync()
保存图片,ScreenshotOptions
中FullPage可以设置保存图片为全屏模式,图片格式为Png类型。
可以看到项目根目录已经生成了图片和PDF,感觉去试试吧。
接下里来实现发送邮件的功能。
我这里发邮件的账号是用的腾讯企业邮箱,也可以用普通邮箱开通SMTP服务即可。
在appsettings.json
配置收发邮件的账号等信息。
//appsettings.json
"Email": {
"Host": "smtp.exmail.qq.com",
"Port": 465,
"UseSsl": true,
"From": {
"Username": "123@meowv.com",
"Password": "[Password]",
"Name": "MEOWV.COM",
"Address": "123@meowv.com"
},
"To": [
{
"Name": "test1",
"Address": "test1@meowv.com"
},
{
"Name": "test2",
"Address": "test2@meowv.com"
}
]
}
然后再AppSettings
中读取配置的项。
//AppSettings.cs
public static class Email
{
/// <summary>
/// Host
/// </summary>
public static string Host => _config["Email:Host"];
/// <summary>
/// Port
/// </summary>
public static int Port => Convert.ToInt32(_config["Email:Port"]);
/// <summary>
/// UseSsl
/// </summary>
public static bool UseSsl => Convert.ToBoolean(_config["Email:UseSsl"]);
/// <summary>
/// From
/// </summary>
public static class From
{
/// <summary>
/// Username
/// </summary>
public static string Username => _config["Email:From:Username"];
/// <summary>
/// Password
/// </summary>
public static string Password => _config["Email:From:Password"];
/// <summary>
/// Name
/// </summary>
public static string Name => _config["Email:From:Name"];
/// <summary>
/// Address
/// </summary>
public static string Address => _config["Email:From:Address"];
}
/// <summary>
/// To
/// </summary>
public static IDictionary<string, string> To
{
get
{
var dic = new Dictionary<string, string>();
var emails = _config.GetSection("Email:To");
foreach (IConfigurationSection section in emails.GetChildren())
{
var name = section["Name"];
var address = section["Address"];
dic.Add(name, address);
}
return dic;
}
}
}
分别介绍下每项的含义:
Host
:发送邮件服务器地址。Port
:服务器地址端口号。UseSsl
:是否使用SSL方式。From
:发件人的账号密码,名称及邮箱地址,一般邮箱地址和账号是相同的。To
:收件人邮箱列表,也包含名称和邮箱地址。
收件人邮箱列表我将其读取为IDictionary<string, string>
了,key是名称,value是邮箱地址。
接着在.ToolKits
层添加一个EmailHelper.cs
,收发邮件我选择了MailKit
和MailKit
两个库,没有安装的先安装一下,Install-Package MailKit
、Install-Package MimeKit
。
直接新建一个发送邮件的方法SendAsync()
,按照要求将基本的配置信息填进去,然后直接调用即可。
//EmailHelper.cs
using MailKit.Net.Smtp;
using Meowv.Blog.Domain.Configurations;
using MimeKit;
using System.Linq;
using System.Threading.Tasks;
namespace Meowv.Blog.ToolKits.Helper
{
public static class EmailHelper
{
/// <summary>
/// 发送Email
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public static async Task SendAsync(MimeMessage message)
{
if (!message.From.Any())
{
message.From.Add(new MailboxAddress(AppSettings.Email.From.Name, AppSettings.Email.From.Address));
}
if (!message.To.Any())
{
var address = AppSettings.Email.To.Select(x => new MailboxAddress(x.Key, x.Value));
message.To.AddRange(address);
}
using var client = new SmtpClient
{
ServerCertificateValidationCallback = (s, c, h, e) => true
};
client.AuthenticationMechanisms.Remove("XOAUTH2");
await client.ConnectAsync(AppSettings.Email.Host, AppSettings.Email.Port, AppSettings.Email.UseSsl);
await client.AuthenticateAsync(AppSettings.Email.From.Username, AppSettings.Email.From.Password);
await client.SendAsync(message);
await client.DisconnectAsync(true);
}
}
}
SendAsync(...)
接收一个参数MimeMessage
对象,这样就完成了一个通用的发邮件方法,接着我们去需要发邮件的地方构造MimeMessage
,调用SendAsync()
。
//WallpaperJob.cs
...
// 发送Email
var message = new MimeMessage
{
Subject = "【定时任务】壁纸数据抓取任务推送",
Body = new BodyBuilder
{
HtmlBody = $"本次抓取到{wallpapers.Count()}条数据,时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss}"
}.ToMessageBody()
};
await EmailHelper.SendAsync(message);
...
//HotNewsJob.cs
...
// 发送Email
var message = new MimeMessage
{
Subject = "【定时任务】每日热点数据抓取任务推送",
Body = new BodyBuilder
{
HtmlBody = $"本次抓取到{hotNews.Count()}条数据,时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss}"
}.ToMessageBody()
};
await EmailHelper.SendAsync(message);
...
分别在两个爬虫脚本中添加发送Email,MimeMessage
中设置了邮件主题Subject
,正文Body
,最后调用await EmailHelper.SendAsync(message)
执行发送邮件操作。
编译运行执行两个定时任务,看看能否收到邮件提醒。
成功了,邮箱收到了两条提醒。
还有一种比较特殊的用法,也介绍一下,如果想要发送带图片的邮件怎么操作呢?注意不是附件,是将图片内嵌在邮箱中。
一般常规都是有邮件模板的,将图片的具体地址插入到img标签中,这就不说了,这里选择另外一种方式。以前面添加的PuppeteerTestJob
为例,正好我们生成了一张图片的。将这种图片以邮件的形式发出去。
public class PuppeteerTestJob : IBackgroundJob
{
public async Task ExecuteAsync()
{
var path = Path.Combine(Path.GetTempPath(), "meowv.png");
...
await page.ScreenshotAsync(path, new ScreenshotOptions
{
FullPage = true,
Type = ScreenshotType.Png
});
// 发送带图片的Email
var builder = new BodyBuilder();
var image = builder.LinkedResources.Add(path);
image.ContentId = MimeUtils.GenerateMessageId();
builder.HtmlBody = "当前时间:{0}.<img src=\"cid:{1}\"/>".FormatWith(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), image.ContentId);
var message = new MimeMessage
{
Subject = "【定时任务】每日热点数据抓取任务推送",
Body = builder.ToMessageBody()
};
await EmailHelper.SendAsync(message);
}
}
先确定我们生成图片的路径 path ,将图片生成Message-Id,然后赋值给ContentId,给模板中<img src=\"cid:{1}\"/>
图片标签cid
赋上值在调用发送邮件方法即可。
成功收到邮件,搞定了,你学会了吗?
开源地址:https://github.com/Meowv/Blog/tree/blog_tutorial
基于 abp vNext 和 .NET Core 开发博客项目,截止到本篇所用到的基础模块算是写完了,如果对您有些许帮助请多多分享,我的所有原创文章都首发于我发个人公众号:阿星Plus 。
下面有二维码可以直接扫一扫,如果你不想关注也没有关系,博客园我也会同步过来的。
不管因为什么,如果你在学习这个项目或者跟着我一起做这个项目,里面肯定还是有瑕疵的,大家可以根据自己的需求自行修改。
接下来应该还会更新博客所用到的接口,这个纯属于CRUD,可以自己先行开发,我这边目前也不知道以什么样的方式展现给大家是最好的选择。
基于 abp vNext 和 .NET Core 开发博客项目 - 定时任务最佳实战(三)的更多相关文章
- 基于 abp vNext 和 .NET Core 开发博客项目 - 定时任务最佳实战(二)
上一篇(https://www.cnblogs.com/meowv/p/12971041.html)使用HtmlAgilityPack抓取壁纸数据成功将图片存入数据库,本篇继续来完成一个全网各大平台的 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 定时任务最佳实战(一)
上一篇(https://www.cnblogs.com/meowv/p/12966092.html)文章使用AutoMapper来处理对象与对象之间的映射关系,本篇主要围绕定时任务和数据抓取相关的知识 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(一)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(二)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(三)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(四)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(五)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(一)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(二)
系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...
随机推荐
- thinkphp历史漏洞
https://github.com/pochubs/pochubs/blob/master/ThinkPHP.md tp 历史漏洞 路由控制類RCE/think/App.php if (!preg_ ...
- 高性能mysql第三版读书笔记3
innodb以前不支持高并发,在搞病房下就是悲剧,全部卡在mutex(缓冲池mutex)上,现在通过线程调度器控制线程怎么进入内核访问数据,参数为innodb_thread_concurrency,它 ...
- 拒绝老土!暗黑风格半透平面化主题—InfinityFreedom正式发布
经常听到“路由器界面土点就土点吧,凑合能用就成.” 诚然,路由器重要的是功能,但为什么要辣眼睛呢? 拯救喜欢折腾的你,抢救干涩的眼球,原创OpenWrt主题Infinity Freedom正式发布! ...
- Vue + Element-ui实现后台管理系统(1) --- 总述
总述 一.项目效果 整体效果 登陆页 后台首页 用户管理页 说明 这里所有的数据都不是直接通过后端获取的, 而是通过Mock这个工具来模拟后端返回的接口数据. 附上github地址: mall-ma ...
- FPGA学习心得汇总(手中写代码,心中有电路)
http://bbs.ednchina.com/BLOG_ARTICLE_2111172.HTM 任何的时序逻辑都可以转换为组合逻辑+D触发器来完成. FPGA内部主要三块:可编程的逻辑单元.可编程的 ...
- Semaphore和AQS
Semaphore意思的信号量,它的作用是控制访问特定资源的线程数量 构造方法: public Semaphore(int permits) public Semaphore(int permits, ...
- 2018-08-27 jq筛选选择器
筛选选择器:为了辅助选择器更简便.快速的找到元素: 1.过滤 eq(n) -> 第n个元素(从零开始) $('h1').eq(2) // 第三个h1 first() -> 第一个元素 la ...
- ES6-10笔记(一)
大纲 (慕课的图先用着) scope-作用域 作用域是指程序源代码中定义变量的区域,规定了如何查找变量,也就是确定当前执行代码对变量的访问权限. JavaScript作用域共有两种主要的工作模型--词 ...
- filebeat-kafka:WARN producer/broker/0 maximum request accumulated, waiting for space
You need to configure 3 things: Brokers Filebeat kafka output Consumer Here a example (change paths ...
- 最简单的git 用法
步骤 在机器上ssh-keygen 然后把id_rsa.pub 复制到csdn 的公钥上去. git clone git@code.csdn.net:aca_jingru/zabbix.git 如果这 ...