Firefox拡張入門第4回(某占いサイトをスクレイピング)
Firefox拡張はスクレイピングの最終兵器です。
今日は、某占いサイトをスクレイピングして、自分の星座のお告げを抜き出します。
某占いサイト
スイーツ(笑)テーブルレイアウトです。
先にFirebugで、抜き出したいところのXPathを調べておきます。
個人的にCSSセレクタのほうが好きなので、XPathとCSSセレクタの対応表をあげておきます。
- John Resig - XPath and CSS Selectors
- Latest topics > CSS3セレクタとXPathでの表現の対応表 - outsider reflex
- Latest topics > getElementsByなんちゃら の代わりにXPathを使う - outsider reflex
次に、拙作のMatchfox拡張で骨組みを作ります。名前はFortuneにします。
XPathを使ったスクレイピング
新しいタブに占いサイトを開いて、onloadイベントでスクレイピングが走ります。
chrome/content/sidebar/00-foobar0.jsに書いてください。
function getMainWindow() { return getTopWin(); } function $x(aDocument, aXPathString) { var nodes = aDocument.evaluate(aXPathString, aDocument, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); var arr = []; for (var i = 0, len = nodes.snapshotLength; i < len; i++) { arr.push(nodes.snapshotItem(i)); } return arr; } function scrapeFortune(aDocument) { //contains(@alt, '座'). XPath 1.0 には end-with()がないのでcontainsでお茶を濁す。 var constes = $x(aDocument, "//img[contains(@alt, '\u5EA7')]").map(function(x) {return x.alt;}); var texts = $x(aDocument, "//td[contains(@class,'text')]") .map(function(x) {return x.innerHTML.replace(/<br>/g, "");}); var luckys = $x(aDocument, "//td[contains(@class,'lucky')]") .map(function(x) {return x.innerHTML;}); var data = {}; data[constes[0]] = {rank: 1, text: texts[0], advice: luckys[0], lucky: luckys[1]}; data[constes[11]] = {rank: 12, text: texts[11], advice: luckys[12], lucky: luckys[13]}; for (var i = 1; i < 11; i++) { data[constes[i]] = {rank: i + 1, text: texts[i], lucky: luckys[i + 1]}; } return data; } function sidebarFoobar0() { const url = "http://www.fujitv.co.jp/meza/uranai/index.html"; var mw = getMainWindow(); var newTab = mw.gBrowser.getBrowserForTab(mw.gBrowser.addTab(url)); newTab.addEventListener("load", function(e) { Application.console.log(scrapeFortune(newTab.contentDocument)); }, true); }
scrapeFortuneの結果はこのようなオブジェクトです。
{ "おうし座": {"rank": 1, "text": "全てが順調に進んでHAPPY。悩んでいた課題に打開策を発見。新たな仲間も増え団結できそう。", "advice": "お気に入りの服を着る", "lucky": "デジタルカメラ"}, "しし座": {"rank": 12, "text": "自己中心の言動に冷たい視線。努力してきたことまで裏目に。理想ばかり追わず現実を見て。", "advice": "レジで一番長い列に並ぶ", "lucky": "アクセサリーショップ"}, "おとめ座": {"rank": 2, "text": "ロマンスがすぐそこに!初対面の人には常に笑顔。", "lucky": "アジアン小物"}, "うお座": {"rank": 3, "text": "実力を発揮するチャンス。攻めの姿勢で一歩成功に。", "lucky": "細長いピアス"}, (以下略) }
星座の設定画面に挑戦
extensions.fortune.consteに"いて座"などと設定を保存することにします。
以下の変更を加えます。
chrome/content/config.xul (設定画面のXULです)
<prefpane id="prefpane0" image="chrome://fortune/skin/images/transparent.png" label="&Fortune.config.pane0;" flex="1"> <preferences> <preference id="extensions.fortune.conste" name="extensions.fortune.conste" type="string" /> </preferences> <vbox> <groupbox orient="vertical"> <caption label="&Fortune.config.conste;"/> <radiogroup preference="extensions.fortune.conste"> <radio value="おひつじ座" label="&Fortune.config.radio.aries;" /> <radio value="おうし座" label="&Fortune.config.radio.taurus;" /> <radio value="ふたご座" label="&Fortune.config.radio.gemini;" /> <radio value="かに座" label="&Fortune.config.radio.cancer;" /> <radio value="しし座" label="&Fortune.config.radio.leo;" /> <radio value="おとめ座" label="&Fortune.config.radio.virgo;" /> <radio value="てんびん座" label="&Fortune.config.radio.libra;" /> <radio value="さそり座" label="&Fortune.config.radio.scorpio;" /> <radio value="いて座" label="&Fortune.config.radio.sagittarius;" /> <radio value="やぎ座" label="&Fortune.config.radio.capricorn;" /> <radio value="みずがめ座" label="&Fortune.config.radio.aquarius;" /> <radio value="うお座" label="&Fortune.config.radio.pisces;" /> </radiogroup> </groupbox> </vbox> </prefpane>
chrome/locale/ja/config.dtd (設定画面の日本語ラベルです)
<!ENTITY Fortune.config.conste "あなたの星座"> <!ENTITY Fortune.config.radio.aries "おひつじ座"> <!ENTITY Fortune.config.radio.taurus "おうし座"> <!ENTITY Fortune.config.radio.gemini "ふたご座"> <!ENTITY Fortune.config.radio.cancer "かに座"> <!ENTITY Fortune.config.radio.leo "しし座"> <!ENTITY Fortune.config.radio.virgo "おとめ座"> <!ENTITY Fortune.config.radio.libra "てんびん座"> <!ENTITY Fortune.config.radio.scorpio "さそり座"> <!ENTITY Fortune.config.radio.sagittarius "いて座"> <!ENTITY Fortune.config.radio.capricorn "やぎ座"> <!ENTITY Fortune.config.radio.aquarius "みずがめ座"> <!ENTITY Fortune.config.radio.pisces "うお座">
defaults/preferences/prefs.js (デフォルト設定です。私のいて座です)
pref("extensions.fortune.conste", "いて座");
chrome/content/sidebar/00-foobar0.js (設定を読むコードです)
function getMyConste() { var prefs = new Fortune.Prefs("extensions.fortune."); return prefs.get("conste"); }
最終的に00-foobar0.jsはこのようになります。
show関数がスクレイピングしたデータから自分の星座をとりだして表示します。
const EXPORT = ["sidebarFoobar0"]; function getMainWindow() { return getTopWin(); } function $x(aDocument, aXPathString) { var nodes = aDocument.evaluate(aXPathString, aDocument, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); var arr = []; for (var i = 0, len = nodes.snapshotLength; i < len; i++) { arr.push(nodes.snapshotItem(i)); } return arr; } function scrapeFortune(aDocument) { //contains(@alt, '座'). XPath 1.0 には end-with()がないのでcontainsでお茶を濁す。 var constes = $x(aDocument, "//img[contains(@alt, '\u5EA7')]").map(function(x) {return x.alt;}); var texts = $x(aDocument, "//td[contains(@class,'text')]") .map(function(x) {return x.innerHTML.replace(/<br>/g, "");}); var luckys = $x(aDocument, "//td[contains(@class,'lucky')]") .map(function(x) {return x.innerHTML;}); var data = {}; data[constes[0]] = {rank: 1, text: texts[0], advice: luckys[0], lucky: luckys[1]}; data[constes[11]] = {rank: 12, text: texts[11], advice: luckys[12], lucky: luckys[13]}; for (var i = 1; i < 11; i++) { data[constes[i]] = {rank: i + 1, text: texts[i], lucky: luckys[i + 1]}; } return data; } function getMyConste() { var prefs = new Fortune.Prefs("extensions.fortune."); return prefs.get("conste"); } function show(aData, aMyConste) { alert([aMyConste, "rank:" + aData[aMyConste].rank, "text:" + aData[aMyConste].text, "lucky:" + aData[aMyConste].lucky, aData[aMyConste].advice ? "advice:" + aData[aMyConste].advice : ""].join("\n")); } function sidebarFoobar0() { const url = "http://www.fujitv.co.jp/meza/uranai/index.html"; var mw = getMainWindow(); var newTab = mw.gBrowser.getBrowserForTab(mw.gBrowser.addTab(url)); newTab.addEventListener("load", function(e) { show(scrapeFortune(newTab.contentDocument), getMyConste()); }, true); }
ここに置いときますね。 http://eva-lu-ator.net/~gemma/geocities/matchfox/fortune.xpi
GOODLUCK!!BABY!!