ヘッドレス Chrome を使えるようになりたいのだ!

「ヘッドレス」 ブラウザってなに?

みなさんがネット今見ているもの、それがブラウザです。
「前に戻る」「アドレスバー」など様々な部品で構成されていますが、そういった目で見えるものがなく、クリックではなく命令言語で動かせる。
それが「ヘッドレス ブラウザ」なのです。

それってなんの意味があるの?

よく「業務が自動化できる」ということがメリットに挙げられます。
言葉としての意味はその通りなんでしょうが、なんとも非常にピンとこない。
個人的にはこれからの時代は「AIがお金を稼いでくれる」という言葉にも同じものを感じます。
噛み砕いて説明しますと、普段皆さんはブラウザで仕事をすることが多いと思います。
たとえば「Aというサイトからとある製品の金額を調べてファイルに記載する」という作業があったとします。これを分解すると

  • ブラウザを立ち上げる
  • アドレスバーに「A 製品名 金額」などと検索ワードを打ちます。
  • 表示された検索結果から A社の目的の製品のページを探し、サイトを表示します。
  • 製品の金額を探しコピーします。
  • ファイルを開き、書き込み保存します

といった流れになります。
もしこれが毎日必要なタスクだったとすると、
このタスクを「ヘッドレスブラウザで毎日自動で3時に行う」 と設定します。

  • 特に作業なし

となります。ヘッドレスブラウザがちゃんと動くようにさえしておけば何もする必要がないのです。
これが「業務の自動化」です。
ただし、検索する製品が同じ場合に限ってしまいます。
この製品を自動で見つけて検索させるとなると、非常に複雑な命令になってしまうと思います。きっと。

ルーティーンで大量な作業をさせるのは非常に得意なものになります。
「スパム」なんて一般的に言われてるものも、こういった自動化作業の負の産物なのかもしれません。

ウェブスクレイピング というネット上から情報を集めてくるものもこれで可能になるはずです。

どーやってつかうのさ

PCのターミナル/コマンドラインというところから命令を送ります。
それだと別にあんまり便利ではないので、命令を記載したファイルを用意しておきそれをアプリから実行することが可能です。
node.jsというものを使うのが一般的みたいですね。pythonでも動かせるみたいです。
自分のPCからnode.jsで動かすことはもちろん可能ですが、node.jsが動く「サーバー」で自動化作業をさせるのもよくあることみたいです。

こうすると、自分のパソコンの電源を常に入れておく必要がなくより自動感がありますね。

利用環境を整えよう

今回はサーバーで動かすまで行かないで、自分のパソコンでヘッドレスブラウザを動かすところをやって見たいと思います。
偉そうに講釈を垂れてますが、自分も触るのはこれが初めてです。

参考 こちらの記事
https://developers.google.com/web/updates/2017/04/headless-chrome?hl=ja

なんか、簡単に扱えるライブラリがいくつか存在するみたいです。
「chromy」 と 「puppeteer」

比較してる記事がありました
http://techblog.raccoon.ne.jp/archives/52414951.html

「puppeteer」が一応公式開発のものみたいなのでこちらの情報を収集して頑張っていきたいと思います。

OSは mac
ターミナルは vscode によってやりたいと思います。

一応「puppeteer」なしで動かします。

とりあえず動かす。

chromeをインストールして、

適当なフォルダを作成。vscodeで開きます。

chromeのインストール場所を指定します。(以下はmacの場合)

alias chrome="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome.app"

PDFの撮影をしてくれます。

chrome --headless --disable-gpu --print-to-pdf https://google.com/

puppeteer をインストールする

先ほど作ったフォルダで継続して行います。
nodeをインストールしてある環境なので「npm」でやっちゃいます。
未導入の場合は以下からインストールで
https://nodejs.org/ja/

npm install puppeteer

ではtest.jsを作っていきましょう。

test.js

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({ args: ['--no-sandbox'] });
    const page = await browser.newPage();
    await page.goto('https://www.yahoo.co.jp');
    await page.screenshot({ path: 'screenshot.png', fullPage: true });
    await browser.close();
})();

実行は以下の通り

node test.js

「yahoo」のスクリーンショットを取ってくれます。(すげぇ)

puppeteerでなにができるか知りたいんだよなぁ・・・

配布先でapiの説明があります。
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md

英語が苦手なのが恨めしいですなぁ

googleで検索して1位のサイトをスクショ

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({ headless: true });
    const page = await browser.newPage();

    // Googleにアクセス
    await page.goto('https://www.google.co.jp/');
    // 検索窓に「logzitsu」と入力
    await page.type('#lst-ib', 'logzitsu');
    // 検索ボタンクリック
    await page.focus('input[name="btnK"]');
    await page.click('input[name="btnK"]');
    // 遷移完了を待機
    await page.waitForNavigation();
    // 検索結果の先頭リンクをクリック
    await page.click('.rc > .r > a');
    await page.waitFor(5000);
    // ページタイトルを表示
    const title = await page.title();
    console.log(title);

    // スクリーンショット
    await page.screenshot({ path: 'ss.png', fullPage: true });

    await browser.close();
})();

googleサイトのロゴ画像を保存

const puppeteer = require('puppeteer');
const fs = require('fs');

(async () => {
    const browser = await puppeteer.launch({ args: ['--no-sandbox'] });
    const page = await browser.newPage();

    // ページ作成
    await page.goto('https://www.google.co.jp/');
    await page.waitFor(2000);

    // ページタイトル出力
    console.log(await page.title());

    // URL出力
    console.log(await page.url());

    //特定の場所のテキストを出力 今回は右上のナビのclassを指定
    const texts = await page.$eval('.gb_P', item => { return item.textContent });
    console.log(texts);

    // スクリーンショット撮影
    await page.screenshot({ path: 'screenshot01.png', fullPage: true });

    //ロゴ画像ソースの取得
    var imgurl = await page.$eval('#hplogo', el => el.src);
    var viewSource = await page.goto(imgurl);
    fs.writeFile("./logo.png", await viewSource.buffer(), function (err) {
        if (err) {
            return console.log(err);
        }
        console.log("画像を保存したよ");
    });

    await browser.close();
})();

めっちゃ汎用性あるなぁ。
もーちょいいろいろ組み合わせるといろんなことができそうな予感です。
メールで結果を送ったり、チャットワークにメッセージを送ったりね・・・。