【C#】自作したブラウザでWebスクレイピングをやってみる(検証)

# C#でWin32API(Windows API)を使ってみる

## はじめに

この記事では自分で作成したブラウザを  
Win32 API(以下、Windows API)を使って操作してみる話を書きます。
主にWebBrowser コントロールから `HtmlDocument` および `HtmlElement` を取得する方法について書いています。

## 動作環境

- Windows10 Pro 21H2
- Visual Studio Code
- dotNET Core 6.0.101

## 前提知識

### Windows APIとは

Microsoftが提供するWindowsで動作するAPI  
またはWindows提供するアプリケーションインターフェイスのことを指します。  

特に32bit 時代のWindowsでは`Win32 API`と呼ばれていましたが  
現在のWindows OSは64bit が圧倒的に多いということもあり、現代では単に`Windows API`と呼ばれます。  

`Windows API`はしばしば、いろんなプログラミング言語で呼び出されることが多いですが
Windowsの機能を呼び出すことがキモとなる為、Windows系の技術でよく利用されます。

もちろん、CやC++など`Windows API` を呼び出せる技術であれば、利用可能です。
今回は次に説明する `WebBrowser` コントロールを`Windows API`でタッチしにいきます。

### WebBrowser コントロール(WebBrowser クラス)

皆さんご存知の`Internet Explorer(IE)`に使われているブラウザコントロールまたはクラスです。  
IEとVBAが連携するのも極端な話、コイツのおかげです。  

とても便利ですが、今後廃止されるかもしれないクラスですので多用は禁物です。`WebView2`を使いましょう。  
ちなみにMicrosoft Edge に搭載される IEモードはおそらく、この`WebBrowser` コントロールが担っています。  
※なぜそうなるのか理由は後述します。  

今回は`WebBrowser` にホストされている(内にある)`HtmlDocument` を操作します。

### HtmlDocument

`HtmlDocument`とは`WebBrowser`内にあるHTMLドキュメントを示すクラスのことです。

名前空間は`System.Windows.Forms` です。

※参考：Microsoftのドキュメント

[HtmlDocument クラス](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.htmldocument?view=windowsdesktop-6.0)

なお、`HtmlDocument` は `HtmlElement`を取得するメソッドおよびプロパティを有しています。  
Web系技術に詳しい人であれば、直観的に理解できると思います。  

## 本題

### 準備

まずは[こちら](https://github.com/ymd65536/LegacyBrowser/releases/tag/Browser)から ブラウザをダウンロードします。  
解凍してデスクトップなどに配置します。

解凍ができましたら、フォルダ内の`LegacyBrowser.exe`をクリックします。
起動時表示されるページはあらかじめ設定しておいたホームページが表示されます。
※おそらく、大半の人はMSN がトップに出るかもしれません

次に`git clone`でリポジトリをクローンします。

```bash
git clone https://github.com/ymd65536/CSharpWin32.git
```

今回は特定のパッケージの利用が必須となる為、nuget で取得します。

```bash
cd CSharpWin32
dotnet add package HiNetCloud.Microsoft.mshtml --version 21.8.1.23
```

### 実行してみる

最後に`dotnet run` で実行します。

```bash
donet run
```

実行結果

```bash
プロセス名:LegacyBrowser
タイトル名:MSN Japan - ニュース, 天気, メール (Outlook, Hotmail), Bing検索, Skype
+ 0x00010800 - 戻る WindowsForms10.Button.app.0.33c0d9d_r3_ad1 戻る
+ 0x00010802 - 進む WindowsForms10.Button.app.0.33c0d9d_r3_ad1 進む
+ 0x00010804 -  WindowsForms10.Edit.app.0.33c0d9d_r3_ad1 
+ 0x00010806 - Go WindowsForms10.Button.app.0.33c0d9d_r3_ad1 Go
+ 0x00010808 - Home WindowsForms10.Button.app.0.33c0d9d_r3_ad1 Home
+ 0x000307E6 -  WindowsForms10.Window.8.app.0.33c0d9d_r3_ad1 
++ 0x000307EE -  Shell Embedding 
+++ 0x000207F8 -  Shell DocObject View 
++++ 0x00010818 -  Internet Explorer_Server 
+++ 0x00010818 -  Internet Explorer_Server 
++ 0x000207F8 -  Shell DocObject View 
+++ 0x00010818 -  Internet Explorer_Server 
++ 0x00010818 -  Internet Explorer_Server 
+ 0x000307EE -  Shell Embedding 
++ 0x000207F8 -  Shell DocObject View 
+++ 0x00010818 -  Internet Explorer_Server 
++ 0x00010818 -  Internet Explorer_Server 
+ 0x000207F8 -  Shell DocObject View 
++ 0x00010818 -  Internet Explorer_Server 
+ 0x00010818 -  Internet Explorer_Server 
+ 0x0001080A -  WindowsForms10.Window.8.app.0.33c0d9d_r3_ad1 
```

### 実行結果の説明

> プロセス名:LegacyBrowser
> タイトル名:MSN Japan - ニュース, 天気, メール (Outlook, Hotmail), Bing検索, Skype

プロセス名：今回のプログラムの名前
タイトル名：フォームののキャプション

> + 0x00010800 - 戻る WindowsForms10.Button.app.0.33c0d9d_r3_ad1 戻る

上記の出力は以下のように並んでいます。
階層 ハンドル値 コントロールのキャプション クラス名 タイトル名

### 実行結果からわかること

`Windows Fomrs` の コントロールはクラス名が`WindowsForms10.Button.app.0.33c0d9d_r3_ad1`であり
`Windows API`の`EnumChildWindows`は左から右へ上からの下に探索を実行するということがわかります。

今回、一番興味深いところとしては
`Microsoft Edge` に搭載されたIEモードでスクレイピングする時に見かけるクラスである `Internet Explorer_Server` が作成したWebブラウザにもいたということです。

とするとIEモードおよびIEで動くVBAマクロもこのブラウザで動くのかと言うと`動きます`。
もちろん、従来の`Shell.Application` でウィンドウを捉えてVBAを実行する方法も動きました。(検証済)

劣化版ですが、IEで自動化している人はこのブラウザを使えば、今までどおりまでとはいかずとも
自動化できると思います。

## で、スクレイピングはどうやるの？

今回、`LegacyBrowser`のハンドルを取得する為に作成した`Program.cs`の56行目に以下のような記述があると思います。

```cs
// Console.WriteLine(ReHtml.getElementsByName("q").length);
```

この `ReHtml`には`HtmlDocument`が格納されている為、ここで`getElementsByName` を実行すると  
タグを取得できるのではないかなと考えています。  
検証しましたが、メソッドではうまいことデータが取れませんでした。  
なお、`innerHTML` や `innerText` では`HtmlDocument` 内にあるテキストを取得できました。  

## まとめ

今回の調査で判明したこと

- 自作のブラウザに対して `Shell.Application` を使えば、dotNETによるWebオートメーションが見込めそう
- `HtmlDocument` による画面の取得はあんまり融通が効かなさそう
  - 特にJavaScript を実行するのが難しい
- `Windows API` は難しい

これでIEの代わりにゴリゴリと自動化できれば、いろんなところで活躍しそうです。
ただし、WebBrowser コントロールは将来的には廃止されるかもしれないので多用は禁物です。`WebView2` を使いましょう。

## 所感

`Windows API` は難しいですが、できることが広がるので楽しいですね。
検証しているときが一番楽しかったです。

ただ、普通に生きていればここまで`Windows API` に携わる機会はないと思います。
そういうことも考えるとわりと面白い検証でした。

## おわり