初識 DotnetSpider

来源:https://www.cnblogs.com/grom/archive/2018/04/24/8931650.html
-Advertisement-
Play Games

第一次寫博客,比較淺顯,歡迎大牛們指點一二,不勝感激。 ** 溫馨提示:如需轉載本文,請註明內容出處。** 本文連接:http://www.cnblogs.com/grom/p/8931650.html 最近在做爬蟲,之前一直在使用 HttpWebRequest 和 WebClient ,很方便快捷 ...


  第一次寫博客,比較淺顯,歡迎大牛們指點一二,不勝感激

   ** 溫馨提示:如需轉載本文,請註明內容出處。**

   本文連接:http://www.cnblogs.com/grom/p/8931650.html 

  最近在做爬蟲,之前一直在使用 HttpWebRequest 和 WebClient ,很方便快捷,也很適合新手,但隨著抓取任務的增多,多任務,多庫等情況的出現,使用一個優秀的爬蟲框架是十分必要的。於是開始接觸dotnetspider。

 

  

  借鑒一下框架的設計圖,在引入dotnetspider的NuGet包後,我基本也是按照這個進行了分層

  

  Data.Spider - 存放前臺頁面(Winform、控制台)和實體爬蟲(EntitySpider)、Downloader等,相當於發起請求的起點。

  Spider.Processor - 處理器,繼承 IPageProcessor 實現對抓取內容的處理

  Spider.Pipe - 管道,我將它理解為經過了 Processor 處理後的一個回調,將處理好的數據存儲(文件、資料庫等)

  Spider.Entity - 數據實體類,繼承 SpiderEntity

  Spider.Command - 一些常用的公用命令,我這目前存放著轉數據格式類,後臺執行JS類,SqlHelper(因架構自帶資料庫管道,暫時沒用)等

  這樣的分層也是參考了源碼的示例

  

  隨著這幾天的嘗試,真的發現這個框架真的非常靈活,以凹凸租車的爬蟲為例,上代碼

  實體類

[EntityTable("CarWinsSpider", "AtzucheCar", EntityTable.Today)]
[EntitySelector(Expression = "$.data.content[*]", Type = SelectorType.JsonPath)]

public class AtzucheModel : SpiderEntity

