👾

ディズニーのグッズ情報をNext.jsのAPI Routesで取得してみる

に公開

このページでは、Next.jsのAPI routesを使用して、サーバーサイドでWEBスクレイピングをしてみました。

とりあえずurlからhtmlを取得してみる

/api/scraping/goods で欲しい情報をjsonで返すようにしてみます。
まず、簡単にディズニーリゾートのグッズ情報のテキストを取得するコードを調べて書いてみました。

route.ts
import { NextResponse } from 'next/server';

export const GET = async () => {
  const fetchUrl = 'https://www.tokyodisneyresort.jp/goods/116001582/';

  const response = await fetch(fetchUrl, {
    method: 'GET',
  });

  if (!response.ok) {
    return NextResponse.json({
      message: 'Failed to fetch data from the URL.',
    });
  }
  const data = await response.text();

  return NextResponse.json({
    message: 'GET method is not allowed for this endpoint.',
    data,
  });
};

はい。なんとこれだけ。調べててfetchで取得できることを知らなかったので簡単でびっくりしました。
(当たり前かもしれないけど、許して)

取得できたデータ(一部だけ)

"html": "<!DOCTYPE html>\n<html  lang=\"ja\">\n<head>\n<meta charset=\"UTF-8\">\n\n<link rel=\"shortcut icon\" href=\"/_public/favicon.ico?dummy=1518591858\" />\n<link rel=\"stylesheet\" type=\"text/css\" href=\"/css/goods.css\" charset=\"UTF-8\" />\n<link rel=\"stylesheet\" type=\"text/css\" href=\"/publis.css\" /><meta name=\"viewport\" content=\"width=device-width, minimum-scale=1, maximum-scale=1\" /><meta name=\"keywords\" content...

特定の要素を抽出してみる

ただ、このままだとただの文字列として返されてしまって、特定の要素を抽出するにはとても面倒なので"cheerio"というライブラリを使用します。

まずはインストール〜

yarn add cheerio

とりあえず、商品名を取得してみたいので先ほどのroute.tsを以下のように編集します

route.ts
+import * as cheerio from 'cheerio';
import { NextResponse } from 'next/server';

export const GET = async () => {
  //htmlを取得する
  const fetchUrl = 'https://www.tokyodisneyresort.jp/goods/116001582/';

  const response = await fetch(fetchUrl, {
    method: 'GET',
  });

  if (!response.ok) {
    return NextResponse.json({
      message: 'Failed to fetch data from the URL.',
    });
  }

  //変数名がわかりづらかったので変更
- const data = await response.text();
+ const html = await response.text();
+ const $ = cheerio.load(html);

  // ここで欲しい要素を取得(クラス名やタグは実際のHTMLに応じて変えてください)
  +const itemName = $('.heading1').text().trim();

  return NextResponse.json({
    message: 'GET method is not allowed for this endpoint.',
-   data,
+   itemName,
  });
};

結果

{
"message": "GET method is not allowed for this endpoint.",
"itemName": "ぬいぐるみコスチュームNEW"
}

無事取得できました。

結構簡単に取得、抽出ができました。

Vercelにデプロイ

Vercelにデプロイしてapiの動作を確認しようとアクセスしてみると、タイムアウトエラーが出てしまいました。色々調べてみたらNode.jsの時間制限に引っかかってるっぽいので、Edge Functionsを使用することにしました。

route.ts
export const runtime = 'edge';

route.tsの一番上に↑を追加して終わり。
無事タイムアウトにならず処理することができました。

終わり

ローカルだと動いて、Vercelだとタイムアウトする理由を特定するのと、それを修正するのに時間がかかってしまいましたが、学べたので良しですね。

もっといい方法があったら教えてください!

Discussion