since 2007.8 by K-ichi

振り返れば、記事数は350にもなった。350もの個別ネタがあるわけもなく、当然に関連記事、続編記事などが存在する。
過去記事を参照したときには、リンクを張っている。このリンクは、新記事から旧記事へのリンクでしかない。旧記事からは、リンクされたことは判らない。そしてそんな旧記事が、検索に掛かることがままある。
旧記事にも、関連のある最新記事へのリンクが欲しい。それも自動的につけてほしい。

調べてみるとbloggerには、バックリンクという機能があるらしい。ビンゴ! かと思いきや、どうやら「リンクしましたよ」と、その記事に対して伝えないといけないらしい。手動かよ……

少し間口を広げて、直接的な「被リンク検出」ではなくても、関連記事を並べてくれるようなサービスがないか、ネット上を調べてみた。


関連記事をリストアップする、というスクリプトは、いくつか発表されている。
クリボウ氏のページでは、blogger用のものが2種類、紹介されている。Related Postなどと呼ばれ、いずれも記事に付加された「ラベル」から検出しているらしい。
比較的最近の記事では、Smarter Related Posts ~v2.0というものもある。すなふ氏のページなどに紹介があるが、これもラベルを元にリストアップしているよう。
このv2.0を導入してみた。ページ下部の「関連記事 by Related Posts v2.0」がそれに該当する。

導入は、easy setup wizardサイトでコードを得、bloggerの「HTML/JavaScript」ガジェットにコピペする。
setupサイトでは、ブログのトップページURLを入力したほか、Max Postsを「10」に、Show Thumbs?を「no」に、Related Titleを「関連記事~」に変更した。

以下が貼り付けたソース。得られたコードには、少しお足しをしている。
ピンクが追加した部分で、他とのバランスを考え、表示される文字のスタイルを変更した。

なお、Webページ上の特定部分が、ソースのどこに該当するのかを調べるのに、Google Chromeを使用した。調べたいところで右クリックし、メニューから「要素を検証(N)」とすると、該当するソースが見られる。これは便利。

<!--~~~~~~~~~~~~~~~~~ Include these JS files once: jQuery then plugin -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="http://blogger-related-posts.googlecode.com/files/jquery.related-posts-widget-2.0.min.js"></script>
<!---~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->

<!--~~~~~~~~~~~~~~~~~~~~~~~ required HTML -->
<style>
div.related-posts-widget h2{
  font-weight:normal !important;
  font-size:medium !important;
}
div.related-posts-widget ul.rpw li a strong{
  font-weight:normal; !important;
}
</style>
<div class="related-posts-widget">
<!-- {
 blog_url:'http://k-ichi.blogspot.jp/'
 ,max_posts:10
 ,related_title:'関連記事 by Related Posts v2.0'
 ,thumbs:0
} -->
loading..
</div>
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->


blogger以外でも使える一般向けサービスとしては、LinkWithinZenbackが有名どころのよう。Outbrainを挙げているページもある。

ラベル等blogger特有要素は使えないので、文章内容などから推測していると思われる。LinkWithinは「ブログ内」の、Zenbackは「ブログ内」と「それ以外のZenbackユーザ」の両方の、関連記事が表示される。表示内容の細かな設定はできないらしい。
ともかく繋がりが欲しい向きには、Zenbackのブログ外リンクは重宝しそう。また日本の会社なので、サポート等を考えると、モノリンガルには心強いかもしれない。
ここでは、LinkWithinを使ってみた。下部の「関連記事 by LinkWithin」がそれ。

LinkWithinのサイトで、必要事項を入力すればコードが得られる。
Platformに「Blogger」の選択肢があるが、ここでは「Other」としておいた。Widthは検出記事数なので、最大の「5 stories」とした。
あとから設定を変えたい場合は、再度このページで設定しなおす。コードが得られるが、実は一字一句変わっていない。どうやら、登録したURLからRSSフィードURLを推測し、それに対してユニークなIDを振っているよう。各種設定は、このIDに関連付けられて、サーバ側に記録される。したがって、何度再設定しても問題ない。また、TLDが.jpでも.comでも、同じブログとして認識してくれる。

コードは以下のとおり。追加部分は3ヶ所。
標準のままでは、トップページ表示時に、記事毎にリストが表示されてしまう。ガジェット部分だけに表示されるように、classを定義した(紫)。また、スタイルで文字の太さと位置調整をし(水色)、表示タイトルも変更した(ピンク)。位置調整は、文字のみのリストが表示されたときに意味を持つ。ブラウザによってデフォルト値が異なるので、設定前に読み取って出力内容に書き込みたかったが、かなり手間(例1例2例3)なため、「1.5文字」という固定値とした。

<div class="linkwithin_div"></div>
<style>
div.linkwithin_text{
  font-weight:normal !important;
}
ul.linkwithin_textlist li{
  margin:0 0 0 1.5em !important;
}
</style>
<script>
var linkwithin_site_id = 1392455;
var linkwithin_text='関連記事 by LinkWithin';
</script>
<script src="http://www.linkwithin.com/widget.js"></script>
<a href="http://www.linkwithin.com/"><img src="http://www.linkwithin.com/pixel.png" alt="Related Posts Plugin for WordPress, Blogger..." style="border: 0" /></a>




上記いずれも、なかなか使える。なぜこの記事が? というものも含まれるが、むしろ新しい発見があっていいかもしれない。ただ、本来の目的である、被リンク記事を漏らしていることがあった。片方でしか検出されないことが結構ある。仕方ないので、手作りすることにした。
当初は、サーバ側に検索など処理を任せて結果のHTMLだけを得るとか、Googleなどからリンク関連の情報をもらうとか、虫のいい妄想をしていた。結局はいずれも無理と判断し、クライアント側でスクリプトで処理することとした。

