series

  1. Develop blog projects based on ABP vNext and.NET Core – build projects using ABP CLI
  2. Develop blog projects based on ABP vNext and.NET Core – slim the project down and make it run
  3. Development blog project based on ABP vNext and.NET Core – Refinement and beautification, Swagger enter
  4. Develop blog project based on ABP vNext and.NET Core – data access and code priority
  5. Development blog project based on ABP vNext and.NET Core – add, delete, change and check custom warehouse
  6. Develop blog project based on ABP vNext and.NET Core – Uniform specification API, wrapper back model
  7. Develop blog projects based on ABP vNext and.NET Core – say Swagger, grouping, description, little green lock
  8. Develop blog projects based on ABP vNext and.NET Core – access GitHub and protect your API with JWT
  9. Develop blog project based on ABP vNext and.NET Core – exception handling and logging
  10. Develop blog projects based on ABP vNext and.NET Core – using Redis to cache data
  11. Develop blog project based on ABP vNext and.NET Core – integrate Hangfire for timed task processing
  12. Develop blog projects based on ABP vNext and.NET Core – Use AutoMapper to map objects
  13. Developing blog projects based on ABP vNext and.NET Core – Best Practices for Timed Tasks (Part 1)
  14. Developing blog projects based on ABP vNext and.NET Core – Best Practices for Timed Tasks (Part 2)
  15. Developing blog projects based on ABP vNext and.NET Core – Best Practices for Timed Tasks (PART 3)
  16. Blog Development project based on ABP vNext and.NET Core
  17. Abp vNext and.NET Core
  18. Blog Development project based on ABP vNext and.NET Core
  19. Blog Development project based on ABP vNext and.NET Core
  20. Blog Development project based on ABP vNext and.NET Core
  21. Abp vNext and.NET Core Development Blog Project – Blazor
  22. Abp vNext and.NET Core Development Blog Project – Blazor – Part 2
  23. Abp vNext and.NET Core Development Blog Project – Blazor
  24. Abp vNext and.NET Core Development Blog Project – Blazor
  25. Abp vNext and.NET Core Development Blog Project – Blazor
  26. Abp vNext and.NET Core Development Blog Project – Blazor – Part 6
  27. Abp vNext and.NET Core Development Blog Project – Blazor
  28. Abp vNext and.NET Core Development Blog Project – Blazor Series (8)
  29. Abp vNext and.NET Core Development Blog Project – Blazor Series (9)
  30. Abp vNext and.NET Core development blog project – Final release project

An article on https://juejin.cn/post/6844904174950285320), the major completed the entire network platform of the hot news data capture, we continue to revolve around grab after the completion of the operation for a reminder. When the data is captured, an email is automatically sent to remind you.

Before we get down to business, let’s play PuppeteerSharp.

PuppeteerSharp: Headless Chrome.net API, PuppeteerSharp: Headless Chrome.NET API, PuppeteerSharp: Headless Chrome.NET API, PuppeteerSharp: Headless Chrome.NET API, PuppeteerSharp: Headless Chrome.NET API, PuppeteerSharp: Headless Chrome. .

I mainly here to try its asynchronous crawl function, it can also help us to generate web screenshots or PDF.

If not, you can install it first. Install PuppeteerSharp on the BackgroundJobs layer: install-package PuppeteerSharp

Create a new puppeteerTestJob.cs in the Jobs folder that inherits IBackgroundJob and performs the operation in the ExecuteAsync() method.

//PuppeteerTestJob.cs
using System;
using System.Threading.Tasks;

namespace Meowv.Blog.BackgroundJobs.Jobs.PuppeteerTest
{
    public class PuppeteerTestJob : IBackgroundJob
    {
        public async Task ExecuteAsync()
        {
            throw newNotImplementedException(); }}}Copy the code

Using await new BrowserFetcher (.) DownloadAsync (BrowserFetcher. DefaultRevision); The first time we detect that there is no browser file, we will download chromium browser by default.

DownloadAsync(…) You can specify the Chromium version, BrowserFetcher DefaultRevision download the current the most stable version by default.

Then configure how the browser starts.

using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
    Headless = true,
    Args = new string[] { "--no-sandbox"}});Copy the code

I specify Headless = true to run the browser in Headless mode, and then add a startup parameter “–no-sandbox”. In Linux, if the Puppeteer is running under root permission, you need to add “–no-sandbox” when you start Puppeteer, otherwise Chromium will fail to start.

We open a web page that loads asynchronously and get the HTML after the page loads. Take a single page from my personal blog: 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 = awaitpage.GetContentAsync(); }}}Copy the code

Page. SetViewportAsync () preview Settings page size, page. GoToAsync () syntax to open the web page, WaitUntilNavigation.Net workidle0 waiting for web pages to load, Get the HTML using Page.getContentAsync ().

Create a new extension method, call the ExecuteAsync() method of PuppeteerTestJob, and debug to see what happens.

Now that the HTML is out, you can do whatever you want.

The first run may be very slow, because if you don’t have Chromium locally, you will help us download it. Because of network reasons, the download may be very slow, so we recommend manual download.

Source: can use taobao npm.taobao.org/mirrors/chr… .

Note that the decompression path after downloading can not be wrong, the default download address is under the boot directory.

