フロントから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
を作成
- プロジェクト直下に
server
フォルダ作成 -
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
"proxy": "http://localhost:3001"
フロント・バックエンドの起動スクリプト追加
package.json にバックエンド(server.js)の起動、フロント・バックエンドの同時起動のスクリプトを追加
-
npm run server
: バックエンド起動するときのコマンド -
npm run start-all
: フロント・バックエンド同時起動するときのコマンド
"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
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
※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 からプロキシ設定削除
- "proxy": "http://localhost:3001"
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