あけましておめでとうございました。本年もよろしくお願いいたします。
さて去年後半、xmlを読み込んでは切ったり貼ったりする仕事が相次いでました。
大変勉強になったので、サンプルや注意点、参考URLなどをまとめておきます。
本当にメモ書きのようなものなので、JS部分のみ抜粋してます。詳しい書き方はサンプルのソース見てくださると助かります。
Google Feed APIが廃止されたため、このページで公開されている方法の一部は使用できません。現在はPHPを使う必要がありますのでご注意ください。
(2018.08.08追記)
Contents
外部サーバのXMLファイルを読み込む
「ア○ーバブログの更新情報をサイトに載せたい!」なんて時に使えます。
Google AJAX Feed API を使う
シンプルにデータ取得→HTMLに表示はこんな感じでできます。
<script src="https://www.google.com/jsapi" type="text/javascript"></script> <script type="text/javascript"> var xmlUrl = "http://arcaxxx.blog21.fc2.com/?xml"; //feedのURL var setNum = 5; //表示件数 var setID = "feed"; //表示させる箇所のID google.load("feeds", "1"); function initialize() { var html = ''; var feed = new google.feeds.Feed(xmlUrl); feed.setNumEntries(setNum); feed.load(function(result) { if (!result.error){ var container = document.getElementById(setID); for (var i = 0; i < result.feed.entries.length; i++) { var entry = result.feed.entries[i]; var title = entry.title; //記事タイトル取得 var link = entry.link; //記事のリンクを取得 //日付を取得し年月日を整形 var publishedDate = entry.publishedDate; var pubDD = new Date(publishedDate); yy = pubDD.getYear();if (yy < 2000) { yy += 1900; } mm = pubDD.getMonth() + 1;dd = pubDD.getDate(); var pubDate = yy +'年'+ mm +'月'+ dd +'日'; //カテゴリ要素がある場合は取得 for (var j = 0; j < entry.categories.length; j++) { var categorie = entry.categories[j]; } //表示する部分を整形 html += '<li><a href="' + link + '">' + title +'</a> - ' + categorie + '(' + pubDate + ')</li>'; } container.innerHTML = html; } }); } google.setOnLoadCallback(initialize); </script>
ちょっと凝ったのはこんな感じ
<script type="text/JavaScript" src="js/jquery.js"></script> <script src="https://www.google.com/jsapi" type="text/javascript"></script> <script type="text/javascript"> var xmlUrl = "http://mei331.tumblr.com/rss"; //feedのURL var setNum = 3; //表示件数 var setID = "feed"; //表示させる箇所のID google.load("feeds", "1"); function initialize() { DD = new Date();HH = DD.getHours();MM = DD.getMinutes();SS = DD.getSeconds(); xmlUrl = xmlUrl+"?"+HH+MM+SS; //キャッシュ対策のクエリを付与 var feed = new google.feeds.Feed(xmlUrl); feed.setNumEntries(setNum); feed.setResultFormat(google.feeds.Feed.XML_FORMAT); feed.load(function(result) { if (!result.error){ var container = document.getElementById(setID); var xmlDoc = result.xmlDocument; var items = xmlDoc.getElementsByTagName("item"); var html = ''; $(xmlDoc).find('item').each(function(){ title = $(this).find('title').text(); //タイトル取得 description = $(this).find('description')[0].firstChild.nodeValue; //コンテンツ取得 link = $(this).find('link').text(); //link取得 guid = $(this).find('guid').text(); //guid取得 //日付を取得し年月日を整形 publishedDate = $(this).find("pubDate").text(); var pubDD = new Date(publishedDate); yy = pubDD.getYear();if (yy < 2000) { yy += 1900; } mm = pubDD.getMonth() + 1;dd = pubDD.getDate(); var pubDate = yy +'年'+ mm +'月'+ dd +'日'; //表示する部分を整形 html += '<li><h3><a href="' + link + '">' + title + '</a></h3>'; html += '<div class="contents">' + description + '</div>'; html += '<p>'+ guid + '(' + pubDate + ')</p></li>'; }); container.innerHTML = html; } }); } google.setOnLoadCallback(initialize); </script>
補足)
Tumblrのコンテンツ(descriptionタグの中身)は、firstChild.nodeValue で取得できます。
ただ、タグ内にある情報を全て表示してしまうので、CSS等での調整が必要だと思います。
/** 2012.9.4追記ここから **/
サンプル2がIE8以下で動いていないという指摘を受けまして、色々調べてみました。
上記コードの20〜37行目を以下のように修正するとIE8以下でも問題なく動作するようです。
for (var i = 0; i < items.length; i++) { var title = items[i].getElementsByTagName("title")[0].firstChild.nodeValue; var link = items[i].getElementsByTagName("link")[0].firstChild.nodeValue; var guid = items[i].getElementsByTagName("guid")[0].firstChild.nodeValue; var description = items[i].getElementsByTagName("description")[0].firstChild.nodeValue; //日付を取得し年月日を整形 publishedDate = items[i].getElementsByTagName("pubDate")[0].firstChild.nodeValue; var pubDD = new Date(publishedDate); yy = pubDD.getYear();if (yy < 2000) { yy += 1900; } mm = pubDD.getMonth() + 1;dd = pubDD.getDate(); var pubDate = yy +'年'+ mm +'月'+ dd +'日'; //表示する部分を整形 html += '<li><h3><a href="' + link + '">' + title + '</a></h3>'; html += '<div class="contents">' + description + '</div>'; html += '<p>'+ guid + '(' + pubDate + ')</p></li>'; }
IE8以下はどうやら<![CDATA[~]]>で囲まれていないHTMLタグの書かれた要素の中身を取得する場合、$(xmlDoc).find(‘item’).eachを使って参照しようとすると上手く参照出来ないようです。
古い記事ですがこちらにあるように、thisを使った参照の仕方だと上手くいかないみたいです。
最初のサンプルではdescription以外はきちんと取得出来ていたので、RSS側のdescriptionが<![CDATA[~]]>で囲まれていないとノードの取得が上手くできない、もしくはタグとして認識してしまうのかもしれないな、と。憶測ですが。
/** 2012.9.4追記ここまで **/
自分で書くのが面倒!よくわからない!場合はプラグインで。
これ系のプラグインは色々有りますが、色々試した中では「zRssfeed」がカスタマイズしやすくて便利でした。
プラグインならzRssfeedがオススメ!
参考)jQueryでサイト内に簡単にRSSフィードを組み込めるプラグイン「zRSSFeed」
PHPを使う
google Feed APIを使わないで、外部サーバのXMLファイルを読みに行く為のPHPを設置する方法もあります。
feed2jsというのがあるんですが、これがサクッとできて一番お手軽ですね。
php側で表示数や日付などを設定できます。表示するHTMLには吐き出されたscriptタグを書くだけ。
ただCSSの設定がちょっと面倒なうえ、php側の設定画面が英語なのでちょっと難しいです。
feed2js
同一サーバのXMLファイルを読み込む
jQueryを使う
jqueryには便利なajax関数があるので、そちらを使って読み込んだりします。
<script type="text/JavaScript" src="js/jquery.js"></script> <script type="text/javascript"> $(function() { var setURL = '../feed'; //feedのURL var setNUM = 5; //表示件数 var setID = 'feed'; //表示させる箇所のID xmlLoad(setURL,setID,setNUM); }); function xmlLoad(_xmlUrl,_id,_num){ DD = new Date();HH = DD.getHours();MM = DD.getMinutes();SS = DD.getSeconds(); var xmlUrl = _xmlUrl+"?"+HH+MM+SS; //キャッシュ対策のクエリを付与 var main = this; $.ajax({ url: xmlUrl, type: 'GET', dataType: 'xml', timeout: 10000, error: function(){ _msg = 'error'; xmlOpen(_msg,_id,_num); }, success: function(_xml){ main.xml = _xml; _msg = 'load'; xmlOpen(_msg,_id,_num); } }); return; } var xmlOpen = function(_msg,_id,_num){ var main = this; var html = ''; var ID = _id; var Num = _num; if(_msg == 'load'){ xml = main.xml; var channelData = $(xml).find('channel')[0]; $(channelData).find('item').each(function(i){ if(i < Num){ postTitle = $(this).find('title').text();//記事タイトル取得 postLink = $(this).find('link').text();//記事リンク取得 //日付を取得し年月日を整形 publishedDate = $(this).find('pubDate').text(); var pubDD = new Date(publishedDate); yy = pubDD.getYear();if (yy < 2000) { yy += 1900; } mm = pubDD.getMonth() + 1;dd = pubDD.getDate(); var postDate = yy +'年'+ mm +'月'+ dd +'日'; //カテゴリが1記事に複数あるのである分だけ取得 var postCategory = ''; $(this).find('category').each(function(i){ category = $(this).text(); if(i <= 0){ postCategory += category; }else{ postCategory += ',' + category; } }); //表示する部分を整形 html += '<li><a href="' + postLink + '">' + postTitle +'</a> - ' + postCategory + '(' + postDate + ')</li>'; } }); }else{ html += '<li>通信エラー</li>'; } $("#"+ID).html(html); } </script>
ちょっと複雑なxmlを読み込む場合はこんな感じ
<script type="text/JavaScript" src="js/jquery.js"></script> <script type="text/javascript"> $(function() { var setURL = '../feed'; //feedのURL var setNUM = 5; //表示件数 var setID = 'feed'; //表示させる箇所のID xmlLoad(setURL,setID,setNUM); }); function xmlLoad(_xmlUrl,_id,_num){ DD = new Date();HH = DD.getHours();MM = DD.getMinutes();SS = DD.getSeconds(); var xmlUrl = _xmlUrl+"?"+HH+MM+SS; //キャッシュ対策のクエリを付与 var main = this; $.ajax({ url: xmlUrl, type: 'GET', dataType: 'xml', timeout: 10000, error: function(){ _msg = 'error'; xmlOpen(_msg,_id,_num); }, success: function(_xml){ main.xml = _xml; _msg = 'load'; xmlOpen(_msg,_id,_num); } }); return; } var xmlOpen = function(_msg,_id,_num){ var main = this; var html = ''; var ID = _id; var Num = _num; var userAgent = window.navigator.userAgent.toLowerCase(); if(_msg == 'load'){ xml = main.xml; var channelData = $(xml).find('channel')[0]; $(channelData).find('item').each(function(i){ if(i < Num){ postTitle = $(this).find('title').text();//記事タイトル取得 postLink = $(this).find('link').text();//記事リンク取得 //日付を取得し年月日を整形 publishedDate = $(this).find('pubDate').text(); var pubDD = new Date(publishedDate); yy = pubDD.getYear();if (yy < 2000) { yy += 1900; } mm = pubDD.getMonth() + 1;dd = pubDD.getDate(); var postDate = yy +'年'+ mm +'月'+ dd +'日'; postDescription = $(this).find('description').text(); //記事冒頭部分取得 if ((userAgent.indexOf('msie') != -1) || (userAgent.indexOf('firefox') != -1)) { postMaster = $(this).find('dc\\:creator').text(); //投稿者名取得 commentNum = $(this).find('slash\\:comments').text(); //コメント数取得 }else{ postMaster = $('creator:first', this).text(); //投稿者名取得 commentNum = $('comments', this)[1].textContent; //コメント数取得 } //カテゴリが1記事に複数あるのである分だけ取得 var postCategory = ''; $(this).find('category').each(function(i){ category = $(this).text(); if(i <= 0){ postCategory += category; }else{ postCategory += ',' + category; } }); //表示する部分を整形 html += '<li><a href="' + postLink + '">' + postTitle +'</a> - ' + postMaster + '<br>' + postDescription; html += '<span>' + postCategory + '/コメント数:'+ commentNum +' (' + postDate + ')</span></li>'; } }); }else{ //エラー時の表示 html += '<li>通信エラー</li>'; } $("#"+ID).html(html); } </script>
補足)
xml内にある<dc:creator>などの要素は、$(xml).find(‘dc\\:creator’) という感じで「:」の前に「\\」を追加することで取得出来ます。
xml内にある<dc:creator>などの要素の取得については、2014.11.05追記をご確認ください。
また、今回のサンプルには無いですが、<tags status="date"></tags> というタグがあった場合。
tagsのstatusの値を取得すしたい時は、$(xml).find(‘tags’).attr(‘status’) という指定で取得出来ます。
jQueryを使わない
使わないでやる方法を色々と試してみたんですが、ブラウザによって読み込みの仕様が違うため、難しくて断念しましたorz
jQueryさまさまですね…。
どうしても!となると同一サーバのファイルだけど、Google AJAX Feed APIを使って取得する、という方法になるのでしょうか。
その他xmlをjsで扱う場合に注意することなど
- ローカルでxmlを参照する場合、表示するhtmlより下位の階層にあること
セキュリティ的な問題で、ローカルで参照する場合は下階層に無ければ参照できません。 - Google Chromeはローカルでxmlを参照することができない
できるようにする方法ももちろんありますが、セキュリティの問題もありますので、自己責任でお願いします。
参考)Google Chrome でAjaxを利用しローカルファイルにアクセスする[to-R] - xmlのキャッシュに注意
「xmlを更新しても更新されない!」場合はキャッシュを読んでしまってる可能性があります。
xmlファイルの末尾にタイムスタンプのクエリなどを追加しておくと回避できます。
(サンプル1以外にはキャッシュ対策のクエリを追加してますので参考までに) - xmlファイルのエラーに注意
「jsでおかしい部分はないのに、うまく読み込めない!」場合はxmlファイル自体がパースエラーを起こしている場合もあります。また、xmlファイルは「&」をそのまま書いているとエラーになります。「&」にしておきましょう。
パースエラーかどうかはxmlファイルをfirefoxなどのブラウザで読み込むことで確認できます。 - xmlに記載したタグを表示したい
xmlの表示したい部分を<![CDATA[~]]>で囲むと幸せになれます。 - 文字化けする時はJSや読み込むXML、表示するHTMLの文字コードをチェック
結構それが原因だったりします。特にIEさんとかIEさんとか… - Google Feed API Key は必要?/* 2014.1.20 追記 */
色んなサイトで呪文のように「まずはGoogle Feed API Keyを取得します」とあるんですが、どうやら不要のようです?(ちょっと曖昧)サンプルではソースを見ていただければ分かる通り使ってはいませんが、動いてはいます。不要になったようです。
JSONの取得のみならAPI Keyは要らない、という記述はみつけましたが。
参考)Google AJAX Feed APIのJSONをjQueryで取得する [Feed to JSON] : ずっと工事中
ある日突然必要になる可能性もなきにしもあらず。公式のリンクがあちこちNot Foundで上手く探せないですね。
こちらは引き続き探しておきたいと思います。
Sign-up for an API Key – Google Loader — Google Developers
↑にあるように、「The Google Loader no longer requires keys.」必要としないようです。
/* 2014.1.20 追記ここまで */
こんなもんでしょうか。
去年このブログを開設したけど、そんなに書かなくなってしまい。原因を考えて、webだけにくくってるからかなーと
思い至ったので、それ以外のことも書いていきたいなーと思います。はい。
とかいいつつ、webの記事で始まりましたが、気にしないでください。
今年はもうちょっとブログ更新できるようにするぞー@・ェ・@
参考記事一覧)
・Google AJAX Feed API入門
・Google Feed APIで用意されている要素以外の要素にアクセスする方法 – KUMA TYPE
・Google AJAX Feed APIのJSONをjQueryで取得する [Feed to JSON] : ずっと工事中
・小粋空間: RSS Feed(フィード)を表示する
・jQueryでサイト内に簡単にRSSフィードを組み込めるプラグイン「zRSSFeed」
・Using jQuery on WSH – 葉っぱ日記
・Google Chrome でAjaxを利用しローカルファイルにアクセスする[to-R]
/* 2014.7.24追記 */
複数のURLを指定する場合についてよくご質問をいただくので、記事にしました。
・複数のRSSなどのxmlファイルを静的なHTMLに表示させる方法
どうぞご参考までに。
/* 2014.11.05追記 */
<dc:creator>などの要素を、$(xml).find(‘dc\\:creator’)で取得できていたのですが、Chromeで取得できなくなっていたので対策についての追記です。
ChromeまたはSafariの場合、
<dc:creator>は、$('creator:first', this).text();
で取得できます。
参考)javascript – Get first element from tag name in jQuery – Stack Overflow(英語)
<slash:comments>は、$('comments', this)[1].textContent;
で取得できました。
xml内の「comments」という文字列を含むタグから探して参照しているので、xmlの内容によっては[1]
の部分は数字が変わります。
IEやfirefoxの場合はこれまでと同じ$(xml).find('dc\\:creator')
で取得できます。
ブラウザで取得方法をかえるので、ブラウザ判定を追加してこんな感じでできます。
var userAgent = window.navigator.userAgent.toLowerCase(); if ((userAgent.indexOf('msie') != -1) || (userAgent.indexOf('firefox') != -1)) { postMaster = $(this).find('dc\\:creator').text(); //投稿者名取得 commentNum = $(this).find('slash\\:comments').text(); //コメント数取得 }else{ postMaster = $('creator:first', this).text(); //投稿者名取得 commentNum = $('comments', this)[1].textContent; //コメント数取得 }
そういえば、SuefaceRTのIEはMSIE
で判別されませんでした。ユーザーエージェント違うみたいですね?