0
1

フロントからWebスクレイピング ( react, express 連携 )

Last updated at Posted at 2023-11-17

フロントからWebスクレイピンを行おうとするとCORSのエラーでだいたいはまる...
相手側の設定次第では回避できる時もあるがフロントのみだと解決できない...
そこで express で簡易にバックエンドのサーバ作って対応してみる
スクレイピングには jsdom 使ってます
サンプルでは yahoo news の画像を適当にとってきてます

フロントのみで CORS のエラー解消する方法あったら教えてください!!

実施環境

  • Windows11
  • node (v18.17.1) ※インストール済
  • react@18.2.0
  • express@4.18.2

手順

react プロジェクト作成・移動 (create-react-app)

create-react-app を使ってプロジェクトを作成
プロジェクト名は任意でOK!

  • プロジェクト作成
npx create-react-app project-name
  • プロジェクト移動
cd project-name

express インストール(バックエンドサーバ)

express のインストールの他に開発などを楽にするやつもインストール
・nodemon : バックエンド(express)起動時自動反映するため
・npm-run-all : フロント(react), バックエンド(express) を同時起動するため
・pino : express のログ出力するため

  • express
npm i express
  • 起動時反映・同時実行・ログ出力
npm i nodemon npm-run-all express-pino-logger pino-colada

jsdom インストール(Webスクレイピング)

Webスクレイピングするのに jsdom をインストール

npm i jsdom

バックエンドサーバ作成 (express)

実行モジュールはどこに作ってもOK
ここではプロジェクト直下に server 作ってその下にモジュール server.js を作成

  1. プロジェクト直下に server フォルダ作成
  2. server フォルダ直下に server.js 作成
    server.js
    // ライブラリ読込
    const express = require('express');
    const pino = require('express-pino-logger');
    const { JSDOM } = require('jsdom');
    
    // express作成
    const app = express();
    app.use(express.urlencoded({ extended: false }));
    app.use(pino());
    
    // 簡易なJSON返すAPI
    app.get('/api/sample1', async (req, res) => {
      res.setHeader('Content-Type', 'application/json');
      res.send(JSON.stringify({ sample: 'Hello world!' }));
    });
    
    // 簡易にWebスクレイピングしてJSON返すAPI
    app.get('/api/sample2', async (req, res) => {
      // fetch でサイトとってきて dom を生成
      const url = 'https://news.yahoo.co.jp/';
      const urlRes = await fetch(url);
      const urlHtml = await urlRes.text();
      const dom = new JSDOM(urlHtml);
      // ほしい情報を querySelector などを使って取得
      const imgSrcs = [...dom.window.document.querySelectorAll('section img')].map(
        (img) => img.src
      );
    
      res.setHeader('Content-Type', 'application/json');
      res.send(JSON.stringify({ sample: imgSrcs }));
    });
    
    // サーバ起動
    app.listen(3001, () =>
      console.log('Express server is running on localhost:3001')
    );
    
    

サーバへのプロキシ設定

package.json にプロキシの設定を追加
追加する場所はどこでもOK

package.json
  "proxy": "http://localhost:3001"

フロント・バックエンドの起動スクリプト追加

package.json にバックエンド(server.js)の起動、フロント・バックエンドの同時起動のスクリプトを追加

  • npm run server : バックエンド起動するときのコマンド
  • npm run start-all : フロント・バックエンド同時起動するときのコマンド
package.json
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
-    "eject": "react-scripts eject"
+    "eject": "react-scripts eject",
+    "server": "nodemon server/server.js | pino-colada",
+    "start-all": "run-p server start"
  },

フロント(react) からAPI実行の簡易サンプル

App.js にバックエンドの以下のAPI実行するサンプルコードを記載
元のコードあるけど全部、置き換えてOK

  • /api/sample1 : 簡易な JSON を返すAPI
  • /api/sample2 : 簡易にWebスクレイピングした値を設定した JSON を返すAPI
App.js
import { useState } from 'react';

function App() {
  const [sample1, setSample1] = useState('');
  const [sample2, setSample2] = useState('');
  // api/sample1 実行して sample1 に設定する処理
  const getApiSample1 = async () => {
    const json = await (await fetch('/api/sample1')).json();
    setSample1(json.sample);
  };
  // api/sample2 実行して sample2 に設定する処理
  const getApiSample2 = async () => {
    const json = await (await fetch('/api/sample2')).json();
    const imgs = json.sample.map((src, i) => {
      return (
        <>
          <img src={src} alt={i}></img>
        </>
      );
    });
    setSample2(imgs);
  };

  return (
    <>
      <button onClick={getApiSample1}>get api sample1</button>
      <p>{sample1}</p>
      <button onClick={getApiSample2}>get api sample2</button>
      <p>{sample2}</p>
    </>
  );
}

export default App;

実行確認

フロント・バックエンドを同時実行で以下の画面がブラウザで上がってくる
あがってこないときは htttp://localhost:3000 にブラウザでアクセス

  • 実行コマンド
npm run start-all
  • こんな感じになればOK(モザイクは通常かからないです)
    image.png

※React実行でエラーが出る場合

開発を繰り返しているうちに何故か React の実行で以下のエラーが出るようになったので対応を記載
理由については不明...
最初からこっちの方が安定するかも...
React でもこんな感じで紹介してる方法です
https://create-react-app.dev/docs/proxying-api-requests-in-development/

エラーログ
Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
 - options.allowedHosts[0] should be a non-empty string.

http-proxy-middleware インストール

npm i http-proxy-middleware

package.json からプロキシ設定削除

package.json
-  "proxy": "http://localhost:3001"

setupProxy.js 作成

フォルダ src 直下に setupProxy.js を作成

src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = (app) => {
  app.use(
    '/api/*',
    createProxyMiddleware({
      target: 'http://localhost:3001',
      changeOrigin: true,
    })
  );
};

実行確認

実行方法はいままでと同様、エラーなく実行できればOK

npm run start-all
0
1
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
0
1