Help us understand the problem. What is going on with this article?

Headless Chromeを試してみる

この記事の内容は古いです

時々いいねされて気になったのですが、いま(2019/10現在)からだと puppeteer を使うのがいいと思います
https://qiita.com/tags/puppeteer

Headlessブラウザとは

JavaScriptSPAの普及によりAjaxなど動的なページが増え、Webサイトをスクレイピングする際にcurlなどでHTMLを取得しても取得したいデータが見つからないことが多々ある。
そのためページ内のデータを取得したい場合はHeadless(画面に表示されない)ブラウザを裏で実行し、普通のブラウザと同じようJavaScriptを実行してページの内容を参照する必要がある。

Headlessブラウザにはどんなものがあるか

  • Phantom.js
  • Nightmare.js など

いままではPhantom.jsと、それを自動化するCasper.jsを何度か使っていたが、Chromeが公式でHeadless対応したためPhantom.jsのメンテナが辞任することを発表し一部で話題になりHeadless Chromeへの乗り換えを決意した。

Headless Chrome でスクレイピング

今回はmacOS環境のNode.jsからQiitaの投稿内容を取得してみる。

環境のインストール

Node.jsは既にインストール済みと想定して、
$ npm install chromy
ブラウザはGoogle ChromeGoogle Chrome Canaryを予めインストールしておく。

実際のコード

以下のコードはQiitaのプロフィール画面から記事のURLを取得し、
各記事に設定されているタイトル、タグ、投稿日時を取得する。

app.js
const Chromy = require('chromy')

async function scrap() {
  let chromy = new Chromy()

  await chromy.blockUrls(['*.ttf', '*.gif', '*.png', '*.jpg', '*.jpeg', '*.webp'])
  await chromy.goto('http://qiita.com/Ria0130')
  const urls = await chromy.evaluate(() => {
    let urls = []
    document.querySelectorAll("#main .tableList .media .ItemLink__title a").forEach(item => {
      urls.push('http://qiita.com/' + item.getAttribute('href'));
    })
    return urls
  })


  for(let url of urls){
    await chromy.goto(url)
    let json = await chromy.evaluate(() => {

      let title = document.querySelector("h1.ArticleMainHeader__title").innerHTML

      let tags = []
      document.querySelectorAll("#main .TagList a span").forEach(elem => {
        tags.push(elem.innerHTML)
      })

      let date = document.querySelector(".container .ArticleAsideHeader__date span time").getAttribute('datetime')

      return {
        title: title,
        tags: tags,
        date: date,
      }

    })
    console.log(json)
  }

  await chromy.close()

}

scrap()

解説

app.js
async function scrap() {

記事一覧記事A記事B記事Cと逐次処理が必要になるのでasync/awaitを使用した。
async/awaitについてはpromiseを書くよりも数段楽だったので後日記事にまとめたい。


app.js
  await chromy.blockUrls(['*.ttf', '*.gif', '*.png', '*.jpg', '*.jpeg', '*.webp'])

ブロックするURLを指定。ここでは画像等のファイルを開かずにスキップするよう指定している。


app.js
  await chromy.goto('http://qiita.com/Ria0130')
  const urls = await chromy.evaluate(() => {
    let urls = []
    document.querySelectorAll("#main .tableList .media .ItemLink__title a").forEach(item => {
      urls.push('http://qiita.com/' + item.getAttribute('href'));
    })
    return urls
  })

.goto()http://qiita.com/Ria0130を開き、
evaluate()でページ内容の操作を行っている。


app.js
  for(let url of urls){
    await chromy.goto(url)

その場でブラウザを閉じずに次(記事)のURLを開き同等の処理をforで回す。


app.js
await chromy.close()

.close()しないとブラウザのプロセスが残り続け、macOSの場合だとDockにブラウザアイコンが表示されたままになる。

感想

Node.jsからだとJavaScriptなのもあって非同期処理にさえ気をつければ簡単なスクレイピングは容易にできた。
ただ今回ページ内でJavaScriptのクリックイベント等動的な部分は試していないので難しいことをしようとするとどうなるかはまだわからない。

Headless Chrome + Python + ChromeDriver + Selenium で試したときは
ChromeDriverが原因の可能性もあるが、ウインドウ(タブ)の切り替えやファイルのダウンロードが上手く行かなかったので、普段のブラウザですることをなんでも出来るわけではなさそう。
この辺りは今後バージョンアップで改善してくれると嬉しいな。

Ria0130
フロントエンドエンジニア モダンなものが好き
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした