网站程序自带的采集器采集文章(如何制作一只简单的Web爬虫的基础知识系统?-八维教育)
优采云 发布时间: 2021-11-10 19:06网站程序自带的采集器采集文章(如何制作一只简单的Web爬虫的基础知识系统?-八维教育)
在前面的章节中,我们已经陆续介绍了使用C#制作爬虫的基础知识,现在我们应该对如何制作一个简单的网络爬虫有了更好的了解。
本节我们搭建一个完整的爬虫系统,将之前的零散事物连接起来,可以作为一个爬虫项目运行过程的初步探索。但是,在实际项目中,还有其他问题需要解决。我们将在后续章节中继续修炼。:)
先看一下解决方案的整体结构:
我们也希望我们的爬虫框架可以应用到跨平台的项目中,所以这个项目以.Net Core Framework为基础。
根据上图,项目结构还是很简单的。爬虫框架部分与上一章的内容相比没有太大变化。本节主要看应用中如何将一只小蚂蚁扩展为一群小蚂蚁。
本样例项目以采集某部网络小说网站为例,特此对小说网站说一句话:如有冒犯,敬请谅解,如有引流,请赐教奖励:P
好了,步入正文,现在我们来看看应用入口(MikeWare.Crawler.EBooks)项目,是怎么做的。
1 namespace MikeWare.Crawlers.EBooks
2 {
3 using MikeWare.Crawlers.EBooks.Bizs;
4 using MikeWare.Crawlers.EBooks.Entities;
5 using System;
6
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 var lastUpdateTime = DateTime.MinValue;
12
13 BooksList.Start(1, lastUpdateTime);
14
15 Console.Read();
16 }
17 }
18 }
入门项目-程序类
这个项目非常简单。它使用项目的初始 Program 类。Main方法中构造了一个DateTime lastUpdateTime变量,然后启动采集任务。
关于lastUpdateTime变量,我们可以理解为,在采集的过程中,我们可能需要对数据源反复执行采集才能获取更新的数据。在实际情况下,可能会更新数据源。并非所有数据都在变化。例如,本例中的小说。小说的作者昨天写了一些章节。那么这些章节在今天甚至今生都不会改变。又改了,所以我们不需要每次采集都采集小说的所有章节,也就是我们只对较新的小说感兴趣,那么如何区分新的数据呢?与旧数据相比,这取决于数据源为我们提供了什么样的特征。可以从中找到一个或多个合适的特征作为我们的地标。在这种情况下,使用小说的更新时间。可以根据具体情况自定义lastUpdateTime的起源,达到采集递增的目的。
所以第一次采集,我们不妨再采集整个网站的所有小说。那么,这时候可以将lastUpdateTime的初始值设置为DateTime.MinValue,这样即使一本小说再老,它的更新时间也不会早于这个标志位,达到< @采集 所有小说;那么对于另一个采集,我们可以先统计最后一个采集的结果,把最近的更新时间作为这个采集的lastUpdateTime。所以第一次采集或者再次采集,逻辑可以合并为“获取上次采集的最后更新时间”,这个逻辑内部会判断,如果有一条采集记录,返回最近的更新时间,如果没有则返回DateTime.MinValue,这样一切就统一了。
同时,这个项目其实只是为采集任务的启动提供了一个触发点。我尽量让它很轻,以便它可以很容易地移植。或许 WinForm 项目的 Button_Click 事件或 WebApplication 项目的 Page_Load 事件是其入口点。Anywhere 和 Main 方法中的内容,复制过去就好了 :) View 部分暂时不多说。
接下来我们简单介绍一下(MikeWare.Crawlers.EBooks.Entities)项目
1 namespace MikeWare.Crawlers.EBooks.Entities
2 {
3 using System;
4 using System.Collections.Generic;
5
6 public class Book
7 {
8 public int Id { get; set; }
9 public string Name { get; set; }
10 public string PhotoUrl { get; set; }
11 public Dictionary Sections { get; set; }
12 public Dictionary SectionContents { get; set; }
13
14 public string Author { get; set; }
15 public DateTime LastUpdateTime { get; set; }
16 }
17 }
实体类-Book
这个项目也很简单。只提供一门课(书)。在这个类中,定义了一本书的ID、名称、封面图片的URL、作者、最后更新时间和章节内容等属性。用于描述一本书的基本特征。但是,我没有采集书评和评分内容,一、数据源不提供书评数据;二、 希望实现自己的评分和评价体系,不依赖于数据源的评分;这里我只想说明一下,实体的定义是针对业务服务的,大家可以根据自己的需求进行自定义;当然,如果你想要数据完整,我们应该采集过来做。坚持。如果你在未来的某一天突然想再次使用这部分数据怎么办?采集 再次?哈哈……打你脑袋的东西总是防不胜防。
好的,接下来介绍一下(MikeWare.Crawlers.EBooks.Bizs)项目
<p> 1 namespace MikeWare.Crawlers.EBooks.Bizs
2 {
3 using MikeWare.Core.Components.CrawlerFramework;
4 using System;
5 using System.Net;
6 using System.Text;
7 using System.Text.RegularExpressions;
8 using System.Threading;
9 using System.Threading.Tasks;
10
11 public class BooksList
12 {
13 private static Encoding encoding = new UTF8Encoding(false);
14 private static int total_page = -1;
15 private static Regex regex_list = new Regex(@"[^尾页</a>.+?", RegexOptions.Singleline);
17
18 public static void Start(int pageIndex, DateTime lastUpdateTime)
19 {
20 new WorkerAnt()
21 {
22 AntId = (uint)Math.Abs(DateTime.Now.ToString("yyyyMMddHHmmssfff").GetHashCode()),
23 OnJobStatusChanged = (sender, args) =>
24 {
25 Console.WriteLine($"{args.EventAnt.AntId} said: {args.Context.JobName} entered status '{args.Context.JobStatus}'.");
26 switch (args.Context.JobStatus)
27 {
28 case TaskStatus.Created:
29 if (string.IsNullOrEmpty(args.Context.JobName))
30 {
31 Console.WriteLine($"Can not execute a job with no name.");
32 args.Cancel = true;
33 }
34 else
35 Console.WriteLine($"{args.EventAnt.AntId} said: job {args.Context.JobName} created.");
36 break;
37 case TaskStatus.Running:
38 if (null != args.Context.Memory)
39 Console.WriteLine($"{args.EventAnt.AntId} said: {args.Context.JobName} already downloaded {args.Context.Memory.Length} bytes.");
40 break;
41 case TaskStatus.RanToCompletion:
42 if (null != args.Context.Buffer && 0 lastUpdateTime)
84 {
85 Thread.Sleep(5);
86 BookSectionsList.Start(id);
87 }
88 else
89 return;
90 }
91 }
92
93 if (-1 == total_page)
94 {
95 var match = regex_page.Match(context);
96 if (null != match && match.Success && int.TryParse(match.Groups["totalPage"].Value, out total_page)) ;
97
98 }
99
100 if (pageIndex