Node.jsでGETリクエストを投げる時に最低限必要な処理
つらつら書いてみたら長い長い。
誰もが引っかかる罠、
- HTTPリクエストに対するレスポンスがなければerrorオブジェクトに中身が入る
- errorとなった場合にはresponseオブジェクトはundefined
- UTF-8以外のコンテンツは明示的に変換しないと文字化けする
あたりを考慮しないといけない。
特に文字コード辺りは裏でよしなにして欲しいところなのですが……
必要なライブラリ
Request
$ npm install --save request
お手軽にGETリクエストを投げるのに必要
node-iconv
bnoordhuis/node-iconv · GitHub
$ npm install --save iconv
ICU Character Set Detection for Node.js
mooz/node-icu-charset-detector · GitHub
$ brew install icu4c && brew link icu4c
$ npm install --save node-icu-charset-detector
レスポンスヘッダーや<meta>
タグで文字コードを指定してない場合に、本文から自動判定するために必要(最終手段なので無くてもいいかも)
逆に、最初からこれで全部判定するという手もありますが、意外と誤判定が多いんですよね……
ソース
var request = require('request');
var Iconv = require('iconv').Iconv;
var CharsetMatch = require("node-icu-charset-detector").CharsetMatch;
var inspect = require('util').inspect;
var requestUrl = 'http://hentai-kun.hatenablog.jp/';
var options = {
followRedirect: true, // 3xx系でリダイレクトを行う(default: true)
maxRedirects: 5, // 最大リダイレクト回数(default: 10)
timeout: 10000, // タイムアウトさせるまでのミリ秒(default: タイムアウトしない)
encoding: 'binary' // 文字コードの指定(default: UTF-8)、EUC-JP/SHIFT_JISなサイトを開く可能性がある場合は'binary'にする
};
request.get(requestUrl, options, function (error, response, body) {
if (error) {
//
// 基本的にサーバー側からレスポンスが返ってこない場合
// なので、responseはundefined
console.log(inspect(error));
} else if (response.statusCode != 200) {
//
// 基本的にサーバー側からレスポンスが返ってきた場合
// bodyがあるかどうかはサーバー次第
console.log(inspect(error), inspect(response));
} else {
//
// 正常終了
// bodyがある
// 文字コードを特定する
var charset = findCharset(response, body);
console.log('charset:', charset);
// 文字コードをUTF-8に変換する
var html = convertUTF8(charset, body);
console.log(inspect(response), html);
}
});
// 文字コードを特定する
function findCharset(response, bin) {
var charset;
// レスポンスヘッダーにあればそれを使う
var matcher = (response.headers['content-type']||'').match(/charset=([^;\s]+)/);
if (matcher && matcher[1]) {
charset = matcher[1];
} else {
// 無ければHTMLのmetaタグから探す
matcher = bin.match(/<meta\b[^>]+charset=["']?([\w\-_]+)/i)
if (matcher && matcher[1]) {
charset = matcher[1];
} else {
// 無ければコンテンツから推測
charset = new CharsetMatch(new Buffer(bin, 'binary')).getName();
}
}
return charset;
}
// 文字コードをUTF-8に変換する
function convertUTF8 (charset, bin) {
var iconv, text;
var buffer = new Buffer(bin, 'binary');
try {
iconv = new Iconv(charset, 'UTF-8//TRANSLIT//IGNORE');
text = iconv.convert(buffer).toString();
} catch (error) {
console.log(inspect(error));
// 失敗なら、仕方ないのでUTF-8とみなす
text = buffer.toString('utf8');
}
return text;
}
エラーコード
error.code
の値としてあるもの。
他にもあるかもしれないけど、出たことがない。
補足:followAllRedirectsとは何か
followRedirect
と似たオプションでfollowAllRedirects
というのがあって、オプションの説明に
followAllRedirects - follow non-GET HTTP 3xx responses as redirects (default: false)
としか書いてなくて何をするものなのかソースを追ってみました。
request.js
if (self.followAllRedirects) {
redirectTo = location
} else if (self.followRedirect) {
switch (self.method) {
case 'PATCH':
case 'PUT':
case 'POST':
case 'DELETE':
// Do not follow redirects
break
default:
redirectTo = location
break
}
}
どうやら、リクエストメソッドに関係なくリダイレクトさせる、ということみたいです。