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

Nuxt.jsからPuppeteerが実行できない TypeError: Cannot read property 'on' of undefined

More than 1 year has passed since last update.

現象

単体のCLIから起動すると成功するコードを、NuxtのserverMiddlewareとしてリクエスト経由で起動するとpage.gotoが動かない。

実行環境

  • Windows7
  • Node.js 11.0.0 (に上げたが変わらず)
  • Puppeteer 1.9.0 (および@nextも同様)
  • Nuxt.js 2.2.0

エラー出力

TypeError: Cannot read property 'on' of undefined
    at Function.addEventListener (~\node_modules\puppeteer\lib\helper.js:165:13)
    at new NavigatorWatcher (~\node_modules\puppeteer\lib\FrameManager.js:1146:14)
    at FrameManager.navigateFrame (~\node_modules\puppeteer\lib\FrameManager.js:75:21)
    at Frame.goto (~\node_modules\puppeteer\lib\FrameManager.js:404:37)
    at Frame.<anonymous> (~\node_modules\puppeteer\lib\helper.js:145:23)
    at Page.goto (~\node_modules\puppeteer\lib\Page.js:579:49)
    at Page.<anonymous> (~\node_modules\puppeteer\lib\helper.js:145:23)
(中略: 呼び出し元スクリプト)
    at process.internalTickCallback (internal/process/next_tick.js:77:7)

エラー箇所の原因

puppeteer\lib\Connection.js

  /**
   * @param {!CDPSession} session
   * @return {!Connection}
   */
  static fromSession(session) {
    let connection = session._connection;
    // TODO(lushnikov): move to flatten protocol to avoid this.
    while (connection instanceof CDPSession)
      connection = connection._connection;
    return connection;
  }

このループ文が、Nuxt経由の場合は、connectionがConnectionのインスタンスにも関わらず条件がtrueとなり掘り続けて、undefinedまで潜って終わるため。

console.logで見るとConnectionなのにinstanceof CDPSessionが通る理由はprototypeに明るくないのでよくわからない…

コードの意味を汲み取れないが、潜り続ける問題を場当たり的に回避したコードを書くと、Nuxt経由でもうまく動いた。

  /**
   * @param {!CDPSession} session
   * @return {!Connection}
   */
  static fromSession(session) {
    let connection = session._connection;
    // TODO(lushnikov): move to flatten protocol to avoid this.
    while (connection instanceof CDPSession) {
      if (connection._connection) {
        connection = connection._connection;
      } else {
        break;
      }
    }
    return connection;
  }

これで少なくとも返される値はundefinedではなくなる。


でもこれじゃnode_modulesのコードをいじるのでデプロイできないんですよね。

コード例

api.js(NuxtでserverMiddlewareとして動かすスクリプト)
module.exports = async (req, res, next) => {
   const puppeteer = require('puppeteer')
   const browser = await puppeteer.launch({ headless: true, })
   const page = await browser.newPage()
   await page.goto('https://www.google.com/')
}
nuxt.config.js
const serveStatic = require('serve-static')

module.exports = {
    serverMiddleware: [{
        path: '/api', handler: '~/api.js'
    },]
}

nuxt で http://localhost:3000 に立てhttp://localhost:3000/apiにアクセスすると、api.jsが実行される。

調査

エラー的に似てそうなのはこれなのだが関係ないと考えている。
ただ、Nuxtが裏でどう取りまとめているのかを理解していないのでなにか無理解が原因の可能性もあるのではと思っている。
zero configurationで動くのは本当にすごい1がエラーが出たときにこまることを痛感。

どうする?

expressとpuppeteerの連携はいくつか見かけたので、Nuxtからexpressに乗り換えるかserverMiddlewareをexpressにしてそこからpuppeteer起動でなんとかならないかと考えている。

しかしすごく遠回りなのでモチベーションがまったく出ない。


追記

serverMiddlewareをexpressにしたが、こちらでも同様のエラーが出た。
そのままexpressのコードでexpress単体でサーバーを立てリクエストしたら成功したので…

nuxtとpuppeteerを使う場合はexpressをメインでnuxtをミドルウェアとして実装するのが正解っぽい。

こっちの方法を調べて移植してみる。

追記の追記

expressメインで動かしました。

components/
express.js
node_modules/
package.json
package-lock.json
pages/
store/

こういう構成で、基本はNuxtのもの。

express.js
const express = require('express')
const { Nuxt, Builder } = require('nuxt')
const puppeteer = require('puppeteer')


// Create express instnace
const app = express()

const bodyParser = require('body-parser')
app.use(bodyParser.json())


const config = require('./nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')
const nuxt = new Nuxt(config)

if (config.dev) {
    const builder = new Builder(nuxt)
    builder.build()
}

app.post('/api',  async (req, res) => {
    console.log(req.body.id)
   const browser = await puppeteer.launch({ headless: true, })
   const page = await browser.newPage()
   await page.goto('https://www.google.com/')
    res.send('success')
})

// 後ろに持ってこないとexpressのルーティングが効かない?
app.use(nuxt.render)

var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('Example app listening at http://%s:%s', host, port);
  });

ルーティングルールをまだ理解しきれてないが、望む動作は成功。
リクエストのparamを使いたい場合は、ちゃんとbodyParserを設定する。

app.use(nuxt.render)
を先に持ってくると/apiが404になってしまった。


  1. serverMiddlewareを使わなければnuxt.config.jsも不要 

khsk
週一ぐらいでなにか書いていました。 いろいろお休み中。
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
ユーザーは見つかりませんでした