[开源 .NET 跨平台 Crawler 数据采集 爬虫框架: DotnetSpider] [一] 初衷与架构设计
[DotnetSpider 系列目錄]
- 一、初衷與架構設計
- 二、基本使用
- 三、配置式爬蟲
- 四、JSON數據解析與配置系統
- 五、如何做全站采集
為什么要造輪子
同學們可以去各大招聘網站查看一下爬蟲工程師的要求,大多是招JAVA、PYTHON,甚至于還有NODEJS,C++;再或者去開源中國查詢C#的爬蟲項目,僅有幾個非常簡單或是幾年沒有更新的項目。
而單純性能上.NET對比JAVA,PYTHON并沒有處于弱勢,反而有開發上的優勢(得益于世界上最強大的IDE)。爬蟲性能瓶頸大多是在并發下載(網速)、IP池,那么為什么.NET沒有一個強大的爬蟲框架呢?說真的我不知道,可能爬蟲框架核心上比較簡單,也可能.NET的開發人員沒有別的語言的開發人員勤奮,或是.NET的開源氛圍沒有別的語言高。直到.NET要出開源版的消息傳來,我覺得是時候開發一個跨平臺,跨語言的爬蟲框架了。但一開始是比較忐忑的,覺得自己水平不夠去完全重新設計一個新的框架出來,因此參考了JAVA的一個輕量級爬蟲框架webmagic,并加入了我自己的理解和改進。如果設計或寫得不好請大家指正海涵
框架設計
由于我是參考的webmagic,所以整體架構上沒有什么大的變化,設計圖如下(圖片是直接從webmagic上拿的)
?
- Scheduler:負責URL的調度、去重,可以實現如Queue, PriorityQueueScheduler, RedisScheduler(可用于分布式)等等
- Downloader: 負責下載HTML,可以實現如HttpDownloader, 瀏覽器的Downloader(WebDriver), FiddlerDownloader,本地文件Downloader等等
- PageProcesser: 負責HTML解析、目標URL的選擇
- Pipeline: 負責數據的存儲, 已實現文件存儲, MySql存儲, MySqlFile存儲(腳本),MSSQL存儲,MongoDb存儲, 更多存儲期待您的貢獻
優點
- 數據對象是通過Json結構來解析的:可以實現類中屬性是類的情況(較少此種情況, 而且數據Pipeline僅限MongoDB時才能存儲此種類型數據)
- 支持Json文件定義爬蟲:最終實現跨語言調用
- 自動創建數據庫、數據表
- 支持 .NET CORE,可以跨平臺
- 支持ADSL撥號換IP:如果所有爬蟲統一部署, 可以實現單臺機器同時運行多個任務撥號互不影響、或者一個路由下面多個電腦下多個任務撥號互不影響
- 支持自定義代理池
基本使用
基本使用只需要引用DotnetSpider2.Core(Nuget中獲取)
DotnetSpider實現一個完整爬蟲是需要4個模塊的:Scheduler、Downloader、PageProcessor、Pipeline。由于Downloader和Scheduler都是有基本實現的,因此只需要實現PageProcessor和Pipeline就可以實現一個基本爬蟲了,這種方式也是最自由的方式。
完全自定義的例子如下:
public static void Main(string[] args){// Custmize processor and pipeline 完全自定義頁面解析和數據管道BaseUsage.CustmizeProcessorAndPipeline();Console.WriteLine("Press any key to continue...");Console.Read();}public static void CustmizeProcessorAndPipeline(){// Config encoding, header, cookie, proxy etc... 定義采集的 Site 對象, 設置 Header、Cookie、代理等var site = new Site { EncodingName = "UTF-8", RemoveOutboundLinks = true };for (int i = 1; i < 5; ++i){// Add start/feed urls. 添加初始采集鏈接site.AddStartUrl("http://" + $"www.youku.com/v_olist/c_97_g__a__sg__mt__lg__q__s_1_r_0_u_0_pt_0_av_0_ag_0_sg__pr__h__d_1_p_{i}.html");}Spider spider = Spider.Create(site,// use memoery queue scheduler. 使用內存調度new QueueDuplicateRemovedScheduler(),// use custmize processor for youku 為優酷自定義的 Processornew YoukuPageProcessor())// use custmize pipeline for youku 為優酷自定義的 Pipeline.AddPipeline(new YoukuPipeline())// dowload html by http client.SetDownloader(new HttpClientDownloader())// 1 thread.SetThreadNum(1);spider.EmptySleepTime = 3000;// Start crawler 啟動爬蟲spider.Run();}public class YoukuPipeline : BasePipeline{private static long count = 0;public override void Process(ResultItems resultItems){foreach (YoukuVideo entry in resultItems.Results["VideoResult"]){count++;Console.WriteLine($"[YoukuVideo {count}] {entry.Name}");}// Other actions like save data to DB. 可以自由實現插入數據庫或保存到文件}}public class YoukuPageProcessor : BasePageProcessor{protected override void Handle(Page page){// 利用 Selectable 查詢并構造自己想要的數據對象var totalVideoElements = page.Selectable.SelectList(Selectors.XPath("//div[@class='yk-pack pack-film']")).Nodes();List<YoukuVideo> results = new List<YoukuVideo>();foreach (var videoElement in totalVideoElements){var video = new YoukuVideo();video.Name = videoElement.Select(Selectors.XPath(".//img[@class='quic']/@alt")).GetValue();results.Add(video);}// Save data object by key. 以自定義KEY存入page對象中供Pipeline調用page.AddResultItem("VideoResult", results);// Add target requests to scheduler. 解析需要采集的URLforeach (var url in page.Selectable.SelectList(Selectors.XPath("//ul[@class='yk-pages']")).Links().Nodes()){page.AddTargetRequest(new Request(url.GetValue(), null));}}}public class YoukuVideo{public string Name { get; set; }}?
配置式爬蟲
配置式爬蟲需要額外引用DotnetSpider2.Extension(Nuget中獲取)
大部分情況下只需要配置式來實現一個采集任務。相對于基本使用方式,配置式爬式只需要短短的幾行代碼就可以實現一個爬蟲。但凡事有利就有弊,配置式爬的自由度相對低了一些。
使用配置式爬蟲的步驟如下:
完整代碼如下, 感受一下就好,后面章節會詳細介紹如何實現:
public class JdSkuSampleSpider : EntitySpiderBuilder{protected override EntitySpider GetEntitySpider(){EntitySpider context = new EntitySpider(new Site{//HttpProxyPool = new HttpProxyPool(new KuaidailiProxySupplier("快代理API"))});context.SetThreadNum(1);context.SetIdentity("JD_sku_store_test_" + DateTime.Now.ToString("yyyy_MM_dd_hhmmss"));// dowload html by http clientcontext.SetDownloader(new HttpClientDownloader());// save data to mysql.context.AddEntityPipeline(new MySqlEntityPipeline("Database='test';Data Source=localhost;User ID=root;Password=1qazZAQ!;Port=3306"));context.AddStartUrl("http://list.jd.com/list.html?cat=9987,653,655&page=2&JL=6_0_0&ms=5#J_main", new Dictionary<string, object> { { "name", "手機" }, { "cat3", "655" } });context.AddEntityType(typeof(Product));return context;}[Schema("test", "sku", TableSuffix.Today)][EntitySelector(Expression = "//li[@class='gl-item']/div[contains(@class,'j-sku-item')]")][Indexes(Index = new[] { "category" }, Unique = new[] { "category,sku", "sku" })][TargetUrlsSelector(XPaths = new[] { "//span[@class=\"p-num\"]" }, Patterns = new[] { @"&page=[0-9]+&" })]public class Product : ISpiderEntity{[StoredAs("sku", DataType.String, 25)][PropertySelector(Expression = "./@data-sku")]public string Sku { get; set; }[StoredAs("category", DataType.String, 20)][PropertySelector(Expression = "name", Type = SelectorType.Enviroment)]public string CategoryName { get; set; }[StoredAs("cat3", DataType.String, 20)][PropertySelector(Expression = "cat3", Type = SelectorType.Enviroment)]public int CategoryId { get; set; }[StoredAs("url", DataType.Text)][PropertySelector(Expression = "./div[1]/a/@href")]public string Url { get; set; }[StoredAs("commentscount", DataType.String, 32)][PropertySelector(Expression = "./div[5]/strong/a")]public long CommentsCount { get; set; }[StoredAs("shopname", DataType.String, 100)][PropertySelector(Expression = ".//div[@class='p-shop']/@data-shop_name")]public string ShopName { get; set; }[StoredAs("name", DataType.String, 50)][PropertySelector(Expression = ".//div[@class='p-name']/a/em")]public string Name { get; set; }[StoredAs("venderid", DataType.String, 25)][PropertySelector(Expression = "./@venderid")]public string VenderId { get; set; }[StoredAs("jdzy_shop_id", DataType.String, 25)][PropertySelector(Expression = "./@jdzy_shop_id")]public string JdzyShopId { get; set; }[StoredAs("run_id", DataType.Date)][PropertySelector(Expression = "Monday", Type = SelectorType.Enviroment)]public DateTime RunId { get; set; }[PropertySelector(Expression = "Now", Type = SelectorType.Enviroment)][StoredAs("cdate", DataType.Time)]public DateTime CDate { get; set; }}}?
public class Program {public static void Main(string[] args){JdSkuSampleSpider spiderBuilder = new JdSkuSampleSpider();spiderBuilder.Run();} }代碼地址
https://github.com/zlzforever/DotnetSpider??望各位大佬加星?:)
發現一個更簡單可以幫助小白做數據采集的工具
爬一爬?http://www.pa1pa.com
參與開發或有疑問
QQ群: 477731655
郵箱:?zlzforever@163.com
轉載于:https://www.cnblogs.com/jjg0519/p/6707513.html
總結
以上是生活随笔為你收集整理的[开源 .NET 跨平台 Crawler 数据采集 爬虫框架: DotnetSpider] [一] 初衷与架构设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 日志服务客户端(logtail)异常检测
- 下一篇: 【精】自学路线图