C#でTwitterのツイートをスクレイピングすることはできるのでしょうか? 実際に検索してみると情報が公開されていますが、じっさいに書いてあるとおりにやってもうまくいきません。HTMLを解析してスクレイピングする場合、仕様を変更されるとその方法は通用しません。
HTMLをみてもそこにはツイートの内容は書かれていません。JavaScriptで動的に表示されるコンテンツなので
1 2 3 |
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(); string url = "http://twitter.com/XXXX"; string str = await client.GetStringAsync(new Uri(url)); |
とやってstrを解析しても肝心のツイートを取得することはできません。
それからHttpClientをusingで囲っているサンプルコードを見かけますが、やってはいけません。
そこでSelenium.WebDriverを使います。NuGetでSelenium.WebDriverとSelenium.Support、そして操作したいブラウザのDriverを入手します。Chromeを操作するのであれば、Selenium.Chrome.WebDriverを入手します。
ただし注意点があります。まずは失敗例から紹介します。
1 2 3 4 5 6 7 8 |
using OpenQA.Selenium.Chrome; ChromeDriver driver = new ChromeDriver(); driver.Url = "http://twitter.com/XXXX"; string source = driver.PageSource; driver.Quit(); driver.Dispose(); |
これでsourceを調べてみるとツイートは取得できていません。
下の方法であれば取得できます。5秒くらい待つのがポイントです。JavaScriptの読み込みに時間がかかっっているということなのでしょうか? 1秒ではダメです。5秒くらい待ちましょう。
1 2 3 4 5 6 7 8 9 |
ChromeDriver driver = new ChromeDriver(); driver.Url = "http://twitter.com/XXXX"; System.Threading.Thread.Sleep(5000); // 待つのがポイント string source = driver.PageSource; driver.Quit(); driver.Dispose(); |
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 |
<div class="css-1dbjc4n"> <div class="css-1dbjc4n"> <div dir="auto" class="css-901oao r-18jsvk2 r-1tl8opc r-a023e6 r-16dba41 r-ad9z0x r-bcqeeo r-bnwqim r-qvutc0" lang="ja"> <span class="css-901oao css-16my406 r-1tl8opc r-bcqeeo r-qvutc0"> 【ツイートがここに入っている】 </span> </div> </div> <div class="css-1dbjc4n"></div> <div aria-label="1 件のいいね" role="group" class="css-1dbjc4n r-18u37iz r-1wtj0ep r-1s2bzr4 r-1mdbhws"> <div class="css-1dbjc4n r-18u37iz r-1h0z5md"> <div aria-label="0 件の返信。返信する" role="button" data-focusable="true" tabindex="0" class="css-18t94o4 css-1dbjc4n r-1777fci r-11cpok1 r-1ny4l3l r-bztko3 r-lrvibr" data-testid="reply"> <div dir="ltr" class="css-901oao r-1awozwy r-m0bqgq r-6koalj r-1qd0xha r-a023e6 r-16dba41 r-1h0z5md r-ad9z0x r-bcqeeo r-o7ynqc r-clp7b1 r-3s2u2q r-qvutc0"> <div class="css-1dbjc4n r-xoduu5"> <div class="css-1dbjc4n r-1niwhzg r-sdzlij r-1p0dtai r-xoduu5 r-1d2f490 r-xf4iuw r-1ny4l3l r-u8s1d r-zchlnj r-ipm5af r-o7ynqc r-6416eg"> </div> <svg viewBox="0 0 24 24" class="r-4qtqp9 r-yyyyoo r-1xvli5t r-dnmrzs r-bnwqim r-1plcrui r-lrvibr r-1hdv0qi"></svg> </div> <div class="css-1dbjc4n r-xoduu5 r-1udh08x"> <span class="css-901oao css-16my406 r-1tl8opc r-n6v787 r-1sf4r6n r-1k6nrdp r-1e081e0 r-d3hbe1 r-1wgg2b2 r-axxi2z r-qvutc0"> </span> </div> </div> </div> </div> |
長々と引用してしまいましたが、<span class=”css-901oao css-16my406 r-1tl8opc r-bcqeeo r-qvutc0″> の部分を抜き出せばツイートを取得することができそうなのですが、実はこの部分はツイート以外の部分もあるので、その親である <div dir=”auto” class=”css-901oao r-18jsvk2 r-1tl8opc r-a023e6 r-16dba41 r-ad9z0x r-bcqeeo r-bnwqim r-qvutc0″ lang=”ja”> の部分を抜き出します。
以下のようにできれば簡単なのですが、FindElementsByClassNameメソッドの引数にスペースが入っているとエラーになるのでAngleSharpを使います。
1 2 3 4 5 6 7 8 |
using OpenQA.Selenium; // ここでエラーとなる var elms = driver.FindElementsByClassName("css-901oao r-18jsvk2 r-1tl8opc r-a023e6 r-16dba41 r-ad9z0x r-bcqeeo r-bnwqim r-qvutc0"); foreach (IWebElement elm in elms) { string str = elm.Text; } |
AngleSharpならエラーが出ないので・・・
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 |
private void button1_Click(object sender, EventArgs e) { ChromeDriver driver = new ChromeDriver(); string url = "http://twitter.com/HoraguchiTomoko"; driver.Url = url; System.Threading.Thread.Sleep(5000); string source = driver.PageSource; driver.Quit(); driver.Dispose(); AngleSharp.Html.Parser.HtmlParser parser = new AngleSharp.Html.Parser.HtmlParser(); AngleSharp.Html.Dom.IHtmlDocument doc = parser.ParseDocument(source); AngleSharp.Dom.IHtmlCollection<AngleSharp.Dom.IElement> elms = doc.GetElementsByClassName("css-901oao r-18jsvk2 r-1tl8opc r-a023e6 r-16dba41 r-ad9z0x r-bcqeeo r-bnwqim r-qvutc0"); StringBuilder sb = new StringBuilder(); foreach (AngleSharp.Dom.IElement elm in elms) { string str = elm.TextContent; sb.Append(str + "\n\n"); } richTextBox1.Text = sb.ToString(); } |