7839

雑草魂エンジニアブログ

【Nuxt】Puppeteerでスクレイピング〜データ取得から表示まで(serverMiddleware)

今回は、Puppeteer を用いてスクレイピングし、取得したデータを表示するサンプルアプリケーションを構築したので紹介する。

f:id:serip39:20200613153917j:plain

Puppeteerとは

github.com

アプリケーション構成

今回は、Dockerに開発環境を構築し、Nuxt.js上でアプリケーションを完結させる構成とした。

Dockerの構成

参考: Running Puppeteer in Docker

なお、実装したサンプルコードは GitHub に公開している。 Dockerから構築したため、pullして、Dokcerを立ち上げるのみで確認ができるようにした。

github.com

本記事では、ポイントだけを抜粋して説明するため、実際にコード全体を確認する場合は GitHub を参照ください。

NuxtでのAPI(サーバーサイドの処理)を実装する方法

基本的には、2つの方法があるようだ。
(上記で、APIと述べているのは、フロント側から何らかの形で呼び出して、データを得ることを想定しているためである。)

  1. Expressのミドルウェアとしてnuxtを使う
  2. NuxtのサーバーミドルウェアとしてExpressを使う

1. Expressのミドルウェアとしてnuxtを使う

構成図としては、以下のようになる。

f:id:serip39:20200613111850j:plain

nuxtで create-nuxt-app でサーバーサイドのフレームワークExpress を選択した場合は、こちらの方式となる。

const express = require('express')
const { Nuxt, Builder } = require('nuxt')
const app = express()

<- 省略 ->

こちらの場合は、APIを組み込む場合は、通常のExpress同様に、 app.use('/api', function (req, res, next) { (api処理) }) を実装することで実現できる。

2. NuxtのサーバーミドルウェアとしてExpressを使う

ja.nuxtjs.org

Nuxt は内部で connect のインスタンスを作ります。 それはミドルウェアをスタックに登録したり、 外部サーバーを必要とせず に API などのルートを増やす事を可能にしてくれます。

Connectとは何かと思い、調べると以下の通りでした。

github.com

Connect is an extensible HTTP server framework for node using "plugins" known as middleware.

これは便利かもしれない。APIなどのルートを増やして、データの登録などができる!
json形式で返却もできれば、言うことなし!

と思いきや、 res.end()しか使えませんでした。
requestに対するライブラリー(body-parserなど)は充実しているので、フロント側からデータを受け渡して、サーバー側でミドルウェア的に処理することはできるが、APIみたいに返却値を返すことはConnectでは厳しいみたいでした。

そこで、 Express を導入することに変更せざるを得ませんでした。

でも、そもそもサーバーミドルウェアってそもそもなんだ?ってことで、Nuxt.jsのライフサイクルを確認。

https://ja.nuxtjs.org/nuxt-schema.svg

このライフサイクルのミドルウェアで実行されるのか思いきや、サーバーミドルウェア vs ミドルウェア!に以下のように記載されていた。

クライアントサイドや SSR の Vue で各ルートの前に呼び出されている ルーティングのミドルウェア と混同しないでください。 serverMiddleware は vue-server-renderer の 前に サーバー側で実行され、API リクエストの処理やアセットの処理などのサーバー固有のタスクとして使用できます。

ここの認識を間違えると大変なので、公式を確認することはやはり大切だと実感。

どちらにおいても、Expressを使用するので、正直大差はない気がするが、APIにどれだけの機能を実装するのか、またどんな機能を実装するかで選択すべきかと思う。 - APIをガッツリ開発する場合は、 「1. Expressのミドルウェアとしてnuxtを使う」 - サーバー側でデータ取得を行い、フロントから実行することがない場合、 「2. NuxtのサーバーミドルウェアとしてExpressを使う」 こんなイメージではないだろうか。 今回は、サーバー側でスクレイピングしたデータをサーバーでレンダリングする際に取得するだけなので、「2. NuxtのサーバーミドルウェアとしてExpressを使う」を使うこととした。

export default {
  serverMiddleware: [
    '~/server',
  ],
  <- 省略 ->
}
const express = require('express');
const app = express();
const scraping = require('./scraping')

app.get('/scraping', async(req, res) => {
  const data = await scraping.train()
  res.json(data)
})

module.exports = {
  path: '/api',
  handler: app
}

スクレイピング

基本的には、 APIドキュメント に従って実行することができる。 何でもできるように、様々なメソッドがあるが、スクレイピングのみであれば以下があれば十分かと思われる。

取得した要素に対して、以下のメソッドを用いる

取得したい要素のセレクタを確認する場合は、Chromeディベロッパーツールで確認する。

developers.google.com

今回は試験的に、Yahooの運行情報のページをスクレイピングしてみたので、参考にしてみてください。

画面表示に関して

Nuxtのサーバーミドルウェアとして実行するので、 asyncData を使い、データを取得する。

async asyncData({ $axios }) {
  const data = await $axios.$get('/api/scraping')
  return { data }
},

data: () => ({
  data: []
})

データの更新ボタンも設けているが、画面をリロードさせているだけである。

f:id:serip39:20200613153917j:plain

以上、今回はPuppeteerでスクレイピングを行い、Nuxt.js上で表示するまでを実装した。 スクレイピングをやる際に、Puppeteerは非常に便利だと感じた。

では、ステキな開発ライフをー