{

/// <summary>
/// 車輛編號
/// </summary>
[PropertyDefine(Expression = "$.carNo", Type = SelectorType.JsonPath)]
public int carNo { get; set; }
/// <summary>
/// 品牌
/// </summary>
//[ReplaceFormatter(NewValue = "", OldValue = "\r")]
//[ReplaceFormatter(NewValue = "", OldValue = "\t")]
//[ReplaceFormatter(NewValue = "", OldValue = "&nbsp;")]
//[ReplaceFormatter(NewValue = "", OldValue = "\n")]
//[ReplaceFormatter(NewValue = "", OldValue = "\"")]
//[ReplaceFormatter(NewValue = "", OldValue = " ")]
[PropertyDefine(Expression = "$.brand", Type = SelectorType.JsonPath)]
public string brand { get; set; }
/// <summary>
/// 地址
/// </summary>
[PropertyDefine(Expression = "$.carAddr", Type = SelectorType.JsonPath)]
public string carAddr { get; set; }
/// <summary>
/// 車系
/// </summary>
[PropertyDefine(Expression = "$.type", Type = SelectorType.JsonPath)]
public string type { get; set; }
/// <summary>
/// 排量
/// </summary>
[PropertyDefine(Expression = "$.sweptVolum", Type = SelectorType.JsonPath)]
public string sweptVolum { get; set; }
/// <summary>
/// 圖片
/// </summary>
[PropertyDefine(Expression = "$.coverPic", Type = SelectorType.JsonPath)]
public string coverPic { get; set; }
/// <summary>
/// 日租金
/// </summary>
[PropertyDefine(Expression = "$.dayPrice", Type = SelectorType.JsonPath)]
public int dayPrice { get; set; }
/// <summary>
/// 公裡數
/// </summary>
[PropertyDefine(Expression = "$.distance", Type = SelectorType.JsonPath)]
public string distance { get; set; }
/// <summary>
/// 評分
/// </summary>
[PropertyDefine(Expression = "$.evalScore", Type = SelectorType.JsonPath)]
public string evalScore { get; set; }
[PropertyDefine(Expression = "$.gbType", Type = SelectorType.JsonPath)]
public string gbType { get; set; }
/// <summary>
/// 車牌
/// </summary>
[PropertyDefine(Expression = "$.plateNum", Type = SelectorType.JsonPath)]
public string plateNum { get; set; }
[PropertyDefine(Expression = "$.replyTag", Type = SelectorType.JsonPath)]
public string replyTag { get; set; }
[PropertyDefine(Expression = "$.transCount", Type = SelectorType.JsonPath)]
public string transCount { get; set; }
/// <summary>
/// 年款
/// </summary>
[PropertyDefine(Expression = "$.year", Type = SelectorType.JsonPath)]
public int year { get; set; }
[PropertyDefine(Expression = "$.isPrivilege", Type = SelectorType.JsonPath)]
public int isPrivilege { get; set; }
[PropertyDefine(Expression = "$.isRecommend", Type = SelectorType.JsonPath)]
public int isRecommend { get; set; }
[PropertyDefine(Expression = "$.isUpgrade", Type = SelectorType.JsonPath)]
public int isUpgrade { get; set; }
[PropertyDefine(Expression = "$.lat", Type = SelectorType.JsonPath)]
public string lat { get; set; }
[PropertyDefine(Expression = "$.lon", Type = SelectorType.JsonPath)]
public string lon { get; set; }
[PropertyDefine(Expression = "$.queryId", Type = SelectorType.JsonPath)]
public string queryId { get; set; }
[PropertyDefine(Expression = "$.supplyCarService", Type = SelectorType.JsonPath)]
public int supplyCarService { get; set; }
[PropertyDefine(Expression = "$.freeCarService", Type = SelectorType.JsonPath)]
public int freeCarService { get; set; }
[PropertyDefine(Expression = "$.isShenMaCar", Type = SelectorType.JsonPath)]
public int isShenMaCar { get; set; }
[PropertyDefine(Expression = "$.supportGetReturn", Type = SelectorType.JsonPath)]
public int supportGetReturn { get; set; }
[PropertyDefine(Expression = "$.confirmation", Type = SelectorType.JsonPath)]
public int confirmation { get; set; }

}

 

 

起始:

  /// <summary>
  /// 應用程式的主入口點。
  /// </summary>
  [STAThread]

static void Main()

