LoginSignup
7
3

More than 1 year has passed since last update.

「動的にJavaScriptで出力されるhtmlデータ」をElixirを使って取得することを楽しむ

Last updated at Posted at 2022-06-05

夜をこめてとりのそらねははかるともよに逢坂の関は許さじ

Advent Calendar 2022 114日目1の記事です。
I'm looking forward to 12/25,2022 :santa::santa_tone1::santa_tone2::santa_tone3::santa_tone4::santa_tone5:
私のAdvent Calendar 2022 一覧


はじめに

Elixir を楽しんでいますか:bangbang::bangbang::bangbang:

この記事は、「動的にJavaScriptで出力されるhtmlデータ」を取得することをやってみます。
Pythonの記事が多いです。
とても参考にさせてもらいました。

私はもちろん、Elixirを使います。

準備

まず準備を進めます。
私はmacOSを使っています。

スクレイピングする対象

ヨソサマにご迷惑をかけぬようにしたいとおもいます。
おもうだけでは駄目なので、簡易なものを手元に用意します。

スクレイピングする対象を用意するというわけです。
以下、一例を示します。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>Vue.js App</title>
</head>
<body>
  <div id="list-rendering">
    <ol>
      <li v-for="todo in todos">
        {{ todo.text }}
      </li>
    </ol>
  </div>
  <script
    src="https://unpkg.com/vue@next"></script>
  <script src="main.js"></script>
</body>
</html>
main.js
const ListRendering = {
  data() {
    return {
      todos: [
        { text: 'Learn JavaScript' },
        { text: 'Learn Vue' },
        { text: 'Build something awesome' }
      ]
    }
  }
}

Vue.createApp(ListRendering).mount('#list-rendering')

上記の2ファイルを同じディレクトリに置いておきます。
たとえばDockerをお使いの場合は以下のようにするとよいでしょう。

docker run --rm -v $(pwd):/usr/share/nginx/html:ro -p 8080:80 -d nginx

http://localhost:8080
に訪れると以下のようなページが表示されます。

スクリーンショット 2022-06-05 17.18.42.png

ChromeブラウザとChromeDriverのインストール

雑な説明で恐縮ですが、今回は静的なページではなく、動的にJavaScriptで内容がかわるhtmlを解析したいので、スクレイピングプログラムの実行で、あたかもブラウザでアクセスしたかのようにアクセスをする必要があります。
それで、ChromeブラウザとChromeDriverのインストールが必要となります。

Chromeブラウザはリンク先のインストーラでインストールしました。

ChromeDriverは、Chromeブラウザのバージョンと近いものをインストールしておくことが吉のようです。
Chromeブラウザのバージョンは、右上の三点リーダ(︙)から確認できます。

スクリーンショット 2022-06-05 17.28.59.png

私の場合は、ChromeDriverのページにあるLatest stable releaseChromeブラウザとバージョンが近かったので、その.zipをダウンロードし、解凍して、下記のコマンドで/usr/local/bin/に配置しました。

sudo mv chromedriver /usr/local/bin/

あとで、スクレイピングを実行したときに、 「"chromedriver"は開発元を検証できないため開けません。」 と言われるかもしれません。
そのときはこちらの記事を参考にするとよいです。
この場をお借りして記事を書いてくださった方に感謝を申し上げます。

準備は以上です。

Elixirで楽しむ

wallabyを使います。
他に候補は、Houndがあります。

これらのライブラリの共通点は、テストのためというのを主眼に置いていることです。
つまりは自分のつくったアプリケーションのテストをしようね、と言うことを暗に言っているのだとおもいます。
テストの中にはふつうにElixirのプログラムが書けますので、テストの中でスクレイピングすることにします。

以下、wallabyのドキュメントに書いてある通りの手順です。

プロジェクトの作成

まずはプロジェクトを作っておきましょう。

mix new sample

wallabyの追加

mix.exsに、wallaby Hexを追加します。

mix.exs
  defp deps do
    [
      {:wallaby, "~> 0.29.0", runtime: false, only: :test}
    ]
  end

ファイルを変更後、mix deps.getで依存関係を解決します。

mix deps.get

test_helper.exs の変更

test/test_helper.exs
ExUnit.start()
{:ok, _} = Application.ensure_all_started(:wallaby) # add
Application.put_env(:wallaby, :base_url, "http://localhost:8080") # add

テストファイルを書く

