タイトル通りです。
cheerio という node のモジュールを使って XML を解析して必要な情報をとりだすときのメモです。
インストールは npm でサクッと
npm install cheerio
XML モードを有効にする
ここで 20 分ほどはまりました。
load
メソッドを実行するときにオプションで有効にします。
$ = cheerio(xml, { xmlMode: true });
有効にすることでlink
タグなどがちゃんと読み込まれるようになります。
セレクタのエスケープ
タグや ID,Class に:
(コロン)などの特殊文字が含まれている場合は直前に\(バックスラッシュ)
を 2 つでエスケープします。
$("dc\\:creator").text();
おそらく jQuery と同じだと思われます。
参考:
サンプル
ニコニコ動画のマイリストページの RSS から情報を取り出すサンプルです。
json 形式で出力します。
使い方
node mylist_rss.js "id" "user_session"
user_session は任意です。
非公開の自分のマイリストを取得するときに使います。
例:
node mylist_rss.js 38085032
ソースコード
MIT ライセンスです。
"use strict" | |
const http = require("http"); | |
const fs = require("fs"); | |
const cheerio = require("cheerio"); | |
// コマンドの引数を受け取って処理 | |
if (process.argv.length < 3) { | |
console.log("マイリストIDを指定してください"); | |
} else { | |
var id = process.argv[2], | |
user_session = (process.argv[3])?process.argv[3]:""; | |
getMylistFromRss(id, user_session).then(function (result) { | |
var fileName = "mylist_" + id + ".json"; | |
console.log("タイトル: "+result.title); | |
console.log("アイテム数: "+result.item.length); | |
fs.writeFile( | |
fileName, | |
JSON.stringify(result, null, " ") | |
); | |
console.log("\"" + fileName + "\"に出力しました"); | |
}).catch(function (error) { | |
console.log(error); | |
}); | |
} | |
/** | |
* HTTPでヘッダーも含めて取得してPromiseで返す | |
* @param {Object} options - http.requestで使うオプション | |
* @return {Promise} | |
*/ | |
function getByHttp (options) { | |
return new Promise(function (resolve, reject) { | |
var req = http.request(options, function (res) { | |
res.setEncoding("utf8"); | |
var temp = ""; | |
if (res.statusCode < 200 || res.statusCode > 299) { | |
reject(new Error(res.statusCode)); | |
} | |
res.on("data", function (chunk) { | |
temp += chunk; | |
}); | |
res.on("end", function () { | |
resolve({ | |
headers: res.headers, | |
body: temp | |
}); | |
}); | |
}); | |
req.on("error", function (error) { | |
reject(error); | |
}); | |
req.end(); | |
}); | |
} | |
/** | |
* マイリストを取得してPromiseで返す | |
* @param {String} id - マイリストID | |
* @return {Promise} | |
*/ | |
function getMylistFromRss (id, user_session) { | |
return new Promise(function (resolve, reject) { | |
getByHttp({ | |
hostname: "www.nicovideo.jp", | |
path: "/mylist/"+id+"?rss=2.0", | |
port: 80, | |
method: "GET", | |
headers: { | |
"Cookie": "user_session="+user_session+";" | |
} | |
}).then(function (result) { | |
resolve(getMylistData(result.body)); | |
}).catch(function (error) { | |
reject(error); | |
}); | |
}); | |
} | |
/** | |
* RSSからマイリストデータを生成 | |
* @param {String} xml - RSSの文字列 | |
* @return {Object} | |
*/ | |
function getMylistData (xml) { | |
var $ = cheerio.load(xml, { xmlMode: true}), | |
ret = { | |
id: "", | |
title: "", | |
description: "", | |
creator: "", | |
item: [] | |
}; | |
// マイリストタイトル | |
var title = $("channel > title").text() | |
.match(/^.+?\s(.+)\u2010[^\u2010]+?$/); | |
if (!title || title.length-1 > 1) { | |
ret.title = "No title"; | |
} else { | |
ret.title = title[1]; | |
} | |
// マイリスト説明 | |
ret.description = $("channel > description").text(); | |
// マイリスト所有者 | |
ret.creator = $("channel > dc\\:creator").text(); | |
// 非公開ならここで返す | |
if (!ret.creator) { | |
ret.title = "このマイリストは非公開です"; | |
return ret; | |
} | |
// マイリストID | |
var id = $("channel > link").text().split("/"); | |
if (!id) return ret; | |
ret.id = id.slice(-1)[0]; | |
// マイリストアイテム | |
$("channel > item").each(function (i, elem) { | |
var item_data = { | |
title: "", | |
video_id: "", | |
thumbnail_url: "", | |
length_seconds: 0 | |
}; | |
var description = $(this).children("description").text(); | |
// タイトル | |
item_data.title = $(this).children("title").text(); | |
// ID | |
item_data.video_id = | |
$(this).children("link").text().split("/")[4]; | |
// サムネイルURL | |
item_data.thumbnail_url = | |
description.match(/http:\/\/.+\.smilevideo\.jp\/smile\?i=[0-9]+/)[0]; | |
// 再生時間(秒) | |
item_data.length_seconds = 0; | |
var length_second = | |
description.match(/<strong class="nico-info-length">([0-9|:]+?)<\/strong>/); | |
if (length_second && length_second[1]) { | |
length_second = length_second[1].split(":"); | |
length_second = (length_second[0]-0)*60+(length_second[1]-0); | |
item_data.length_seconds = length_second; | |
} | |
// 配列に追加 | |
ret.item.push(item_data); | |
}); | |
return ret; | |
} |
おわりに
訳あってxml2json
からcheerio
に切り替えたけどなかなか便利ですね。
jQuery っぽく使えるのがすごくいい!