AngleSharpでスクレイピングする

# はじめに

Web関連のお仕事中ですが、なぜかC#を使うことになりネットで検索しながら対応しました。
ですが、案外と古い情報ばかりで答えに到達できずに少し苦労しましたので、最新の情報に更新したいと思います。

# 目的

C#で特定のWebサイトをスクレイピングして、サイト内のURLを取得し、そのリンク先にあるzipファイルをダウンロード。。。
月次、日次や週次処理として自動化することが目的です。

# 使っていること

C# (.Net 4.7.x) Visual Studio 2017を使用
AngleSharp 0.13.0.0 (2019/10現在の最新を使用)
System.IO.Compression.FileSystem (Zipファイルを解凍するのに使用)

# インストールなど

### AngleSharp

Visual Studio 2017の NuGetからインストールします。
※　詳細は割愛します。

### System.IO.Compression.FileSystem

プロジェクトの参照の追加より、System.IO.Compression.FileSystem を追加します。
参考にしましたサイトは以下です。
[C#でZipファイルを解凍・圧縮する](https://takachan.hatenablog.com/entry/2017/12/25/040800)

# 仕様の概要

1. 特定のURLのページデータを取得
2. パースする
3. 必要なURLを抽出する（特定のaタグのhref属性の情報を取得）
4. 取得したURLのzipファイルをダウンロード
5. ダウンロードしたファイルを解凍

# ソースコード例

[HttpClientをusingで囲わないでください](https://qiita.com/superriver/items/91781bca04a76aec7dc0)

ちょうどいいタイミングでトレンドになっていましたので、参考になりました。

```main.cs
private static HttpClient client;

public void mainRun()
{
    var filename = @"<解凍されたファイル名>"; // 本当は全体パスにしています
    // ファイルがなければダウンロードします
    if (!File.Exists(filename))
    {
        // ダウンロードが終わるまで待ち合わせます
        GetDownloadFile().GetAwaiter().GetResult();
    }
         :
         :
         // ファイルの内容をチェック・取り込みなどしてますが割愛します
}

///
/// ダウンロードする
///
public async Task GetDownloadFile()
{
   try
   {
       // まずは、スクレイピングするデータを取得します
       var response = await client.GetAsync(@"<スクレイピングするサイトのURL>");
       var sorce = await response.Content.ReadAsStringAsync();
       
       // パースします
       var doc = default(IHtmlDocument);  
       var parser = new HtmlParser();
       doc = await parser.ParseDocumentAsync(sorce);    // ここが変わっています!!

       // 取得したいURLデータを探します
       // 今回取得したいデータは、同一のselectorの最後のデータでしたので、
       // .Last()で取得しています
       // .Select()とかで取得したほうがいいこともあると思います
       var url = doc.QuerySelectorAll("<取得したい条件>").Last().GetAttribute("href");

       // ファイルをダウンロードします
       // 今回取得したURLはhttps://xxx から始まるURLなのでそのまま使用しています
       WebClient webClient = new WebClient();
       webClient.DownloadFile(url, <保存するファイル名（絶対パスで）>);

       // ZIPファイルを解凍します
       ZipFile.ExtractToDirectory(<保存するファイル名（絶対パスで）>, <解凍先フォルダー名>);
   }
   catch (Exception e)
   {
       // エラー処理
   }
}
```

# 補足事項

```cs
    GetDownloadFile().GetAwaiter().GetResult();
```
呼び出し元で同期したかったので、すべての処理が終わるまで待ち合わせています。
Taskのメソッドを呼び出して対応しています。

```cs
       var response = await client.GetAsync(@"<スクレイピングするサイトのURL>");
       var sorce = await response.Content.ReadAsStringAsync();
```
取得の仕方はいろいろあると思いますが、今回は簡単な方法を採用しました。
client.GetStreamAsync()などでもいいかと思います。

```cs
       var doc = default(IHtmlDocument);  
       var parser = new HtmlParser();
       doc = await parser.ParseDocumentAsync(sorce);    // ここが変わっています!!
```
パースの仕方が以前の方法から変わっています。
参考にしたサイトなどはほとんど```doc = await parser.ParseAsync(stream);```などとなっていました。
現在のバージョンでは、ParseAsync()はありません。

```cs
       var url = doc.QuerySelectorAll("<取得したい条件>").Last().GetAttribute("href");
```
今回掲載はしていませんが、例えば、
`div.button-div > a` などでセレクタ指定します。
上記の場合は、 divタグのclass属性に”button-div”があるときにそのdiv内にあるaタグをすべて取得する
となります。

```cs
       WebClient webClient = new WebClient();
       webClient.DownloadFile(url, <保存するファイル名（絶対パスで）>);
```
ファイルのダウンロードは、WebClient クラスで簡単にできます。

```cs
       // ZIPファイルを解凍します
       ZipFile.ExtractToDirectory(<保存するファイル名（絶対パスで）>, <解凍先フォルダー名>);
```
ZIPファイルの解凍はZipFileクラスで簡単にできます。
.Net 4.5以上ならとても簡単です。

# 最後に

技術は日々変わっていますので、情報のアップデートは必要ですね。
調べるのに結構時間がかかりましたのでご参考になれば幸いです。


# 参考URL
参考になりました。ありがとうございました。

[HttpClientをusingで囲わないでください](https://qiita.com/superriver/items/91781bca04a76aec7dc0)
[C#でモダンにスクレイピングするならAngleSharp](https://qiita.com/matarillo/items/a92e7efbfd2fdec62595)
[C#でZipファイルを解凍・圧縮する](https://takachan.hatenablog.com/entry/2017/12/25/040800)
[ネットワーク上のファイルのダウンロード](http://home.a00.itscom.net/hatada/csharp/webclient03.html)