{

var site = new Site
{
  CycleRetryTimes = 1,
  SleepTime = 200,
  Headers = new Dictionary<string, string>()
  {
    {"Accept","application/json, text/javascript, */*; q=0.01" },
    {"Accept-Encoding","gzip, deflate" },
    {"gzip, deflate","zh-CN,zh;q=0.9" },
    {"X-Requested-With","XMLHttpRequest" },
    { "Referer", "http://www.atzuche.com/hz/car/search"},
    { "Connection","keep-alive" },
    { "Content-Type","application/json;charset=UTF-8" },
    { "Host","www.atzuche.com"},
    { "User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"}
  }
};
List<Request> resList = new List<Request>();

Request res = new Request();
//res.PostBody = $"id=7&j=%7B%22createMan%22%3A%2218273159100%22%2C%22createTime%22%3A1518433690000%2C%22row%22%3A5%2C%22siteUserActivityListId%22%3A8553%2C%22siteUserPageRowModuleId%22%3A84959%2C%22topids%22%3A%22%22%2C%22wherePhase%22%3A%221%22%2C%22wherePreferential%22%3A%220%22%2C%22whereUsertype%22%3A%220%22%7D&page={i}&shopid=83106681";//據說是post請求需要
res.Url = "http://www.atzuche.com/car/searchListMap/2?cityCode=330100&sceneCode=U002&filterCondition%5Blon%5D=120.219294&filterCondition%5Blat%5D=30.259258&filterCondition%5Bseq%5D=4&pageNum=1&pageSize=0";
res.Method = System.Net.Http.HttpMethod.Get;

resList.Add(res);

var spider = DotnetSpider.Core.Spider.Create(site, new QueueDuplicateRemovedScheduler(), new AtzucheProcessor())
.AddStartRequests(resList.ToArray())//頁面抓取整理
.AddPipeline(new AtzuchePipe());//數據回調

//----------------------------------
spider.Monitor = new DotnetSpider.Core.Monitor.NLogMonitor();
spider.Downloader = new DotnetSpider.Core.Downloader.HttpClientDownloader();
spider.ClearSchedulerAfterComplete = false;//爬蟲結束後不取消調度器
//----------------------------------

spider.ThreadNum = 1;
spider.Run();

    Console.WriteLine("Press any key to continue...");
    Console.Read();

}

這裡也可將整個抓取方法當做一個Spider實例單獨放置 -> EntitySpider

 

  /// <summary>
  /// 應用程式的主入口點。
  /// </summary>
  [STAThread]

static void Main()

{

  AtzucheEntitySpider dDengEntitySpider = new AtzucheEntitySpider();
  dDengEntitySpider.AddPageProcessor(new AtzucheProcessor());//控制器
  dDengEntitySpider.AddPipeline(new AtzuchePipe());//回調
  dDengEntitySpider.ThreadNum = 1;
  dDengEntitySpider.Run();
  Console.WriteLine("Press any key to continue...");
  Console.Read();

}

新建爬蟲實體類

public class AtzucheEntitySpider : EntitySpider
{
  protected override void MyInit(params string[] arguments)
  {
    AddPipeline(new SqlServerEntityPipeline("Server=.;Database=AuzucheSpider;uid=sa;pwd=123;MultipleActiveResultSets=true"));//註意連接字元串中資料庫不能帶 .  親測報錯。。。
    AddStartUrl("http://www.atzuche.com/car/searchListMap/2?cityCode=330100&sceneCode=U002&filterCondition%5Blon%5D=120.219294&filterCondition%5Blat%5D=30.259258&filterCondition%5Bseq%5D=4&pageNum=1&pageSize=0");
    AddEntityType<AtzucheModel>();//如添加此實體類,框架將會根據此實體類上面的特性選擇進行匹配,匹配成功後插入資料庫,固可以省略Processor和Pipe,或者不適用此句,通過控制器和回調自定義存儲方法
  }

public AtzucheEntitySpider() : base("AuzucheSpider", new Site
{
  CycleRetryTimes = 1,
  SleepTime = 200,
  Headers = new Dictionary<string, string>()
  {
    {"Accept","application/json, text/javascript, */*; q=0.01" },
    {"Accept-Encoding","gzip, deflate" },
    {"gzip, deflate","zh-CN,zh;q=0.9" },
    {"X-Requested-With","XMLHttpRequest" },
    { "Referer", "http://www.atzuche.com/hz/car/search"},
    { "Connection","keep-alive" },
    { "Content-Type","application/json;charset=UTF-8" },
    { "Host","www.atzuche.com"},
    { "User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"}
  }
})
{
}

}

 接下來是處理器:

解析抓取的數據封裝到"AtzucheList"內,可Pipe內通過此名稱獲取處理好的數據。

public class AtzucheProcessor : IPageProcessor
{
  public void Process(Page page, ISpider spider)
  {
    List<AtzucheModel> list = new List<AtzucheModel>();
    var html = page.Selectable.JsonPath("$.data.content").GetValue();
    list = JsonConvert.DeserializeObject<List<AtzucheModel>>(html);
    page.AddResultItem("AtzucheList", list);
  }
}

 

 

 