test/scraping_test.exs
defmodule ScrapingTest do
  use ExUnit.Case, async: true
  use Wallaby.Feature

  feature "visit /index.html", %{session: session} do
    page_source =
      session
      |> visit("/index.html")
      |> page_source()

    IO.puts(page_source)
  end
end

Run

実行してみます。

mix test

結果:tada::tada::tada:

<html lang="ja"><head>
  <meta charset="utf-8">
  <title>Vue.js App</title>
</head>
<body>
  <div id="list-rendering" data-v-app=""><ol><li>Learn JavaScript</li><li>Learn Vue</li><li>Build something awesome</li></ol></div>
  <script src="https://unpkg.com/vue@next"></script>
  <script src="main.js"></script>


</body></html>

JavaScriptで動的に埋め込まれたデータが得られています!!!
main.js

      todos: [
        { text: 'Learn JavaScript' },
        { text: 'Learn Vue' },
        { text: 'Build something awesome' }
      ]

この部分です。

  • Learn JavaScript
  • Learn Vue
  • Build something awesome

(おまけ) テキストだけ取り出す ーー Flokiを簡単に紹介します

PythonのBeautiful Soupに相当するもとして、ElixirFlokiがあります。
Flokiを利用して、動的に埋め込まれたテキストを取り出してみます。

mix.exsFlokiを追加します。

mix.exs
  defp deps do
    [
      {:wallaby, "~> 0.29.0", runtime: false, only: :test},
      {:floki, "~> 0.32.0"}
    ]
  end

mix.exsを変更したら、依存関係を解決します。

mix deps.get

test/scraping_test.exsを修正します。
Flokiの使い方はドキュメントをご参照ください :pray::pray_tone1::pray_tone2::pray_tone3::pray_tone4::pray_tone5:
以下、使い方の一例を示します。

test/scraping_test.exs
defmodule ScrapingTest do
  use ExUnit.Case, async: true
  use Wallaby.Feature

  feature "visit /index.html", %{session: session} do
    page_source =
      session
      |> visit("/index.html")
      |> page_source()

    IO.puts(page_source)

    # add ここから
    {:ok, document} = Floki.parse_document(page_source)

    Floki.find(document, "li")
    |> Enum.map(&Floki.text/1)
    |> IO.inspect()
    # add ここまで
  end
end

実行してみます。

mix test

結果は、以下の通りテキストの値がしっかり取得できています! :tada::tada::tada:

["Learn JavaScript", "Learn Vue", "Build something awesome"]

本当にこんな面倒くさいことをしないといけないの?

ふつうにindex.htmlをHTTP GETしただけでは、ブラウザでのアクセスのようにJavaScriptが実行されず、本当にindex.htmlのみしか取得できません。
以下、実行例です。

iex

iex> Mix.install [:req]
:ok

iex> Req.get!("http://localhost:8080/index.html") |> Map.get(:body) |> IO.puts
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>Vue.js App</title>
</head>
<body>
  <div id="list-rendering">
    <ol>
      <li v-for="todo in todos">
        {{ todo.text }}
      </li>
    </ol>
  </div>
  <script
    src="https://unpkg.com/vue@next"></script>
  <script src="main.js"></script>
</body>
</html>

もちろん静的なページであれば、単純なHTTP GETでよいです。

今回のように、「動的にJavaScriptで出力されるデータ」がある場合には、ChromeDriverの力を借りると、あたかもブラウザでアクセスしている体となり、所望のhtmlが得られるわけです。

Wrapping up :lgtm::lgtm::lgtm::lgtm::lgtm:

「動的にJavaScriptで出力されるhtmlデータ」をElixirを使って取得することを楽しんでみました。

久々に、AdventCalendar2022を書きました。

Enjoy Elixir:bangbang::bangbang::bangbang:
$\huge{Enjoy\ Elixir🚀}$


参考記事

以下、参考にした記事です。
各記事の作者の方には、この場をお借りして御礼申し上げます。

Python

Pythonの記事です。
参考になるところがたくさんありました。

Elixir

Elixirの記事です。


I organize autoracex.
And I take part in NervesJP, fukuoka.ex, EDI, tokyo.ex, Pelemay.
I hope someday you'll join us.

We Are The Alchemists, my friends!

  1. @kaizen_nagoya さんの「「@e99h2121 アドベントカレンダーではありますまいか Advent Calendar 2020」の改訂版ではありますまいか Advent Calendar 2022 1日目 Most Breakthrough Generator」から着想を得て、模倣いたしました。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3