使用.Net Core做個爬蟲

  最近接手一個新項目,爬亞馬遜分類、商品數據。記得大學的時候,自己瞎玩,寫過一個爬有緣網數據的程序,那個時候沒有考慮那麼多,寫的還是單線程,因爲網站沒有反爬,就不停的一直請求,記得放到實驗室電腦上一天,跑了30w+的數據。然後當前晚上有緣網網站顯示維護中。。。。

 畢竟小打小鬧,沒有真正的寫過爬蟲。就翻別人博客瞭解了下爬蟲所用到的技術、技巧、套路。然後就翻到這個老哥寫的博客, 雖然語言是有點囂張,但是我還是比較認同的 哈哈哈哈。

 

 下面從爬蟲涉及的幾任務調度、數據去重、數據解析、併發控制、斷點續爬、代理來聊聊項目遇到的坑。

一、數據解析

數據解析就是提取網頁上的有效數據。.Net下有個HtmlAgilityPack組件,可以很好地解析HMTL。想都沒想 就直接用了它(這就爲後面挖了一個大坑)。剛開始單線程測試的時候,一切正常,但是當我開了50個線程的時候,內存在90s內飆升到了3G,而且持續爬升。

用.Net Memory工具分析發現 內存被大對象沾滿了,所以每次GC的時候內存並沒有被回收,有5w多HtmlNode,每個對象大小都超過 85000byte。

 因爲亞馬遜的圖片不是通過鏈接外鏈的,而是通過base64編碼的,所以導致下載的網頁Html超過1M,而85000byte就算大對象了。導致每爬取一個商品詳情頁,都會加載到HtmlNode中變成一個大對象,由於GC不壓縮大對象,而C對大對象的回收只有在回收2代的時候才觸發。所以只能改變策略,通過正則、切分字符串來處理。

二、任務調度、並行爬取

目的是爬取亞馬遜的分類和分類下的商品,我做了個3個任務,

1、找到分類入口,爬取分類Id、標題、url 、分類等級存儲到數據庫。

2、根據1任務爬取的分類Id,獲取分類下商品列表。爬取列表上商品部分信息,包括商品的Id、名稱、縮略圖。

3、根據2任務爬取的商品Id,獲取商品詳情頁,爬取商品詳情頁的其他信息。

一個分類下有幾百頁商品列表,而每個列表一般有22個商品。所以1任務爬取完一次,2任務要爬取幾百次(當然不會將分類下的所有頁碼都爬完,設定只爬20頁) ,3任務要爬 20 x 22次。這樣分任務的好處就是 3個任務中,不論哪個任務掛了,其他2個任務是不受影響的,可以繼續跑。

比如2任務掛掉了,1任務不受它影響,雖然3任務的需要2任務的數據,但是3任務的速度是比2任務慢了22倍還多(獲取詳情的時候 還需要在請求其他頁面)所以任務線程相同的情況下 2任務的會有很多剩餘商品 3還沒有來得及跑。

調度採用了QuartZ 使用Cron配置定時任務。使用Parallel並行爬取,線程數量可以配只需要將方法和方法所需要的參數集合放到ForEach

 

合理配置每個任務的線程數量,我設置爬取分類線程數1,商品列表的線程是2,商品詳情爬取線程爲50。爬取的速度不同線程數量就不同,而且並不是線程越高越好,這個值是不斷的調試採集相同時間的數據分析得出來的。

image.png

三、代理

現在有很多代理商,普遍分爲兩種:

  第一種通過接口返回代理IP和這個代理的可用時間,在這個時間段內,這個代理是可用的。注意:這種代理方式需要有個代理池,因爲爬蟲一般都是多線程,如果在代理IP可用時間段內,多個線程一直使用同一個代理IP,很大可能會被封。所以保險的做法就是一個線程一個代理,降低每個代理的請求次數。

  第二種代理直接給你一個固定的IP,這個代理IP沒有時間限制,代理商那邊會幫你自動幫你換不同的IP請求目標地址。

.Net Core中使用代理很簡單,因爲我使用的是HttpClientFactory,所以在添加服務的時候配置 HttpClientHandler的代理就可以,需要實現一個IWebProxy類,返回對應的代理IP和端口號就可以了。

 剛開始使用的是第一種代理,爲了實現一個線程一個代理,我創建了一個代理池,在程序啓動的時候,每個線程都會從代理商那獲取到一個代理IP,然後放到代理池中,每次獲取代理的時候,通過代理池中隨機挑選一個代理IP,在挑選前會判斷當前代理池中的代理數量,如果小於線程數據,就會去獲取填充到代理池中。

後面發現國內的代理商的IP訪問國外網址太慢了,就換了國外代理,國外代理使用的是第二種方案。但是這個代理商不是自動幫你更換IP,而是需要每次傳遞一個隨機數 SessionId,隨機數不同,代理商訪問目標網站的IP就不同,而且要傳用戶名和密碼。

 開始 我是這麼做的:

 發現這種只有在第一次創建HttpClient的時候纔會去走配置ConfigurePrimaryHttpMessageHandler方法,之後創建HttpClient都不會走。就導致一直在使用同一個IP請求。

所以沒辦法,只能放棄使用HttpClientFactory,自己手動創建HttpClient ,然後釋放。但這種又隨之帶來一個問題HttpClient雖然釋放了,但是端口還是被佔用着,目前還沒有好的解決辦法。

 四、斷點續爬、數據去重

我這個業務中這兩個功能就很好實現,每次爬取完成商品頁碼,就存儲下一頁的頁碼和當前爬取的頁碼數。配置一個參數是否是斷點續爬,如果是斷點續爬,就從上次記錄的頁碼爬取商品。

數據去重,因爲直接可以拿到亞馬遜的商品Id和分類Id,所以去重就變的很簡單,任務啓動的時候,會將已經爬取過的商品Id和分類Id放到緩存中,爬取的時候對比數據。

 

項目在服務器上跑了2個晚上,表現還是可以的,數據都正確採集到了117w數據(包含未爬取詳情的商品),最後的最後。。。。項目被砍了,因爲亞馬遜的商品詳情頁數據太大,導致爬了12個小時用了40G流量,1G=6美元 ,一個月就要40x2x6x30=14400美元。忙碌了兩週,也算從零寫了一個小小的爬蟲,還算有所得。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章