Windows:.. \. Local-chromium \Win64-706915\ Chrome-Win, Linux:.. /.local-chromium/Linux-706915/chrome-linux

Next, try generating PDF and saving images. It’s easy to use.

await page.PdfAsync("meowv.pdf".new PdfOptions { });
await page.ScreenshotAsync("meow.png".new ScreenshotOptions
{
    FullPage = true,
    Type = ScreenshotType.Png
});
Copy the code

For a quick demonstration, page.pdfasync () generates PDF files directly, and there are many ways to call page yourself. Try it. You can set various parameters in the PdfOptions option.

ScreenshotAsync() saves images. ScreenshotOptions FullPage can be set to save images in full-screen mode and Png format.

You can see that the project root directory has generated images and PDF, so I feel like giving it a try.

Next, let’s implement the function of sending emails.

The email account I use here is Tencent enterprise email, or I can use ordinary email to open SMTP service.

Configure information such as email accounts in appSettings. json.

//appsettings.json
  "Email": {
    "Host": "smtp.exmail.qq.com",
    "Port": 465,
    "UseSsl": true,
    "From": {
      "Username": "[email protected]",
      "Password": "[Password]",
      "Name": "MEOWV.COM",
      "Address": "[email protected]"
    },
    "To": [
      {
        "Name": "test1",
        "Address": "[email protected]"
      },
      {
        "Name": "test2",
        "Address": "[email protected]"
      }
    ]
  }
Copy the code

Then read the configured items in 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);
            }
            returndic; }}}Copy the code

The following are the meanings of each item:

  • Host: Indicates the address of the sending mail server.
  • Port: Server address Port number.
  • UseSsl: Indicates whether to use SSL.
  • From: Sender’s account password, name, and email address. Generally, the email address and account are the same.
  • To: Recipient email list, including name and email address.

I read the recipient email list as IDictionary

where key is the name and value is the email address.
,>

Add emailHelper. cs to the.ToolKits layer. I selected MailKit and MailKit libraries.

Create a new mail sending method, SendAsync(), fill in the basic configuration information as required, and call it.

//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>
        ///To send E-mail.
        /// </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); }}}Copy the code

SendAsync(…) Receiving a parameter MimeMessage object completes a generic mail sending method, then we construct MimeMessage where we need to send mail, calling SendAsync().

//WallpaperJob.cs./ / send Email
    var message = new MimeMessage
    {
        Subject = "[Scheduled task] Wallpaper capture task push",
        Body = new BodyBuilder
        {
            HtmlBody = $" this time fetch{wallpapers.Count()}Piece of data, time:{DateTime.Now:yyyy-MM-dd HH:mm:ss}"
        }.ToMessageBody()
    };
    awaitEmailHelper.SendAsync(message); .Copy the code
//HotNewsJob.cs./ / send Email
    var message = new MimeMessage
    {
        Subject = "[Scheduled task] Daily hotspot data capture task push",
        Body = new BodyBuilder
        {
            HtmlBody = $" this time fetch{hotNews.Count()}Piece of data, time:{DateTime.Now:yyyy-MM-dd HH:mm:ss}"
        }.ToMessageBody()
    };
    awaitEmailHelper.SendAsync(message); .Copy the code

Send Email was added in the two crawler scripts respectively. Mail Subject and Body were set in MimeMessage. Finally, call await emailHelper. SendAsync(message) to send mail.

The compile run performs two scheduled tasks to see if it receives an email alert.

It worked. I got two alerts in the mail.

There is also a special use, also introduced, if you want to send an email with a picture how to do? Note that this is not an attachment, but an image embedded in the mailbox.

Instead of inserting the address of the image into the IMG tag, which is usually done with an email template, choose a different approach. Take the example of PuppeteerTestJob, which was added earlier. Send the picture by email.

public class PuppeteerTestJob : IBackgroundJob
{
    public async Task ExecuteAsync()
    {
        var path = Path.Combine(Path.GetTempPath(), "meow.png"); .await page.ScreenshotAsync(path, new ScreenshotOptions
        {
            FullPage = true,
            Type = ScreenshotType.Png
        });

        // Send an Email with a picture
        var builder = new BodyBuilder();

        var image = builder.LinkedResources.Add(path);
        image.ContentId = MimeUtils.GenerateMessageId();

        builder.HtmlBody = ".FormatWith(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), image.ContentId);

        var message = new MimeMessage
        {
            Subject = "[Scheduled task] Daily hotspot data capture task push",
            Body = builder.ToMessageBody()
        };
        awaitEmailHelper.SendAsync(message); }}Copy the code

Got the email. Done. Did you get it? ๐Ÿ˜ ๐Ÿ˜ ๐Ÿ˜

Open source: github.com/Meowv/Blog/…


Based on abP vNext and.NET Core development blog project, the basic modules used in this paper have been finished by now. If you have any help, please share more. All my original articles were published on my personal public account: STAR Plus.

There is a TWO-DIMENSIONAL code below you can directly scan, if you don’t want to pay attention to it, it doesn’t matter, I will also synchronous blog park over.

Whatever the reason, if you are studying this project or working with me on this project, there are still some flaws in it, you can modify them according to your own needs.

Next, I will update the interface used by the blog, which is purely CRUD and can be developed by myself. I don’t know what is the best way to show it to everyone.