用.NET Core写爬虫爬取电影天堂

 更新时间:2021年9月22日 10:05  点击:1292

自从上一个项目从.NET迁移到.NET core之后,磕磕碰碰磨蹭了一个月才正式上线到新版本。

然后最近又开了个新坑,搞了个爬虫用来爬dy2018电影天堂上面的电影资源。这里也借机简单介绍一下如何基于.NET Core写一个爬虫。

PS:如有偏错,敬请指明…

PPS:该去电影院还是多去电影院,毕竟美人良时可无价。

准备工作(.NET Core准备)

首先,肯定是先安装.NET Core咯。下载及安装教程在这里: https://www.jb51.net/article/87907.htm https://www.jb51.net/article/88735.htm。无论你是Windows、linux还是mac,统统可以玩。

我这里的环境是:Windows10 + VS2015 community updata3 + .NET Core 1.1.0 SDK + .NET Core 1.0.1 tools Preview 2.

理论上,只需要安装一下 .NET Core 1.1.0 SDK 即可开发.NET Core程序,至于用什么工具写代码都无关紧要了。

安装好以上工具之后,在VS2015的新建项目就可以看到.NET Core的模板了。如下图:

为了简单起见,我们创建的时候,直接选择VS .NET Core tools自带的模板。

一个爬虫的自我修养 分析网页

写爬虫之前,我们首先要先去了解一下即将要爬取的网页数据组成。

具体到网页的话,便是分析我们要抓取的数据在HTML里面是用什么标签抑或有什么样的标记,然后使用这个标记把数据从HTML中提取出来。在我这里的话,用的更多的是HTML标签的ID和CSS属性。

以本文章想要爬取的dy2018.com为例,简单描述一下这个过程。dy2018.com主页如下图:

在chrome里面,按F12进入开发者模式,接着如下图使用鼠标选择对应页面数据,然后去分析页面HTML组成。

接着我们开始分析页面数据:

经过简单分析HTML,我们得到以下结论:

www.dy2018.com首页的电影数据存储在一个class为co_content222的div标签里面

电影详情链接为a标签,标签显示文本就是电影名称,URL即详情URL

那么总结下来,我们的工作就是:找到class='co_content222' 的div标签,从里面提取所有的a标签数据。

开始写代码…

