基于 abp vnext 和 .net core 开发博客项目 -凯发k8官方网
基于 abp vnext 和 .net core 开发博客项目 - 定时任务最佳实战(一)
转载于:https://github.com/meowv/blog
本篇主要围绕定时任务和数据抓取相关的知识点并结合实际应用,在定时任务中循环处理爬虫任务抓取数据。
开始之前可以删掉之前测试用的几个helloworld,没有什么实际意义,直接干掉吧。抓取数据我主要用到了,htmlagilitypack和puppeteersharp,一般情况下htmlagilitypack就可以完成大部分的数据抓取需求了,当在抓取动态网页的时候可以用到puppeteersharp,同时puppeteersharp还支持将图片保存为图片和pdf等牛逼的功能。
关于这两个库就不多介绍了,不了解的请自行去学习。
先在.backgroundjobs层安装两大神器:install-package htmlagilitypack、install-package puppeteersharp。我在使用package manager安装包的时候一般都不喜欢指定版本号,因为这样默认是给我安装最新的版本。
之前无意中发现爱思助手的网页版有很多手机壁纸(https://www.i4.cn/wper_4_0_1_1.html),于是我就动了小心思,把所有手机壁纸全部抓取过来自嗨,可以看看我个人博客中的成品吧:https://meowv.com/wallpaper 😝😝😝
图片
最开始我是用python实现的,现在我们在.net中抓它。
我数了一下,一共有20个分类,直接在.domain.shared层添加一个壁纸分类的枚举wallpaperenum.cs。
//wallpaperenum.cs
using system.componentmodel;
namespace meowv.blog.domain.shared.enum
{
public enum wallpaperenum
{
[description(“美女”)]
beauty = 1,
}
查看原网页可以很清晰的看到,每一个分类对应了一个不同的url,于是手动创建一个抓取的列表,列表内容包括url和分类,然后我又想用多线程来访问url,返回结果。新建一个通用的待抓项的类,起名为:wallpaperjobitem.cs,为了规范和后续的壁纸查询接口,我们放在.application.contracts层中。
//wallpaperjobitem.cs
using meowv.blog.domain.shared.enum;
namespace meowv.blog.application.contracts.wallpaper
{
public class wallpaperjobitem
{
///
///
///
public t result { get; set; }
}
wallpaperjobitem接受一个参数t,result的类型由t决定,在.backgroundjobs层jobs文件夹中新建一个任务,起名叫做:wallpaperjob.cs吧。老样子,继承ibackgroundjob。
//wallpaperjob.cs
using meowv.blog.application.contracts.wallpaper;
using meowv.blog.domain.shared.enum;
using system.collections.generic;
using system.threading.tasks;
namespace meowv.blog.backgroundjobs.jobs.wallpaper
{
public class wallpaperjob : ibackgroundjob
{
public async task executeasync()
{
var wallpaperurls = new list
{
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_1_1.html”, type = wallpaperenum.beauty },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_58_1.html”, type = wallpaperenum.sportsman },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_66_1.html”, type = wallpaperenum.cutebaby },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_4_1.html”, type = wallpaperenum.emotion },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_3_1.html”, type = wallpaperenum.landscape },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_9_1.html”, type = wallpaperenum.animal },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_13_1.html”, type = wallpaperenum.plant },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_64_1.html”, type = wallpaperenum.food },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_11_1.html”, type = wallpaperenum.movie },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_5_1.html”, type = wallpaperenum.anime },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_34_1.html”, type = wallpaperenum.handpainted },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_65_1.html”, type = wallpaperenum.text },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_2_1.html”, type = wallpaperenum.creative },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_10_1.html”, type = wallpaperenum.car },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_14_1.html”, type = wallpaperenum.physicaleducation },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_63_1.html”, type = wallpaperenum.military },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_17_1.html”, type = wallpaperenum.festival },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_15_1.html”, type = wallpaperenum.game },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_12_1.html”, type = wallpaperenum.apple },
new wallpaperjobitem { result = “https://www.i4.cn/wper_4_19_7_1.html”, type = wallpaperenum.other }
};
}
}
}
先构建一个要抓取的列表 wallpaperurls,这里准备用 htmlagilitypack,默认只抓取第一页最新的数据。
public async task runasync()
{
…
}
上面这段代码,先new了一个htmlweb对象,我们主要用这个对象去加载我们的url。
web.loadfromwebasync(…),它会返回一个htmldocument对象,这样就和上面的list_task对应起来,从而也应证了前面添加的wallpaperjobitem是通用的一个待抓项的类。
循环处理 wallpaperurls,等待所有请求完成。这样就拿到了20个htmldocument,和它的分类,接下来就可以去处理list_task就行了。
在开始处理之前,要想好抓到的图片数据存放在哪里?我这里还是选择存在数据库中,因为有了之前的自定义仓储之增删改查的经验,可以很快的处理这件事情。
添加实体类、自定义仓储、dbset、code-first等一些列操作,就不一一介绍了,我相信看过之前文章的人都能完成这一步。
wallpaper实体类包含主键guid,标题title,图片地址url,类型type,和一个创建时间createtime。
自定义仓储包含一个批量插入的方法:bulkinsertasync(…)。
贴一下完成后的图片,就不上代码了,如果需要可以去github获取。
图片
回到wallpaperjob,因为我们要抓取的是图片,所以获取到html中的img标签就可以了。
图片
查看源代码发现图片是一个列表呈现的,并且被包裹在//article[@id=‘wper’]/div[@class=‘jbox’]/div[@class=‘kbox’]下面,学过xpath语法的就很容易了,关于xpath语法这里也不做介绍了,对于不会的这里有一篇快速入门的文章:xpath语法 。
利用xpath helper工具我们在浏览器上模拟一下选择的节点是否正确。
图片
使用//article[@id=‘wper’]/div[@class=‘jbox’]/div[@class=‘kbox’]/div/a/img可以成功将图片高亮,说明我们的语法是正确的。
public async task runasync()
{
…
}
在 foreach 循环中先拿到当前循环的item对象,即wallpaperjobitem。
通过.documentnode.selectnodes()语法获取到图片列表,因为在a标签下面有两个img标签,取第一个即可。
getattributevalue()是htmlagilitypack的扩展方法,用于直接获取属性值。
在看图片的时候,发现图片地址的规则是根据时间戳生成的,于是用trytodatetime()扩展方法将其处理转换成时间格式。
这样我们就将所有图片按分类存进了列表当中,接下来调用批量插入方法。
在构造函数中注入自定义仓储iwallpaperrepository。
…
private readonly iwallpaperrepository _wallpaperrepository;
…
…
var urls = (await _wallpaperrepository.getlistasync()).select(x => x.url);
wallpapers = wallpapers.where(x => !urls.contains(x.url)).tolist();
if (wallpapers.any())
{
await _wallpaperrepository.bulkinsertasync(wallpapers);
}
因为抓取的图片可能存在重复的情况,我们需要做一个去重处理,先查询到数据库中的所有的url列表,然后在判断抓取到的url是否存在,最后调用bulkinsertasync(…)批量插入方法。
这样就完成了数据抓取的全部逻辑,在保存数据到数据库之后我们可以进一步操作,比如:写日志、发送邮件通知等等,这里大家自由发挥吧。
写一个扩展方法每隔3小时执行一次。
…
public static void usewallpaperjob(this iserviceprovider service)
{
var job = service.getservice();
recurringjob.addorupdate(“壁纸数据抓取”, () => job.executeasync(), crontype.hour(1, 3));
}
…
最后在模块内中调用。
…
public override void onapplicationinitialization(applicationinitializationcontext context)
{
…
service.usewallpaperjob();
}
编译运行,打开hangfire界面手动执行看看效果。
图片
完美,数据库已经存入了不少数据了,还是要提醒一下:爬虫有风险,抓数需谨慎。
hangfire定时处理爬虫任务,用htmlagilitypack抓取数据后存入数据库,你学会了吗?😁😁😁
开源地址:https://github.com/meowv/blog/tree/blog_tutorial
总结
以上是凯发k8官方网为你收集整理的基于 abp vnext 和 .net core 开发博客项目 - 定时任务最佳实战(一)的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 基于 abp vnext 和 .net
- 下一篇: 基于 abp vnext 和 .net