C#でスクレイピングは何度かやりましたが、Webアプリとして結果を誰でも見ることができるものを作る方法を考えます。
Contents
動的サイトのスクレイピング
単にHTMLを解析するだけなら簡単なのですが、SNSのようなサイトは表示される内容が直接HTML上に書き込まれているわけではなく、JavaScriptなどを使用して動的に表示しています。そこでこれまでに作成したデスクトップアプリではSeleniumやPuppeteerを使用しました。
Webアプリとしてつくる場合も同様にSeleniumやPuppeteerをサーバーにインストールできればよいのですが、これがうまくいきません。専用サーバーやVPSであればできるのかもしれませんが、共有サーバーだと必要なライブラリをインストールする手段がありません。
PhantomJsCloudを使う
そこで別の方法を考えます。今回はPhantomJsCloudを使います。無料プランでは1日に500回という制限がありますが、実験で使ってみるのであれば充分だと思います。Sign up now!のボタンをクリックしてメールアドレスを入力します。すると登録したアドレスにメールがくるのでリンクをクリックしましょう。あとは適当なパスワードを入力するとログインできるようになります。
ログインすると ApiKey が表示されているのでこれをメモしておきましょう。
ターゲットにするサイト
試しにこれをスクレイピングしてみることにします。
HTMLを見るとこのようになっていて、HTMLだけでは何が書かれているのかわかりません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>サンプル</title> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> p { margin-top: 0px; margin-bottom: 0px; } .content { margin-bottom: 10px; } .name { font-weight: bold; } </style> </head> <body> <div id = "contents"></div> <script src = "./sample.js"></script> </body> </html> |
解析ツールを使うと以下のような構造になっていることがわかります。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<div id = "contents"> <div class="content"> <p class="name">投稿者</p> <p class="user-comment">投稿内容</p> <p class="post-time">投稿時刻</p> </div> <div class="content"> <p class="name">投稿者</p> <p class="user-comment">投稿内容</p> <p class="post-time">投稿時刻</p> </div> ・・・ </div> |
PhantomJsCloudでスクレイピングする
ではPhantomJsCloudで必要な内容を抜き出してみましょう。ボタンをクリックするとレンダリングされたHTMLと鳩でもわかるC#の書き込みのみを表示する処理を実装することにします。
1日あたりの実行回数の制限があるので、似たような動作をさせているだけで、実際のコードは以下のものとは異なります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>PhantomJsCloudでスクレイピング</title> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> p { margin-top: 0px; margin-bottom: 0px; } .content { margin-bottom: 10px; } .post-time { font-weight: bold; } </style> </head> <body> <button onclick="ShowSource()">ソースを表示</button> <button onclick="ShowHato()">鳩の書き込みを表示</button> <div id = "result"></div> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <script src="./index.js"></script> </body> </html> |
index.js
1 2 3 4 5 6 7 |
let key = "KEYは各自で取得してください"; let url = 'https://lets-csharp.com/samples/2306/how-to-phantom-js-cloud/' let option = { url:url, renderType:"HTML", outputAsJson:true, }; |
レンダリングされたHTMLソースを取得
まずレンダリングされたHTMLソースを表示する処理を示します。
1 2 3 4 5 6 7 8 9 10 |
function ShowSource(){ payload = encodeURIComponent(JSON.stringify(option)); let apiUrl = "https://phantomjscloud.com/api/browser/v2/"+ key +"/?request=" + encodeURIComponent(JSON.stringify(option)); $.get(apiUrl, null, ShowSource2); } function ShowSource2(data, status, xhr){ let source = data["content"]["data"]; document.getElementById('result').innerText = source; } |
必要な部分だけを抜き出す
次に「鳩でもわかるC#」の書き込みのみを表示する処理を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
function ShowHato(){ payload = encodeURIComponent(JSON.stringify(option)); let apiUrl = "https://phantomjscloud.com/api/browser/v2/"+ key +"/?request=" + encodeURIComponent(JSON.stringify(option)); $.get(apiUrl, null, ShowHato2); } function ShowHato2(data, status, xhr){ let source = data["content"]["data"]; document.getElementById('source').innerText = source; let parser = new DOMParser(); let doc = parser.parseFromString(source, 'text/html'); let elms = doc.getElementsByClassName('content'); let len = elms.length; let text = ''; for(let i = 0; i < len; i++){ doc = parser.parseFromString(elms[i].innerHTML, 'text/html'); let name = doc.getElementsByClassName('name')[0].innerHTML; let comment = doc.getElementsByClassName('user-comment')[0].innerHTML; let postTime = doc.getElementsByClassName('post-time')[0].innerHTML; // '鳩でもわかるC#'による書き込みのみ表示する if(name == '鳩でもわかるC#'){ text += `<div class="content">` text += `<p class="post-time">${postTime}</p>` text += `<p class="user-comment">${comment}</p>` text += `</div>` } } document.getElementById('result').innerHTML = text; } |
1日あたりの実行回数の制限があるので、似たような動作をさせているだけで、実際のコードは以下のものとは異なります。