2023/4/6

C# 非同步程式抓取資料的開發技巧(async, wait, web crawler)

最近有個side project的需求,是需要利用爬蟲去抓取國內外官方機構的Meta data後設資料,對現有的資料做補充。但這些抓取資料的來源格式差異很大,而且刻出來的程式是希望可以打包給不太懂電腦的使用者,執行幾千筆資料的抓取與更新,因此使用python+爬蟲的solution就不太適用。最後,決定用 c# 寫windows form。

但由於抓資料的來源不是後端資料庫而是前端網站,爬資料的時候可能會遇到連線中斷、網路速度遲緩等各種情形,若是幾千筆跑下來,在程式執行完畢前不僅程式會有凍結的情形發生,使用者也會抱怨非常緩慢。所以在開發上選擇使用非同步的寫法。

設計上針對XML、HTML、JSON三種資料來源轉寫三個解析器,以下有幾個設計過程中的心得:

  • 使用 HttpClient而非古早時代的WebRequest寫法抓取資料(相容.NET Core)。
  • 為查詢請求加上瀏覽器header(實際測試,空的請求會被視為機器人而遭拒絕)。
  • 解析的設定(如 xml的節點、Xpath等)另外寫在設定檔中,提高修改程式的彈性。
  • 為每段請求加上一定的delay,以免被視為濫用或過度存取。
  • 為了解析網站資料,用到HtmlAgilityPack套件。另外也需要regexp來解析標籤資料。

實作程式時要加上存取修飾詞 async ,回傳值必須用Task<T>包起來(T是指你的程式想要的型別)。

public async Task<SearchResultData> getDataFromJson(string Oid, string keyword, SearchSetting policy, bool interactive)

{

    //....你的查詢邏輯

    return SearchResultData;

}

呼叫時,在程式之前加上 await ,告訴程式必須等待回傳值。

List<SearchResultData> metaList = new List<SearchResultData>();

 if (policy.Media == "html")

{

    metaList.Add(await getDataFromHtml("virtual", keyword, policy, interactive));

 }

 如果這段程式又被其他程式Caller呼叫,則呼叫的Caller程式建議也要加上async 存取修飾詞。這樣C#才會在背後實作依照發出請求的先後順序回傳資料。否則各個請求資料回傳的順序會是真的完全非同步,也就是同一筆資料先後發出去三個Query1,Query2,Query3,回傳的順序卻會顯示2,3,1。學理上是完全可以理解的,但在實務運作上這樣卻不利於我們整理蒐集回來的資料。

沒有留言:

張貼留言