最後是回調,可在此加入保存數據的代碼,至此結束。

public class AtzuchePipe : BasePipeline
{
  public override void Process(IEnumerable<ResultItems> resultItems, ISpider spider)
  {
    var result = new List<AtzucheModel>();
    foreach (var resultItem in resultItems)
    {
      Console.WriteLine((resultItem.Results["AtzucheList"] as List<AtzucheModel>).Count);
      foreach (var item in (resultItem.Results["AtzucheList"] as List<AtzucheModel>))
      {
        result.Add(new AtzucheModel()
        {
          carNo = item.carNo
        });
        Console.WriteLine($"{item.carNo}:{item.type} ");
      }
    }
  }
}

   結果圖:

  

 

  總體來說,此框架對新手還是很友好的,靈活寫法可以讓我們有較多的方式去實現爬蟲,因為這個爬蟲比較簡單,就先寫到這裡,未來如果可能,會再嘗試使用框架內的多線程、代理等功能,如有心得將繼續分享,希望能對跟我一樣的新手有所幫助,十分感謝。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 之前用 Servlet + JSP 實現了一個 "簡易版的學生管理系統" ,在學習了 SSM 框架之後,我們來對之前寫過的項目重構一下! 技術準備 為了完成這個項目,需要掌握如下技術: Java 基礎知識 前端: HTML, CSS, JAVASCRIPT, JQUERY J2EE: Tomcat, ...
  • 從知乎作者Rui L學來的一招。應該用過 IPython 吧?想象一下,拋出異常時自動把你帶到 IPython Shell 是不是很開心?而且和普通的IPython不同,這個時候可以調用 p (print), up(up stack), down(down stack) 之類的命令。還能創建臨時變數... ...
  • 課前預習 1.編譯型語言:將源碼一次性轉化為二進位代碼再執行(嚼碎了再吃,執行效率高)優點:執行效率高;缺點:開發效率低,不能跨平臺。 解釋型語言:程式運行時,從上往下將源碼一句一句轉化為二進位的同時執行(邊解釋邊執行,一心二用所以執行效率低)優點:開發效率高,可跨平臺;缺點:執行效率低。 2.py ...
  • .NET 單元測試的利劍——模擬框架Moq 前言 這篇文章是翻譯文,因為通過自己參與的項目,越發覺得單元測試的重要性,特別是當跟業務數據打交道的時候的,Moq就如雪中送炭,所以想學習這個框架,就從這篇譯文開始吧,順便提升下自己英文閱讀水平吧,由於英語實在不行,藉助有道翻譯有時候還理解不了原文的意思。 ...
  • 1 namespace Test 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //作業:橡皮rubber鴨子、木wood鴨子、真實的鴨子realduck。 8 //三個鴨子都會游泳,而橡皮鴨子和真實的鴨... ...
  • 場景: EF底層,獲取完主表,點擊按鈕,添加主表,字表內容時,報以上錯誤 解決方案: 在EF文件的空白處右鍵--屬性,將“應用延遲載入”,改為False ...
  • RabbitMQ是一個由erlang開發的AMQP(Advanved Message Queue)的開源實現,是實現消息隊列應用的一個中間件,消息隊列中間件是分散式系統中重要的組件,主要解決應用耦合,非同步消息,流量削鋒等問題。實現高性能,高可用,可伸縮和最終一致性架構。是大型分散式系統不可缺少的中間... ...
  • 軟體官網:http://www.ezdml.com/ 作者郵箱:[email protected] EZDML EZDML是一個資料庫建表的軟體。 可快速的進行資料庫表結構設計,建立數據模型。 類似大家常用的資料庫建模工具如 PowerDesigner、ERWIN、ER-Studio 和 Rational- ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...