やりたかったこと
シンプルにとあるWebサイトをスクレイピングして、情報を取得したかった。
過去にもスクレイパーを作成したことはあるのだが、その時リクエストを送信するのにrequestモジュールを使用していたが、どうやらパッケージが2020年2月に廃止されたらしい…
そこで見つけたのが「node-fetch」。
試しにこれを使って指定したページのHTML要素を取得したい。
使ってみる
TypeScriptをインストール
yarn add typescript
詳しい環境構築はこの記事の本筋からズレるため省略させていただきます。
node-fetchをインストール
yarn add node-fetch
ここでは詳しい使い方などは省略させていただきます。
詳しくはこちらを
サンプルコードを作成
importfetchfrom'node-fetch';consturl:string="https://qiita.com/";fetch(url).then(res=>res.text()).then(body=>console.log(body));
これでHTMLが取得できるはず。
補足
JavaScriptで動的に生成されている要素を取得したい場合、ヘッドレスブラウザが必要になるようです。
この記事の本筋と異なるため、こちらも省略させていただきます。
(今回スクレイピングするサイトには必要なかったので詳しく調べてないです…ごめんなさい)
実行してみる
ts-nodeを使用して実行してみる。
"scripts":{"start":"ts-node ./index.ts","build":"webpack"}
実行
$ npm run start
<!DOCTYPE html><html><head><meta charset="utf-8" />
<title>Qiita</title><meta content="Qiitaは、プログラマのための技術情報共有サービスです。 プログラミングに関するTips、ノウハウ、メモを簡単に記録 &amp; 公開することができます。" name="description" />
<meta content="width=device-width,initial-scale=1,shrink-to-fit=no" name="viewport" />
<meta content="#55c500" name="theme-color" />
<meta content="XWpkTG32-_C4joZoJ_UsmDUi-zaH-hcrjF6ZC_FoFbk" name="google-site-verification" />
<link href="/manifest.json" rel="manifest" />
<link href="/opensearch.xml" rel="search" title="Qiita" type="application/opensearchdescription+xml" />
<meta name="csrf-param" content="authenticity_token" />
....省略
うん。取れた。
webpack
たったこれだけなのでJavaScriptで書いても良かったのですが、今回作るプロダクトではTypeScriptを使いたかった。
というわけでwebpackでトランスパイルします。
まずはwebpackのツールをインストール
yarn add -D webpack webpack-cli ts-loader
webpack.config.jsを作成します。
constpath=require('path');module.exports={mode:'development',entry:'./index.ts',output:{path:path.join(__dirname,"dist"),filename:"index.js"},module:{rules:[{test:/\.ts$/,use:[{loader:'ts-loader'}]}]},resolve:{modules:["node_modules",],extensions:['.ts','.js','json']}};
webpackして実行
$ node ./dist/index.js
webpack:///./node_modules/node-fetch/browser.js?:11
throw new Error('unable to locate global object');
^
Error: unable to locate global object
at getGlobal (webpack:///./node_modules/node-fetch/browser.js?:11:8)
at eval (webpack:///./node_modules/node-fetch/browser.js?:14:14)
at Object../node_modules/node-fetch/browser.js (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:97:1)
at __webpack_require__ (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:20:30)
at eval (webpack:///./src/index.ts?:26:36)
at Object../src/index.ts (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:109:1)
at __webpack_require__ (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:20:30)
at /Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:84:18
at Object.<anonymous> (/Users/taisei/Documents/project/MagicReview/scraping/dist/index.js:87:10)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
あれ?トランスパイルしたらエラーでた…
解決方法
調査した結果、以下の方法で解決しました。
// 省略target:"node",//この行を追加module:{rules:[{test:/\.ts$/,use:[{loader:'ts-loader'}]}]},....
どうやら、webpackでバンドルするときにtargetをnodeに指定しないとbrowserオブジェクトが取得できないようです。
省略しますが、webpack.config.jsを上記のように修正後、webpack→実行すると意図した結果が得られました。
targetをnodeに指定することで、Node.js環境で実行できるようにコンパイルしてくれるようです。
参考→webpackドキュメント
追記
ちなみに、axiosでやってみても同じことが起こりました。
フロントでaxiosを使用していたときには、targetを指定しなくても意図した挙動をしてくれていたのですが、サーバーサイド(Node.js環境)で使用するには必要なようです。
いい勉強になりました。