[PR]
解決済
RSSをブラウザでパースすることはできますか?
こんにちは。XML初心者です。
ライブドアブログのコメントRSSからコメントの内容を取得して
サイドバーに表示するカスタムをしています。
その際、RSSの内容をそのままブラウザでパースできれば
DOMで簡単に取り出せると思うのですが、今の所うまくできていません。
(ダメだった例文です↓)
var comment_rss = (new DOMParser).parseFromString(httpoj.responseText, 'application/xml'); // DOMParser : DOMパーサインスタンス // parseFromString : テキストをパースするメソッド // httpoj.responseText : レスポンステキスト…RSSぺージの内容そのまま // application/xml : MIMEタイプ…RSSのレスポンスヘッダと合わせてある
ブラウザでRDF形式のXMLをパースできないというのは仕様でしょうか?
それともコーディング次第では解決できるのでしょうか。
回答以外にも、
アイディアやヒント、雑感、要望などを書き込んでいただけると助かります。
ご教示のほど、よろしくお願いいたします。
回答(1)
1.

RDFでもパースはできる。
ただし文字化けしたり(して失敗)する場合はあるかも知れない。
註:responseTextもresponseXMLも、読み込まれるファイルの文字コードによって文字化けを生じる場合があります
http://jsgt.org/ajax/ref/charset_test/responsetext...
おかげさまで、なんとか完成まで漕ぎ着けることができました。
書籍やネットではなかなか拾えない情報もあって、大変勉強になりました。
コメント(53)
今まで関わった質問の中で、
なんとなくスッキリしていないものを、これで白黒させたい
…という意図もあります。
http://knowledge.livedoor.com/29267
http://knowledge.livedoor.com/29005
http://knowledge.livedoor.com/28098
http://knowledge.livedoor.com/27516
http://knowledge.livedoor.com/27207
こういうJavaScriptの拡張編みたいのまったくわからない...
僕だったらやっぱり地道に正規表現使ってがんばりますがな。
>>#2
地道ではない正規表現で、なんちゃってパースをしたバージョンは
なんとなく動いているのですが。
http://knowledge.livedoor.com/29005
…パースだなんておこがましいですね。。
ブラウザでできてしまえば、今後の使い道は色々ありそうかなと。
できるよーな気がしますが、ソースを見ないと何も言えません。。。
私もできると思うけど・
>>#4
>>#5
ソースは#3を使ってコメントRSSを取得しています。
on_loaded関数の中身が質問欄のような
var comment_rss = (new DOMParser).parseFromString(oj.responseText, 'application/xml');です。
responseTextにRSSの内容が入っていることは確認しています。
ただ、これではDOMで取り出すことができないので、
(例えばcomment_rss.getElementsByTagName('tilte')など)
うまくパースされていないのか、XMLオブジェクトの作り方が違うのか、
というところを疑ってはいるのですが。
>>#7
どのブラウザでも動かないですか?
大丈夫だとは思いますけど、application/xmlがちょっとだけ気になります。text/xmlが無難かなーと・・・。application/rdf+xmlってのもありますね。
自分でも、試作して試してみましたが、特に問題なくできました。(IE6,FF1.5でテスト)
#8で言われているtext/xml ですが、どちらでもできました。
なんらかの理由でパースに失敗しているのだと思いますが、こちらでは再現できませんでした。
responseXML を使ってみたらどうですか?
MIMEタイプやブラウザなどを色々試していたら遅くなってしまいました。
こちらではやっぱりダメみたいです。
致命的なミスをしているかもしれないので、
チェック用ブログのURLを書いておきます。
http://blog.livedoor.jp/eeu/
Recent Commentsにスクリプトを貼り付けています。
>>#8
>>#9
普段はNN9(≒FF2)でチェックしています。
IE6ではDOMParserが未対応なようでエラーになりますね。
ActiveXObject('Microsoft.XMLDOM')を使うとのことですが、
まだ試してないです。
>>#10
responseXMLでもパースできなかったです。
overrideMimeTypeでMIMEタイプを強制してみたりもしたのですが。
parseFromStringが返すのはDocumentオブジェクトだとすると、まずノードを特定しないとinnerHTMLは使えないとかそういう問題ではないでしょうか?ルートノードなら、
comment_rss.documentElement
みたいに参照できるかと・・・。
コメントアウトされている以下の行、titleがミススペルとかそういう問題って気もします。
//var comment_rss_title = comment_rss.getElementsByTagName('tilte');
IEの時はこんな感じでいいのでしょうか?
var comment_rss =new ActiveXObject('Microsoft.XMLDOM');
comment_rss.loadXML(oj.responseText);
ついでにブログ内のHTMLページや、ページ内に埋め込まれているRDFもパースできるかどうか
今夜試してみます。
しかし、クライアントサイドでそれを行うというのはあまり望ましくないように思いますが...
別サーバのCGIで読ませて、それをiframeで配置するのが一番簡単だと思うのです。
そのいろいろな使い道にもよりますけれど。
>>#15
えー、そうなんですか?(←けっこう無知^^)
開発効率優先での話でしょうか。
実はまだサーバとクライアントでどう振り分けるのがベストか
という所までは把握してないです。
今後の参考にしてみます。
個人的なことを言えば
JSを始めてからまだ日が浅いこともあって、
何ができるのか押さえておきたいという意図もありました。
>>#16
お墨付きをいただいて心強いです。
ただ、昨日は結局手が付きませんでした^^
マルチブラウザで使いやすいように、ちょっと関数にしてみました。
function createXMLObject(XMLStr){
if(window.ActiveXObject){
var xmloj = new ActiveXObject('Microsoft.XMLDOM');
xmloj.loadXML(XMLStr);
return xmloj;
}
else if(window.DOMParser){
return new DOMParser().parseFromString(XMLStr, 'text/xml');
}
else{return null;}
}
パースした結果の方ですが、
・RSS … OK
・ブログ内のHTMLページ … NG
・ページ内に埋め込まれているRDF … NG
でした。
とりあえず、そのまま実行した結果のみ。
NGの原因はまだ探ってないです。
>>#11 responseXMLでもパースできなかったです。
最近忙しくてでられなくですみません。
responseXML の返値はXMLドッキュメントオブジェクトだからパースする必要はないですよ。
>>#18
responseXMLの使い方は一応理解していますが、
書き方がまずかったみたいですみません。
プレーンテキストのパースよりも先に試してダメだったのですが、
原因は結局#8だったので、再チャレンジしたらうまくいきました。
色々すみません。
別な話ですが、
今はコメントアウトにしてる
getElementsByTagName('content:encoded')
がエラーを起こしているようです。<content:encoded>…</content:encoded>という所を抽出しようとして、こうしていますが、
方法が違うのでしょうか。
あとはHTMLページのパースができないので(パーサが文法に対してけっこう厳格みたい、responseXMLも空です)、
その方法を探っているところです。
FFではinnerHTMLにresponseTextを代入した時点でパースするのですが、
IEはその方法もうまくいかないみたいで。
>>#20
余裕が無くて実際に試していないんですけど
>'content:encoded'
がうまくいかないのはおそらく名前空間の関係(認識されてない)かと思います
>HTMLページのパース
勘違いしてたらゴメンですが
HTMLをXMLとして読み込む場合、みなしXMLとして読み込まれます。(つまりXMLに整合していないといけません)HTMLはXMLに整合しない部分がある(単独タグ)のでうまくいかないのではないかと思います。
サポートしている余裕がないので、もし(直接的)知識的な解決でなくてもいいなら
http://www.kawa.net/xp/index-j.html
のJKL.ParseXMLを利用するのが簡単だと思います。
また、同じサイトにJKL.ParseXMLを利用したRSSリーダーがあります。
>>#22
いえ、ちょっとした情報でもありがたいです。
というか、こちらも時間があまり取れなくてスローペースです。
名前空間の認識コロン付きが関係しているのでしょうね。
手持ちの本には情報がないので、検索で引っかからないか調べています。
JKL.ParseXMLぱっと見ですが、getElement~での部分抽出ではなく
全ノードをオブジェクトに置き換えているような感じでしょうか。
確かに名前空間の問題も回避できるし、新たに作らなくていいわけだし、メリットは大きいですね。
そちらとは別にして、
今までの諸問題の解決は勉強も兼ねて引き続き調べていくつもりです。
ちょっとした事でも思うところがあれば、
書きこんでいただけると助かります。
#>>20
余裕ができたので追試してみました。
oSource = xmlhttp.responseXML;
var tags=oSource.getElementsByTagName("content:encoded");
としてみた場合。
IE6では
tags[0].firstChild.nodeValue
のようにして内容が取り出せました。
FF1.5の場合
var tags=oSource.getElementsByTagName("encoded");
tags[0].firstChild.nodeValue
として取り出せました。
両者を共通のやり方で処理する方法として
スタイルシートを定義しXMLに適用するという方法では1つのスタイルシートで同様の結果が得られました。
>>#24
ありがとうございます。
とりあえず動作確認はできました。
取り急ぎご報告まで。
1つのスタイルシートで同様の結果が得られました。スタイルシートでは共通の名前が使えるということでしょうか?
>>#25 スタイルシートでは共通の名前が使えるということでしょうか?
#24の例では、"content:encoded" と"encoded" をブラウザで使い分ける必用がありますが、スタイルシートテンプレート上では"content:encoded"でいけます。そういう意味です。
ちなみにここでいうスタイルシートはXSLTを使うという意味です。
XMLにXSLTする方法はブラウザによって違います(つまりは振り分けが必要になるので一長一短)けどそういうのは関数に押し込めます。
プログラムによる抜き出し出力構成は微に入り細に入りできますけど、煩雑で見通しが悪くなるので簡単な抜き出し定型化処理ならXSLTを使うといいかもしれませんというだけのことです。
こうした方が良いということではありません。
余計なことだったかも知れません。
まあ、そういう方法もあるよという程度のことです。
>>#26
そういうのもあるんですね~
面白そうなので、機会を見つけてちょっと触ってみようと思います。
DOMの方はtry~chatchで分岐させました。
あとは簡単な記事別ソートを付けてひとまず完成させようと思います。
というか、既にこの質問についてはクリアできているのですけど
回答が入らないのをいい事に、だらだら続けています(汗
書いてから思ったんですが、
今のRSSはRDF形式ではないです。
回答と無関係で、ごめんなさい。
先日、大変ご丁寧に回答してくださったにもかかわらず、気がつかずにお蔵入りさせてしまった者です。
ぜひお礼させて頂きたいと思って、書き込みさせていただきました。
お越しくださるのをお待ちしております。
RDFについては、ページ内のRDFだったのですね。
またとんちんかんなコトを書いてしまいました。
ページ内のRDFは、コメントアウトされていますから、DOMではとりだせないですね。
ソース全体から文字列で抽出しないとしょうがないですね。
一旦文字列でRDF部分を切り出せたらパースはできると思います(未試験)
「ブログ内のHTMLページ」については、リンク先をXMLHTTPで読み出すというような意味なら、普通他のサイトの読み出しはできないようになっています(禁止されている)。IEは許可(してやれば)があればできるようですが、FFはそもそもできません。実行時のjavascriptコンソールを見ればそのようなメッセージがでているはずです。
>>#30 コメントアウトされていますから、DOMではとりだせないですね。
DOM で nodeType でコメントがあるから、とりだせはするだろうけど(未試験)「コメントだけを狙った位置づけができない」から結局総当たりになるのでそれなら全体のソースから抽出したほうが楽
>>1
回答ありがとうございます。
おかげさまで、今回はお蔵入りにならずに済みました。
でも折角なのでもう少し続けたいと思います(←結局続ける気満々だったりして)
文字化けについては前に試行錯誤したことがありました。
すごく苦労した割には、ごくシンプルな方法で解消できたのが嬉しいような悲しいような。。
ネタとしてはそれなりに面白そうなのでちょっと記事にしてみました。
http://blog.livedoor.jp/eeu/archives/54871319.html
>またとんちんかんなコトを書いてしまいました。
いえ、質問したときは正直に言って少し混同していました。
ページ内に埋め込まれたRDFの部分は、
id="content"内のコメントノードをチェックすれば抜き出せますが、
確かにしらみつぶしな感じで、スマートではないですね。
それとは別に、
RDFの文字列を#18の方法でパースしてみましたが、そちらはうまくいきませんでした。
well formedなXMLとは解釈されていないようです。
結局、こちらの方はDOMよりも正規表現の方が良さそうな気がして、
それ以上は追求してないのですけど。
ブログ内のHTMLページこれはつまり同一ドメイン内のページですので、XMLHTTP自体は使えています。
それで、responseTextには入るのですが、not well formedなXMLとされるようで、
responseXMLを使うと内容が空になってしまいます。
パース用のライブラリを使えば実現できますが、
元々パーサを備えているブラウザでパースする方法がないかということと、
ライブラリを使ってしまうと、フリーエリアにコピペするだけでは済まなくなって、
万人向けではなくなるかなーということで、
ちょっと変にこだわっているところもあったりします。
>>#34 well formedなXMLとは解釈されていないようです。
そんなばかな!?
と思ったので追試してみました。
rdf に該当RDF部分文字列が入っているとして
IEの場合
var dom = new ActiveXObject("Microsoft.XMLDOM");
dom.loadXML(rdf);
var el=dom.documentElement.selectSingleNode("rdf:Description");
el.getAttribute("dc:title")
のようにしてタイトル部分が取り出せました。
FFの場合
var dom = (new DOMParser()).parseFromString(rdf, "text/xml")
var el =dom.documentElement.getElementsByTagName("Description")[0];
el.getAttribute("dc:title")
のようにしてタイトル部分が取り出せました。
>>#35 not well formedなXMLとされるようで、
既に指摘したように(#22)一般にHTMLファイルをXMLとして読み込むことはできません。
「同一ドメインHTMLページをDOMとしてアクセスできる形で読み込むこと」は、おそらくいくつかの方法でできるかと思いますが、#15で指摘されているように1つのページから複数のページにアクセスする(結局まるまる読むことになる)のはやり過ぎじゃないかと思います、それだけのコストに対する程のニーズがあるのですか?
>>#20 FFではinnerHTMLにresponseTextを代入した時点でパースするのですが、IEはその方法もうまくいかないみたいで。
こちらで試してみたところでは、IEも(FFも)うまくいきました。
>>#36
同じことの二の舞でしたorz
面目ないです
IEの方はselectSingleNodeを使っているんですね。
getElementsByTagNameでもできそうな気がしますが、
これはどういうときに使い分けるのでしょうか?
>>#37
ニーズはこちら↓ですが、
http://knowledge.livedoor.com/27516#comment2
コストには見合ってないですね。
サーバの役割りを、クライアントで無理やり実現しようとしているようで、
ちょっと心地の悪さは感じています。
敢えてやっているのは、
技術的な興味の方が強いからでしょうか。
>>#39 これはどういうときに使い分けるのでしょうか?
言われるように getElementsByTagName でできると思います。
そうすることのメリットはコードの共用部分が増えるということですね。
この場合、同じgetElementsByTagName()を使うにはタグ名を振り分け(変数に押し込める)必要がありますが、このように結局振り分けが必要なら特化した関数やメソッドを使っても私は特にイヤな気持ちにはなりません。
また、特化されたメソッドは効率化されている可能性があります。(逆に単なる糖衣構文の場合効率が悪い)
selectSingleNode() について(推測を)言えば
getElementsByTagName() では全てのタグを検索しノードリストを作るという処理が必要になりますが
selectSingleNode() ではひとつ目のタグが見つかった時点で処理を止めることができます。
(おそらく内部的にgetElementsByTagName()を呼び出すような処理はしていないと期待する)
>>#38
ありがとうございます。できると分かれば心強いです。
こちらで試したときはどこか方法がまずかったんだと思いますが、
そのときは大して追求もしていなかったので、
コメントRSSの方が完成してから着手してみます。
>>#41
どうせ分岐させるなら、処理の速い可能性がある方を取る
ということですね。
勉強になりました^^
閉じる前に一応ソースを公開しておきます。
もう少し直したいところはありますが、
とりあえず主要ブラウザでは動くと思います。
全てつなげて、フリーエリアに貼り付け、
改行設定を変換しないに設定して保存してください。
ソースの詳細については割愛します。
<div id="recentcomments"></div> <script type="text/javascript"> <!--
function createHttpRequest() {
if (window.ActiveXObject) {
try {return new ActiveXObject('Msxml2.XMLHTTP');}
catch (e) {
try{return new ActiveXObject('Microsoft.XMLHTTP');}
catch(e2){return null;}
}
} else if (window.XMLHttpRequest) {return new XMLHttpRequest();}
else {return null;}
}
function requestFile(reqoj) {
var httpoj = createHttpRequest();
httpoj.open(reqoj.method, reqoj.fileName, reqoj.async);
httpoj.onreadystatechange = function() {
if (httpoj.readyState == 4 && httpoj.status == 200) {
reqoj.onLoaded(httpoj);
}
}
httpoj.send(reqoj.data);
}
function createRequestValue(data, method, fileName, async, onLoad) {
this.data = data;
this.method = method;
this.fileName = fileName;
this.async = async;
this.onLoaded = onLoad;
}
function comment_item(el) {
el.getElementsByTagName('title')[0].firstChild.nodeValue.match(/「(.*)」へのコメント/);
this.title = RegExp.$1;
this.link = el.getElementsByTagName('link')[0].firstChild.nodeValue;
this.description = el.getElementsByTagName('description')[0].firstChild.nodeValue;
this.pubDate = el.getElementsByTagName('pubDate')[0].firstChild.nodeValue;
this.guid = el.getElementsByTagName('guid')[0].firstChild.nodeValue;
this.author = el.getElementsByTagName('author')[0].firstChild.nodeValue;
var txt;
try {txt = el.getElementsByTagName('content:encoded')[0].firstChild.nodeValue;}
catch (e) {
try {txt = el.getElementsByTagName('encoded')[0].firstChild.nodeValue;}
catch (e) {txt = '';}
}
this.content = txt.replace(/\s*/g, '');
}
function toggle_comment(i) {
this.toggleButton[i].innerHTML = this.toggleButton[i].innerHTML == this.openButton ? this.closeButton : this.openButton;
this.commentMore[i].style.display = this.commentMore[i].style.display == 'none' ? 'inline' : 'none';
this.commentConti[i].style.display = this.commentConti[i].style.display == 'none' ? 'inline' : 'none';
}
function createElementWithClass(tagname, classname) {
var el = document.createElement(tagname);
el.className = classname;
return el;
}
function createElementWithTextNode(tagname, classname, textnode) {
var el = createElementWithClass(tagname, classname);
var tn = document.createTextNode(textnode);
el.appendChild(tn);
return el;
}
function comments_rss_on_loaded(oj) {
var comment = new Array();
this.toggleButton = new Array();
this.commentMore = new Array();
this.commentConti = new Array();
var el_comment = document.getElementById(this.targetId);
var comment_rss_item = oj.responseXML.getElementsByTagName('item');
var fl = document.createDocumentFragment();
var m = Math.min(comment_rss_item.length, this.maxNum);
for (var i = 0; i < m; i++) {
var com = new comment_item(comment_rss_item[i]);
if (comment[com.title] == null || comment[com.title] == 'undefined')
comment[com.title] = new Array();
comment[com.title].push(com);
}
for (var a in comment) {
var el_title = createElementWithClass('div', 'sidebody comment');
var el_link = createElementWithTextNode('a', 'comment_link', a);
el_link.setAttribute('href', comment[a][0].link);
el_title.appendChild(el_link);
for (var c in comment[a]) {
var el_author = createElementWithTextNode('div', c == comment[a].length - 1 ? 'leaf_last leaf_author' : 'leaf leaf_author', comment[a][c].author);
el_title.appendChild(el_author);
var el_wrapper = createElementWithClass('div', 'comment_wrapper');
var el_comain, el_comore, el_cocont;
el_cocont = createElementWithTextNode('span', 'comment_continue', '...');
el_comore = createElementWithTextNode('span', 'comment_more', comment[a][c].content.substring(this.commentLength, comment[a][c].content.length));
if (comment[a][c].content.length > this.commentLength) {
var el_button = createElementWithClass('a', 'comment_button');
el_button.innerHTML = this.openButton;
el_button.setAttribute('href', 'javascript:comments_rss_request.toggle(' + this.commentMore.length + ')');
el_wrapper.appendChild(el_button);
el_comain = createElementWithTextNode('span', 'comment_main', comment[a][c].content.substr(0, this.commentLength));
el_wrapper.appendChild(el_comain);
el_cocont.style.display = 'inline';
el_wrapper.appendChild(el_cocont);
el_comore.style.display = 'none';
el_wrapper.appendChild(el_comore);
} else {
var el_button = createElementWithTextNode('span', 'comment_button');
el_button.innerHTML = this.notButton;
el_wrapper.appendChild(el_button);
el_comain = createElementWithTextNode('span', 'comment_main', comment[a][c].content);
el_wrapper.appendChild(el_comain);
}
this.toggleButton.push(el_button);
this.commentMore.push(el_comore);
this.commentConti.push(el_cocont);
el_author.appendChild(el_wrapper);
}
fl.appendChild(el_title);
}
el_comment.appendChild(fl);
}
var comments_rss_request = new createRequestValue('', 'GET', '', true, comments_rss_on_loaded);
comments_rss_request.toggle = toggle_comment;
comments_rss_request.fileName = 'ブログのURL/recent_comments.xml';
comments_rss_request.targetId = 'recentcomments';
comments_rss_request.maxNum = 10;
comments_rss_request.commentLength = 15;
comments_rss_request.openButton = '\u25BC';
comments_rss_request.closeButton = '\u25B2';
comments_rss_request.notButton = '';
requestFile(comments_rss_request);
//-->
</script>
DOMにこだわったら、不幸なソースになってしまいました。
取り出すのは楽だけど、書き込む手順が多すぎますね。
innerHTMLにした方が良いような気もしました。
BLUEPIXYさん、オニキスさん、NIPOさん、どうもありがとうございました。
ナレッジでこういう質問ができるのは皆さんがいての事ですね^^
完成おめでとうございます☆ミ
つっけんどんでちぐはぐな回答(ソースを見てないし)にベストどうもです。
こういう質問の場合に相手がどの程度知っているのかとかそういうのがわからないと答えにくいということはありますね。
そういう意味で、問題点を絞って、問題点を再現できる簡単なケース(絞り込んだソース)の提示があれば、追試もしやすいし回答もつきやすいのではないかと思います。
*
トラックバックつけたのに、反映がされないです・(自サイトへの誘導になるということなら、トラックバックって?)
そういう意味で、問題点を絞って、(…以下略)肝に命じておきます(汗
トラックバックはスパムが多かったので中断していますが、
そのまま機能を無くしたいかのような勢いで放置されています。
http://knowledge.livedoor.com/19183