作成に当たっては、いくつか制限をつけた。
bloggerユーザは、ごく日常的に(?)テンプレートのXMLをいじる。それによって、blogger特有のデータが得られたり、好きなところへパーツを置けたりする。が、正直なところ管理が面倒になってきた。よって、HTML/JavaScriptガジェットの中に置いて完了できるもの、とした。
また、後に内容を忘れても運用ができるように、スクリプト自体に特段の設定は要らないようにした。スクリプトは、自身が置かれた場所から、標準的な機能だけを使って各種URLを想定、処理する。bloggerを始めたままのノーマルな状態なら、スクリプトをそのまま貼ってすぐ使用可能。
さらには、表示メッセージなどは好みで変え易いように、冒頭で変数に入れるようにした。

動作内容とコードは、以下のとおり。

このスクリプトは、このブログでは常に表示されている。まずは現在のURLから、検出に適したページかどうかチェックする。適合ページであれば、公開済みの記事をすべて読み込み、このページのURLが含まれるものを探す。すべてリストアップし、その結果を「id_20120829_localBacklinks」のID部分に出力する。

記事の読み込みには、JSON形式のフィードデータを使う。URLには、.comや.jpのようにTLDの異なるものがあるが、同一とみなして処理する。フィードデータ受信に必要なXMLHttpRequestは、IE7以降の機能ということなので、Wikipediaを参考に下位互換を考慮する。またJSONデータのパースを行うJSON.parseは、IE8以降の機能ということなので、状況によってはevalを使うようにする。
このスクリプトは、Win7 IE9、Win7 XPM IE6、およびWin7 Chrome22で動作を確認している。

この記事へのリンク
<div><ul><span id='id_20120829_localBacklinks'></span></ul></div>
<script type='text/javascript'><!-- 

var loadingText = '検索中……';
var noResultText = '( 被リンクなし )';
var notSearchText = '( 複合/特殊ページ )';
var maxTarget = 9999;
var path = location.pathname;
var myUrl = 'http://' + location.host + path;
var feedUrl = 'http://' + location.host + '/feeds/posts/full?alt=json&orderby=published';
var regexMyUrl = myUrl.replace(/\.blogspot\..+?\//i,'.blogspot.[a-z]+/').replace(/\//g,'\\/').replace(/\./g,'\\.');

var LBList = '';
var doc = document.getElementById("id_20120829_localBacklinks");
var addLi = function(str){return '<li>'+str+'</li>';};
var xhr = new function(){
  if(window.XMLHttpRequest) {return new XMLHttpRequest();}
  if(window.ActiveXObject) {
    try{return new ActiveXObject("Msxml2.XMLHTTP.6.0");}catch(e){}
    try{return new ActiveXObject("Msxml2.XMLHTTP.3.0");}catch(e){}
    return new ActiveXObject("Microsoft.XMLHTTP");
  }
};
function getJson(sta,len){
  xhr.open('GET',feedUrl+'&start-index='+sta+'&max-results='+len,true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
      try{var obj = JSON.parse(xhr.responseText);}catch(e){var obj = eval("("+xhr.responseText+")");}
      for(var i=0; i<obj.feed.entry.length; i++){
        var ent = obj.feed.entry[i];
        if(RegExp(regexMyUrl,'i').test(ent.content.$t)){
          for(var j=0; j<ent.link.length; j++){
            if(ent.link[j].rel == 'alternate'){
              LBList += addLi('<a href="' + ent.link[j].href + '">'
                        + (ent.published.$t).match(/.{10}/) + ' : ' + ent.link[j].title
                        + '</a>');
            }
          }
        }
      }
      var nextTarget = obj.feed.openSearch$startIndex.$t-0 + obj.feed.entry.length-0;
      doc.innerHTML = addLi(loadingText + ~~((nextTarget-1)/obj.feed.openSearch$totalResults.$t*100) + '%');
      if(nextTarget <= Math.min(maxTarget,obj.feed.openSearch$totalResults.$t)){
        getJson(nextTarget,101);
      }else{
        if(LBList == ''){doc.innerHTML = addLi(noResultText);}else{doc.innerHTML = LBList;}
      }
    }
  };
  xhr.send(null);
}

if(path == '/'
   || RegExp('\/search','i').test(path)
// || RegExp('_archive.html','i').test(path)
   || RegExp('\/post-preview','i').test(path)
   || RegExp('\/layout-preview','i').test(path)
  ){
  doc.innerHTML = addLi(notSearchText);
}else{
  doc.innerHTML = addLi(loadingText);
  getJson(1,1);
}

// -->
</script>

Ajaxだの正規表現だの、ほぼ初見の状態から作り始めた。正規表現関連では、返値がときに特殊な内容の配列なため、掲示板等でアドバイスを受けてなんとかなった。フィードデータ読み込み確認に関するonreadystatechangeは、複数回設定したとき、予約されるのではなく上書きされることが解らず、再帰呼出の形に辿りつくのにかなり骨が折れた。

JSONデータを処理するには、その構造を知る必要がある。視覚的にその内容を確認するのに、JSON Viewerを使った。現在のバージョンは、1.2.0.0。JSONデータをこのアプリに食わせると、ツリー状に表示されて、どこに何が入っているのか見やすくなる。
bloggerのフィードデータは、一度に受け取れる最大の500件のときとそれ以外とで、構造が一部変わる。こういったビューワで見ないと気づかない。

0 件のコメント:

コメントを投稿

.

関連記事


この記事へのリンク by 関連記事、被リンク記事をリストアップする」記事

ブログ アーカイブ