之前在写做项目的时候用到过AngleSharp库,一个基于.NET(C#)开发的专门为解析xHTML源码的DLL组件。

AngleSharp主页在这里: https://anglesharp.github.io/ ,

详细介绍:https://www.jb51.net/article/99082.htm

Nuget地址: Nuget AngleSharp 安装命令:Install-Package AngleSharp

获取电影列表数据

private static HtmlParser htmlParser = new HtmlParser();

private ConcurrentDictionary<string, MovieInfo> _cdMovieInfo = new ConcurrentDictionary<string, MovieInfo>();
privatevoidAddToHotMovieList()
{
	//此操作不阻塞当前其他操作,所以使用Task
	// _cdMovieInfo 为线程安全字典,存储了当期所有的电影数据
	Task.Factory.StartNew(()=> 
	{
	 try
	 {
	  //通过URL获取HTML
	  var htmlDoc = HTTPHelper.GetHTMLByURL("http://www.dy2018.com/");
	  //HTML 解析成 IDocument
	  var dom = htmlParser.Parse(htmlDoc);
	  //从dom中提取所有class='co_content222'的div标签
	  //QuerySelectorAll方法接受 选择器语法
	  var lstDivInfo = dom.QuerySelectorAll("div.co_content222");
	  if (lstDivInfo != null)
	  {
	   //前三个DIV为新电影
	   foreach (var divInfo in lstDivInfo.Take(3))
	   {
	    //获取div中所有的a标签且a标签中含有"/i/"的
	    //Contains("/i/") 条件的过滤是因为在测试中发现这一块div中的a标签有可能是广告链接
	    divInfo.QuerySelectorAll("a").Where(a => 
	    a.GetAttribute("href").Contains("/i/"))
	    .ToList().ForEach(
	    a =>
	    {
	     //拼接成完整链接
	     var onlineURL = "http://www.dy2018.com" + a.GetAttribute("href");
	     //看一下是否已经存在于现有数据中
	     if (!_cdMovieInfo.ContainsKey(onlineURL))
	     {
	      //获取电影的详细信息
	      MovieInfo movieInfo = FillMovieInfoFormWeb(a, onlineURL);
	      //下载链接不为空才添加到现有数据
	      if (movieInfo.XunLeiDownLoadURLList != null 
	      && movieInfo.XunLeiDownLoadURLList.Count != 0)
	      {
	        _cdMovieInfo.TryAdd
	        (movieInfo.Dy2018OnlineUrl,movieInfo);
	      }
	     }
	    });
	   }
	  }
	
	 }
	 catch(Exception ex)
	 {
	
	 }
	});
}

获取电影详细信息

 privateMovieInfoFillMovieInfoFormWeb(AngleSharp.Dom.IElement a,
 string onlineURL)
{
 var movieHTML = HTTPHelper.GetHTMLByURL(onlineURL);
 var movieDoc = htmlParser.Parse(movieHTML);
 //http://www.dy2018.com/i/97462.html 分析过程见上,不再赘述
 //电影的详细介绍 在id为Zoom的标签中
 var zoom = movieDoc.GetElementById("Zoom");
 //下载链接在 bgcolor='#fdfddf'的td中,有可能有多个链接
 var lstDownLoadURL = movieDoc.QuerySelectorAll("[bgcolor='#fdfddf']");
 //发布时间 在class='updatetime'的span标签中
 var updatetime = movieDoc.QuerySelector("span.updatetime"); 
 var pubDate = DateTime.Now;
 if(updatetime!=null && !string.IsNullOrEmpty(updatetime.InnerHtml))
 {
  //内容带有“发布时间:”字样,
  //replace成""之后再去转换,转换失败不影响流程
  DateTime.TryParse(updatetime.InnerHtml.Replace("发布时间:", 
  ""), out pubDate);
 }
 

 var movieInfo = new MovieInfo()
 {
  //InnerHtml中可能还包含font标签,做多一个Replace
  MovieName = a.InnerHtml.Replace("<font color=\"#0c9000\">","")
  .Replace("<font color=\" #0c9000\">","")
  .Replace("</font>", ""),
  Dy2018OnlineUrl = onlineURL,
  MovieIntro = zoom != null ? WebUtility.HtmlEncode(zoom.InnerHtml) : "暂无介绍...",
  //可能没有简介,虽然好像不怎么可能
  XunLeiDownLoadURLList = lstDownLoadURL != null ?
  lstDownLoadURL.Select(d => d.FirstElementChild.InnerHtml).ToList() : null,
  //可能没有下载链接
  PubDate = pubDate,
 };
 return movieInfo;
}

HTTPHelper

这边有个小坑,dy2018网页编码格式是GB2312,.NET Core默认不支持GB2312,使用Encoding.GetEncoding(“GB2312”)的时候会抛出异常。

解决方案是手动安装System.Text.Encoding.CodePages包(Install-Package System.Text.Encoding.CodePages),

然后在Starup.cs的Configure方法中加入Encoding.RegisterProvider(CodePagesEncodingProvider.Instance),接着就可以正常使用Encoding.GetEncoding(“GB2312”)了。

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

namespace Dy2018Crawler
{
 public class HTTPHelper
 {

  public static HttpClient Client { get; } = new HttpClient();

  publicstaticstringGetHTMLByURL(stringurl)
  {
   try
   {
    System.Net.WebRequest wRequest = System.Net.WebRequest.Create(url);
    wRequest.ContentType = "text/html; charset=gb2312";

    wRequest.Method = "get";
    wRequest.UseDefaultCredentials = true;
    // Get the response instance.
    var task = wRequest.GetResponseAsync();
    System.Net.WebResponse wResp = task.Result;
    System.IO.Stream respStream = wResp.GetResponseStream();
    //dy2018这个网站编码方式是GB2312,
    using (System.IO.StreamReader reader =
    new System.IO.StreamReader(respStream,
    Encoding.GetEncoding("GB2312")))
    {
     return reader.ReadToEnd();
    }
   }
   catch (Exception ex)
   {
    Console.WriteLine(ex.ToString());
    return string.Empty;
   }
  }
  
 }


}

定时任务的实现

定时任务我这里使用的是 Pomelo.AspNetCore.TimedJob 。

Pomelo.AspNetCore.TimedJob是一个.NET Core实现的定时任务job库,支持毫秒级定时任务、从数据库读取定时配置、同步异步定时任务等功能。

由.NET Core社区大神兼前微软MVP AmamiyaYuuko (入职微软之后就卸任MVP…)开发维护,不过好像没有开源,回头问下看看能不能开源掉。

nuget上有各种版本,按需自取。地址: https://www.nuget.org/packages/Pomelo.AspNetCore.TimedJob/1.1.0-rtm-10026

作者自己的介绍文章: Timed Job - Pomelo扩展包系列

Startup.cs相关代码

我这边使用的话,首先肯定是先安装对应的包:Install-Package Pomelo.AspNetCore.TimedJob -Pre

然后在Startup.cs的ConfigureServices函数里面添加Service,在Configure函数里面Use一下。

// This method gets called by the runtime. Use this method to add services to the container.
publicvoidConfigureServices(IServiceCollection services)
{
 // Add framework services.
 services.AddMvc();
 //Add TimedJob services
 services.AddTimedJob();
}

 publicvoidConfigure(IApplicationBuilder app,
 IHostingEnvironment env, ILoggerFactory loggerFactory)
{
 //使用TimedJob
 app.UseTimedJob();

 if (env.IsDevelopment())
 {
  app.UseDeveloperExceptionPage();
  app.UseBrowserLink();
 }
 else
 {
  app.UseExceptionHandler("/Home/Error");
 }

 app.UseStaticFiles();

 app.UseMvc(routes =>
 {
  routes.MapRoute(
   name: "default",
   template: "{controller=Home}/{action=Index}/{id?}");
 });
 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}

Job相关代码

接着新建一个类,明明为XXXJob.cs,引用命名空间using Pomelo.AspNetCore.TimedJob,XXXJob继承于Job,添加以下代码。

 public class AutoGetMovieListJob:Job
 {
  
  // Begin 起始时间;Interval执行时间间隔,单位是毫秒,建议使用以下格式,此处为3小时;
  //SkipWhileExecuting是否等待上一个执行完成,true为等待;
  [Invoke(Begin = "2016-11-29 22:10", Interval = 1000 * 3600*3, SkipWhileExecuting =true)]
  publicvoidRun()
  {
   //Job要执行的逻辑代码
   
   //LogHelper.Info("Start crawling");
   //AddToLatestMovieList(100);
   //AddToHotMovieList();
   //LogHelper.Info("Finish crawling");
  }
}

项目发布相关 新增runtimes节点

使用VS2015新建的模板工程,project.json配置默认是没有runtimes节点的.

我们想要发布到非Windows平台的时候,需要手动配置一下此节点以便生成。

 "runtimes": {
 "win7-x64": {},
 "win7-x86": {},
 "osx.10.10-x64": {},
 "osx.10.11-x64": {},
 "ubuntu.14.04-x64": {}
}

删除/注释scripts节点

生成时会调用node.js脚本构建前端代码,这个不能确保每个环境都有bower存在…注释完事。

//"scripts": {
// "prepublish": [ "bower install", "dotnet bundle" ],
// "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
//},

删除/注释dependencies节点里面的type

"dependencies": {
 "Microsoft.NETCore.App": {
  "version": "1.1.0"
  //"type": "platform"
 },

project.json的相关配置说明可以看下这个官方文档: Project.json-file ,

或者张善友老师的文章 .NET Core系列 : 2 、project.json 这葫芦里卖的什么药

开发编译发布

//还原各种包文件
dotnet restore;

//发布到C:\code\website\Dy2018Crawler文件夹
dotnet publish -r ubuntu.14.04-x64 -c Release -o "C:\code\website\Dy2018Crawler";

最后,照旧开源……以上代码都在下面找到:

Gayhub地址: https://github.com/liguobao/Dy2018Crawler

PS:回头写个爬片大家滋持不啊…

[!--infotagslink--]

相关文章

  • ASP.NET购物车实现过程详解

    这篇文章主要为大家详细介绍了ASP.NET购物车的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
  • .NET Core下使用Kafka的方法步骤

    这篇文章主要介绍了.NET Core下使用Kafka的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 在ASP.NET 2.0中操作数据之七十二:调试存储过程

    在开发过程中,使用Visual Studio的断点调试功能可以很方便帮我们调试发现程序存在的错误,同样Visual Studio也支持对SQL Server里面的存储过程进行调试,下面就让我们看看具体的调试方法。...2021-09-22
  • Win10 IIS 安装.net 4.5的方法

    这篇文章主要介绍了Win10 IIS 安装及.net 4.5及Win10安装IIS并配置ASP.NET 4.0的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • 详解.NET Core 3.0 里新的JSON API

    这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • .net数据库操作框架SqlSugar的简单入门

    这篇文章主要介绍了.net数据库操作框架SqlSugar的简单入门,帮助大家更好的理解和学习使用.net技术,感兴趣的朋友可以了解下...2021-09-22
  • ASP.NET Core根据环境变量支持多个 appsettings.json配置文件

    这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 记一次EFCore类型转换错误及解决方案

    这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
  • 用C#做网络爬虫的步骤教学

    在本篇内容里小编给大家分享的是关于用C#做网络爬虫的步骤和方法,需要的朋友们可以参考下。...2020-06-25
  • Python爬虫基础之selenium库的用法总结

    今天带大家来学习selenium库的使用方法及相关知识总结,文中非常详细的介绍了selenium库,对正在学习python的小伙伴很有帮助,需要的朋友可以参考下...2021-05-25
  • selenium 反爬虫之跳过淘宝滑块验证功能的实现代码

    这篇文章主要介绍了selenium 反爬虫之跳过淘宝滑块验证功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-08-27
  • .NET C#利用ZXing生成、识别二维码/条形码

    ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口。这篇文章主要给大家介绍了.NET C#利用ZXing生成、识别二维码/条形码的方法,文中给出了详细的示例代码,有需要的朋友们可以参考借鉴。...2020-06-25
  • 详解ASP.NET Core 中基于工厂的中间件激活的实现方法

    这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • 利用C#实现网络爬虫

    这篇文章主要介绍了利用C#实现网络爬虫,完整的介绍了C#实现网络爬虫详细过程,感兴趣的小伙伴们可以参考一下...2020-06-25
  • Nginx中配置过滤爬虫的User-Agent的简单方法

    这篇文章主要介绍了Nginx中配置过滤爬虫的User-Agent的简单方法,文中罗列了一些常用搜索引擎的爬虫名称以免造成不必要的过滤,需要的朋友可以参考下...2016-01-27
  • python 爬取京东指定商品评论并进行情感分析

    本文主要讲述了利用Python网络爬虫对指定京东商城中指定商品下的用户评论进行爬取,对数据预处理操作后进行文本情感分析,感兴趣的朋友可以了解下...2021-05-28
  • C#使用Ado.Net更新和添加数据到Excel表格的方法

    这篇文章主要介绍了C#使用Ado.Net更新和添加数据到Excel表格的方法,较为详细的分析了OLEDB的原理与使用技巧,可实现较为方便的操作Excel数据,需要的朋友可以参考下...2020-06-25
  • asp.net通过消息队列处理高并发请求(以抢小米手机为例)

    这篇文章主要介绍了asp.net通过消息队列处理高并发请求(以抢小米手机为例),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • Underscore源码分析

    Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。这篇文章主要介绍了underscore源码分析相关知识,感兴趣的朋友一起学习吧...2016-01-02
  • ASP.NET单选按钮控件RadioButton常用属性和方法介绍

    RadioButton又称单选按钮,其在工具箱中的图标为 ,单选按钮通常成组出现,用于提供两个或多个互斥选项,即在一组单选钮中只能选择一个...2021-09-22