puppeteerを使ってtwitterでツイートしてみteer

1. この記事はなに

この記事は、Googleの出しているヘッドレスブラウザのpuppeteer(読み:パペッターパペッティアァ)を触ってみたという記事です。
puppeteerからtwitterにログインしてツイートしてみるというだけの簡単な内容なので、何も難しいことはやっていません。

2. puppeteerとは

ヘッドレスブラウザの一種です。ヘッドレスブラウザは、GUIの無いブラウザのことらしいです。
GUIのないブラウザの何が嬉しいんじゃー!というと、プログラムを書いてシュッとWEBクローリングができることが嬉しい。「それなら普通にスクレイピングでよくない?WOWOW」とか思うかもしれませんが、最近はJavascripで全ての遷移とかをやるようなSPAも増えてきていて、リクエスト投げたら静的なHTMLが返ってきてタグ解析したら終わり!みたいな状況では無くなってきています。ヘッドレスブラウザを使えばクリック操作やinput操作など、普段GUIのあるブラウザ上で行なっている操作をそのまんま行えるので、そうしたWEBアプリケーションとかの解析も簡単にできるよっていう触れ込みらしいです。
まあ、詳しくは僕も分からないので調べてください。そして僕に教えてください。

3. 実行環境

puppetterは本当に前準備とかほとんど要らないので、わざわざ書くこともないかと思いましたが一応各種環境を記しておきます。

OS:mac
node: v9.11.1
yarn:1.6.0(npmでも可)
typescript: ES2015(Promise, async/await周りが使えれば何でもいいと思う)

4. 早速実装

まず最初にソースコード全文を見てもらった方が早いと思うので、どうぞ。

import * as pptr from 'puppeteer';

async function run(){
    // ブラウザを起動する
    const browser = await pptr.launch({
        args: ['--lang=ja,en-US,en'] // デフォルトでは言語設定が英語なので日本語に変更
    })

    // ページをつくる
    const page = await browser.newPage()

    // サイズを決める
    await page.setViewport({ width: 720, height: 600 })

    // ページを開く
    await page.goto('https://twitter.com/IsPuppeteer')

    // ページタイトルを取得
    console.log(await page.title())

    // スクリーンショットをパシャり
    await page.screenshot({path: 'sample1.png', fullPage: true})

    // ログイン
    await page.type('#signin-dropdown > div.signin-dialog-body > form > div.LoginForm-input.LoginForm-username > input', 'IsPuppeteer');
    await page.type('#signin-dropdown > div.signin-dialog-body > form > div.LoginForm-input.LoginForm-password > input', '**password**');
    await page.click('#signin-dropdown > div.signin-dialog-body > form > input.EdgeButton.EdgeButton--primary.EdgeButton--medium.submit.js-submit');

    await page.waitFor(5000);

    // ツイートしてみる
    await page.click('#global-new-tweet-button');
    await page.type('#Tweetstorm-tweet-box-0 > div.tweet-box-content > div.tweet-content > div.RichEditor.RichEditor--emojiPicker.is-fakeFocus > div.RichEditor-container.u-borderRadiusInherit > div.RichEditor-scrollContainer.u-borderRadiusInherit > div.tweet-box.rich-editor.is-showPlaceholder',
                    'これはpuppeteerからのツイートです。');
    await page.screenshot({path: 'sample2.png', fullPage: false})
    await page.click('#Tweetstorm-tweet-box-0 > div.tweet-box-content > div.TweetBoxToolbar > div.TweetBoxToolbar-tweetButton > span > button.SendTweetsButton.EdgeButton.EdgeButton--primary.EdgeButton--medium.js-send-tweets');

    await page.waitFor(5000);

    // 終了
    await browser.close()
};

run();

めちゃ長い文字列は、全部ただのセレクタなので怖がらないでください。Twitterセレクタが長いのが悪い。

puppeteerはすごいシンプルに扱えてすごいめうという感じです。
基本的には、最初にbrowser.newPage()pageを作り、それについて色々な操作をやっていくという感じでしょうか。
page.typeは第一引数にセレクタを取り、そのセレクタに対して第二引数の文字列を入力します。また、page.clickは、第一引数にセレクタを取り、それに対してクリックの実行を行います。
ところどころにawait page.waitFor(5000);が入っているのは、リクエスト送信後にちょっと待たないと、処理が早すぎてレスポンスが返って来る前に次に行ってしまうからです。
page.type('...', 'これはpuppeteerからのツイートです。');が終わった時点でこんな感じのスクリーンショットが撮れます。

f:id:hilinker:20180830112139p:plain

あとは、page.click()でツイートボタンをクリックすればツイートされます。

f:id:hilinker:20180830112433p:plain

いえーい。

5. 完走した感想

今回は簡単なことしかやっていないが、多分もっと色々できると思います。
TwitterAPIを使わずにこんな自動化的なことやって大丈夫なの?と若干思って、規約を確認してみましたが、

Twitterから提供されている当社の現在利用可能な公開インターフェースを経由する(かつ、その使用条件を遵守する)ことなく、その他の何らかの手段(自動その他を問わず)でTwitterにアクセスまたはその検索を行うか、またはアクセスまたは検索を試みること(は禁止とする)

と書いてあり、今回はヘッドレスブラウザとはいえちゃんとtwitter公式WEBアプリを使っているのでセーフと結論付けました。だってブラウザだもん!普通にブラウザでtwitterやってるだけだもん!!
いや、流石にスパム目的とかで使うのは完全アウトですけど、私的利用の範疇で何かやる分にはいいんじゃないかなぁ(あんまり無責任なことは言えないので、自己責任でお願いします)。
最近twitterAPIが規制が厳しくなったりしたので、開発者登録とかめんどくさい人とかは試しにpuppeteerを使ってみてもいいんじゃないでしょうか。色々捗るぞ。

6. 参考

GitHub - GoogleChrome/puppeteer: Headless Chrome Node API

Puppeteerがクローリングに使えそう · GitHub