manekineko倉金家ホームページ

趣味の部屋/ホームページ余話

アクセスの判定と表示の振分

2011年8月27日 2011年11月3日 更新
 当サイトの表示は<iframe>による疑似フレームにjavascript、cookieを使って表示制御をしているためそのままでは古いブラウザや携帯端末、検索ロボット等との相性は多少よくないようにも思われます。そこでアクセスの判定をして疑似フレームや通常のフレームを切り替えたり、あるいはフレームを使わないなど最も読みやすいよう表示の仕方を変えることにしました。
 そのおかげか今のところページを見る人や検索ロボットさんの評価も上々のようです。


 このサイトでは通常表示にはHTML5対応などと銘打って<iframe>を使い、さらにjavascriptとcookieを使って表示の制御をしています。通常のブラウザでは見やすい表示がされていると思いますが、HTML5未対応の古いブラウザや携帯端末、あるいは検索ロボットについてはうまく見れない可能性もあります。
 そのため<iframe>や<frameset>によるフレーム表示はそれに対応したブラウザに対してのみ行うことにしました。

 で、アクセスの種別を判定し、振分けをします。

1.トップページでアクセスの種類を判別。
2.HTML5対応ブラウザについては<iframe>による疑似フレームを使ってメニューページ(左)と記事ページ(右)を同時に表示。
3.HTML5未対応の古いブラウザについては従来のフレーム(<frameset>)で同様にメニューページと記事ページを表示。
4.携帯端末についてはまずはメニューだけを表示。あとはそこから記事ページに行ってもらう。
 (携帯を持っていないのでフレームページの表示確認ができないので。ごめん。)
5.検索アクセスについても同様にまずはメニューだけを表示。そこから記事ページに行ってもらう。
 記事リンクと記事ページそのものが独立しているので検索ロボットにはむしろ読みやすかろう。
6.その他のフレームが表示可能と確認できないエージェントについてもまずはメニューを表示。そこから記事ページにいってもらう。
 この振り分けはjavascriptではなくphpを使ってHTTPサーバー側でやっているのでアクセスした側からはわかりません。(javascriptで書いてもフレームに対応していないようなブラウザではjavascriptも動作しない可能性が高いと思う。)
 また、通常のブラウザ名をかたった情報収集アクセスもかなりあるようで、その場合もあえてフレームで表示してやるため、ログを見る限り幾度来ても肝心のページが取得できていません(つまり見れていない)。そんなのまでめんどう見る気はないし逆に迷惑ロボよけに使ってしまっています。(ブラウザ名を騙るほうが悪いんだからね。)

 その他これを使って検索アクセスには拡大写真を表示しないなど多少の制限も行ってはいますが。

 参考にこれらの判定用リスト(トップページindex.htmlのリスト)と振分コードです。
手間がかかったのが携帯端末と検索ロボットの判定用リスト。2011年8月現在のものです。
(なお、当サーバーでは.htmlファイルでもphpが動くように設定してあります。)
以下のことを御留意の上、参考としてご覧ください。
1.趣旨は<iframe>あるいは<frameset>による表示が可能かどうかでページが問題なく閲覧できるようにするためですが、リストはあくまで筆者が個人的に使用するため作成したものでその内容を保証するものではありません。
 たまたまこの時点で筆者が利用可能だったブラウザで<iframe>によるページ分割表示を確認したのをHTML5<iframe>対応として分類しているだけで、すべて確実に調べたわけではありません。
2.筆者は携帯端末を持っておらずフレーム表示の確認ができないため携帯端末はすべてフレーム非対応に分類していますが、本当はそうでないかもしれません。
3.検索(情報収集)アクセスについて言えば、通常ブラウザをかたっているあやしいアクセスを無視すれば、必ずしも振分けの条件に入れる必要はないのかもしれません。(今思えば。せっかくリストをつくったのでいれてるけど。)
 携帯端末についてはPC用ブラウザと同じ名前を冠しているのがあり、(表示を変えるなら)判定は省略できないと思われます。
というわけでリストそのものの正確性はともかく、こんな方法もあるんだねくらいのところでご覧ください。

アクセス判定用リストと振分プログラム:
トップページの判定・振り分けコード。
<?php
//// リストはPCRE正規表現。デリミタは%に統一しておく。
//// PCRE正規表現以外の正規表現のメタ文字( ^ . \ + * ? [ ] ( ) $ など)は\でエスケープすること。

//// ブラウザの判定
// 通常の人間が使うブラウザの判定用リスト。frame,cookie,javascriptが使えることが最低条件。
// その他w3mとかあるようだが確認できてないので除く。
$BrowserList = '%(MSIE|Firefox|Safari|Opera|Chrome|SeaMonkey|Lunascape|Sleipnir|Netscape|Iceweasel)[ /][1-9][\.0-9A-Za-z]*%';
function is_browser()
{
global $BrowserList;

if(!empty($_SERVER['HTTP_USER_AGENT']) && preg_match($BrowserList, $_SERVER['HTTP_USER_AGENT'])){
return(TRUE);
}
return(FALSE);
}

// 上記のうちHTML5にある程度対応しているもの。(HTML5によるframe代用iframe+javascriptの動作を確認したもの。)
$HTML5BrowserList = '%MSIE ([8-9]|[1-9][0-9]+)|Firefox/([3-9]|[1-9][0-9]+)|Chrome/(9|[1-9][0-9]+)|Opera/(9|[1-9][0-9]+)|Safari/([5-9]|[1-9][0-9]+)|SeaMonkey/([2-9]|[1-9][0-9]+)|Lunascape[ /]([6-9]|[1-9][0-9]+)|Sleipnir/([2-9]|[1-9][0-9]+)%';
function is_HTML5_browser()
{
global $HTML5BrowserList;

if(!empty($_SERVER['HTTP_USER_AGENT']) && preg_match($HTML5BrowserList, $_SERVER['HTTP_USER_AGENT'])){
return(TRUE);
}
return(FALSE);
}

//// 携帯型モバイルブラウザの検出。
// このリストに合致した場合モバイルブラウザと判断され、フレームを使わないなど小画面での見やすさに考慮した表示がなされます。
// 但し携帯は持っていないので全く確認はしていない!!
// 移り変わりが激しいようでこまめにメンテナンスする必要があるかもしれません。
$mobile_host_list = array(
'%\.mopera\.net$%',
'%\.spmode\.ne\.jp$%',
);
$mobile_agent_list = array(
'%Mobile%',
'%ASTEL/%',
'%BlackBerry[0-9]*/%',
'%DDIPOCKET;%',
'%DoCoMo/%',
'%Google Wireless Transcoder%', // mobile agentそのものではなく接続サービスのようだが。
'%iPhone%',
'%J-PHONE/%',
'%KDDI-%',
'%Mobile Safari/%',
'%Mozzila/.*NetFront/%',
'%Nokia%',
'%SoftBank/%',
'%SonyEricsson%',
'%UP.Browser/%',
'%Vodafone/%',
'%YahooMobile/%', // Mobile Agent向けモードでサーチしているようだ。botにもいれておく。
'%Yeti-Mobile/%',
);
function is_mobile_access()
{
global $mobile_host_list, $mobile_agent_list;

$host = isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : '';
if($host){
foreach($mobile_host_list as $mobile_host){
if(preg_match($mobile_host, $host)){ return(TRUE); }
}
}
$agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
if($agent){
foreach($mobile_agent_list as $mobile_agent){
if(preg_match($mobile_agent, $agent)){ return(TRUE); }
}
}
return(FALSE);
}

//// 検索ロボットの判定。
// このリストに合致した場合フレームを使わないで表示されます。
// 別にこれを使わなくても通常ブラウザをかたっていない限りちゃんと表示はされますが。
// 当初 $bot_ip_listもあったが調査更新の手間が多いわりに効果がないのでやめた。(すべてhostとagentで判定できていた。)
// hostでの判定を使用するにはhttpd.confのHostnameLookupsをonにしておく必要があります。
$bot_host_list = array(
'%as13448\.com$%', // 通常を装っている検索? あやしい!
'%\.asianetcom\.net$%', // 通常を装っている検索? あやしい!
'%\.ask\.com$%',
'%bot[0-9]+\.linguee\.com$%',
'%mkt-search\.comodo\.com$%',
'%copilot\.thunderstone\.com$%',
'%\.crawl\.baidu\.jp$%',
'%\.crawl\.yahoo\.net$%',
'%crawl[-0-9]+\.naver\.jp$%',
'%crawl11\.pub\.colo-fo\.brn1\.verisign\.com$%',
'%crawl-.*\.cuill\.com$%',
'%crawl[-0-9]+.botje\.com$%',
'%crawler.*\.domaincrawler\.com$%',
'%^ec2-[-0-9]+\.compute-1\.amazonaws\.com$%',
'%\.exabot\.com$%',
'%\.googlebot\.com$%',
'%\.inktomisearch\.com$%',
'%\.irl\.cs\.tamu\.edu$%',
'%msnbot\.msn\.com$%',
'%picsearch\.com$%',
'%\.pingdom\.com$%',
'%rate-limited-proxy[-0-9]*\.google\.com$%',
'%search\.tnz\.yahoo\.co\.jp$%',
'%\.search\.live\.com$%',
'%\.search\.msn\.com$%',
'%\.sdi\.trendnet\.org$%',
'%^wtp-g.-maya[1-9]+\.sjdc%',
'%\.super-goo\.com%',
'%\.teoma\.com$%',
'%\.yahooresearchcluster\.com$%',
);
$bot_agent_list = array(
'%aiHitBot[-/]%',
'%archive.org_bot%',
'%Ask Jeeves/Teoma%',
'%Baiduspider/%',
'%Baiduspider\+%',
'%BecomeJPBot%',
'%bingbot/%',
'%BMC/%',
'%CatchBot/%',
'%Comodo-Certificates-Spider%',
'%DotBot/%',
'%DBLBot/%',
'%DomainCrawler/%',
'%\(\+Empirius bot\)%',
'%Exabot/%',
'%Ezooms/%',
'%Googlebot/%',
'%Googlebot-Mobile/%',
'%Google Web Preview%',
'%HTMLParser/%',
'%HuaweiSymantecSpider/%',
'%ia_archiver%',
'%ichiro/%',
'%ips-agent%',
'%IRLbot/%',
'%^Java/%',
'%jig browser web;%',
'%larbin[0-9\.]+%',
'%libwww-perl/%',
'%Linguee Bot%',
'%LinkWalker%',
'%Microsoft-WebDAV-MiniRedir/%',
'%MJ[0-9\.]+bot/%',
'%Mnogosearch%',
'%msnbot-media/%',
'%msnbot%',
'%NetcraftSurveyAgent/%',
'%Netcraft Web Server Survey%',
'%Nutch%i',
'%page_test larbin%',
'%PEAR-crawler%',
'%Peew/%',
'%Pingdom\.com/tools%',
'%Pingdom GIGRIB%',
'%Plukkie/%',
'%psbot/%',
'%Purebot/%',
'%Python-urllib/%',
'%ScoutJet%',
'%Search[0-9]+Bot/%',
'%Shim-Crawler%',
'%SiteBot/%',
'%Sogou Orion Spider/%',
'%Sogou-Test-Spider/%',
'%Sogou web spider%',
'%Sosospider%',
'%Speedy Spider%',
'%Steeler/%', // 東京大学喜連川研究室 http://www.tkl.iis.u-tokyo.ac.jp/~crawler/
'%SurveyBot/%',
'%\(tarakas\)%',
'%TencentTraveler%', // botではないかもしれないが挙動はそれに近い。
'%TMCrawler%',
'%TurnitinBot/%',
'%TweetmemeBot%',
'%Twiceler%',
'%webcrawler%',
'%WebDataCentreBot/%',
'%WebServerStats/%',
'%Wget/%',
'%Y!J-.*crawler%',
'%YahooCacheSystem%',
'%YahooMobile/%', // Mobile Agent向けモードでサーチしているようだ。mobileにもいれておく。
'%Yahoo! Slurp%',
'%YandexBot/%',
'%Yeti/%',
'%yetibot@naver\.com,%',
'%Yeti-Mobile/%',
'%\(Y!J-AGENT\)%',
'%YodaoBot/%',
'%^Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)$%', // trendmicro
// 以下はばかよけに近い。
'%bot%i',
'%crawler%i',
'%robot%i',
'%spider%i',
);
function is_bot_access()
{
global $bot_host_list, $bot_agent_list;

// まずIPアドレスでアクセスしてくるのはロボットとみなす。
if(preg_match('%^[1-9][0-9\.]+$%', trim($_SERVER['SERVER_NAME']))){ return(TRUE); }
// Agentが空なのも普通のアクセスとはみなせない。
if(empty($_SERVER['HTTP_USER_AGENT'])){ return(TRUE); }
$host = isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : '';
if($host){
foreach($bot_host_list as $bot_host){
if(preg_match($bot_host, $host)){ return(TRUE); }
}
}
$agent = $_SERVER['HTTP_USER_AGENT'];
foreach($bot_agent_list as $bot_agent){
if(preg_match($bot_agent, $agent)){ return(TRUE); }
}
return(FALSE);
}


// アクセスによる振り分け(基本形、詳細省略。)
if(is_mobile_access() || is_bot_access()){ // 携帯端末アクセス、検索アクセス
if(!empty($_REQUEST['id'])){ // ページが指定されたら
include('main.html'); // 記事ページを表示
} else {
include('sitemap.html'); // 各記事ページへのリンクを表示
}
}elseif(is_HTML5_browser()){ // ほぼHTML5に対応しているブラウザで疑似フレームの表示確認をしたもの
include('index5.html'); // HTML5でiframeを使用した左右分割疑似フレームにて表示
}elseif(is_browser()){ // 通常のブラウザでHTML5未確認
include('index4.html'); // HTML4で従来の左右分割フレーム(<frameset>)を使用して表示
}else{ // あとはわからん。フレームは使わないでおこう。
if(!empty($_REQUEST['id'])){ // ページが指定されたら
include('main.html'); // 記事ページを表示
} else {
include('sitemap.html'); // 各記事ページへのリンクを表示
}
}

?>

 またアクセスログの色分け等にも使っていますが、アクセスの種別が一目でわかり、たいへん便利です。
見てみたいなら → このサイトのアクセスログ …参考に特別大公開!。表示していない内容もあります。(2011.9.5追加)

改訂版作成…2011.09.30.追加
 しばらくこれで運用していましたが検索アクセスの判定データが多いためアクセスログを表示するのにデータが何千件もあると余計な時間がかかるようです。
で、細かい判定はやめにしてcrawrer,search,spiderだのという語が入っていたら検索アクセスと判定するように修正。
これでも充分。スピードも少し早くなったようだ。
余談ながら検索ロボットといってもいろいろあるようで、Google様やYahooさんのようにその結果をこちらも利用できるのなら歓迎ですが、データを何に使うんだかまったくわからないのもあります。(その方が多い。)
 せめて自分は検索ロボットだよと名乗るのと、検索したら情報はここで見れるよというURLをAgentに表示するくらいの礼儀はあってもいいだろうに。
 次は「いい検索ロボット」と「わるい検索ロボット」に分けてそれなりの対応をしてやろうかなどと考えたりもしています。
新(手抜き)判定リスト:
検索アクセスの判定リストの部分のみ。あとは同じです。
(前略)

//// 検索ロボットの判定。
// このリストに合致した場合フレームを使わないで表示されます。
// 別にこれを使わなくても通常ブラウザをかたっていない限りちゃんと表示はされますが。
// 当初 $bot_ip_listもあったが調査更新の手間が多いわりに効果がないのでやめた。(すべてhostとagentで判定できていた。)
// hostでの判定を使用するにはhttpd.confのHostnameLookupsをonにしておく必要があります。
$bot_host_list = array(
//// 基本的になるべく一般規則で引っかける方針に変更。2011.09.30.
//// 一般的に以下の語が入っていれば検索サイトとみなす。引っかけてほしい順に列記。
'%crawl(er)?%i',
'%search%i',
'%spider%i',
// 'bot'は普通のドメイン名の一部にありそうなのでやめておく。

//// 一般規則でひっかからないものを記載。
'%^rate-limited-proxy[-0-9]*\.google\.com$%', // mobile向けサービスのようだ。
'%bot[0-9]+\.linguee\.com$%',
'%\.exabot\.com$%',
'%msnbot\.msn\.com$%',
);
$bot_agent_list = array(
//// 基本的になるべく一般規則で引っかける方針に変更。2011.09.30.
//// 一般的に以下の語が入っていれば検索エージェントとみなす。引っかけてほしい順に列記。
'%crawl(er)?%i',
'%search%i',
'%spider%i',
'%(ro)?bots?%i',

//// 一般規則でひっかからないものを記載。
'%Google Web Preview%',
'%YahooMobile/%',
'%Yahoo! Slurp%',
'%Ask Jeeves/Teoma%',
'%BMC/%',
'%Ezooms/%',
'%HTMLParser/%',
'%ia_archiver%',
'%ichiro/%',
'%Indy Library%',
'%ips-agent%',
'%^Java/%',
'%larbin[0-9\.]+%',
'%libwww-perl/%',
'%LinkWalker%',
'%NetcraftSurveyAgent/%',
'%Netcraft Web Server Survey%',
'%Nutch%i',
'%page_test larbin%',
'%Peew/%',
'%Pingdom\.com/tools%',
'%Pingdom GIGRIB%',
'%Plukkie/%',
'%^pirst; MSIE 8\.0;$%',
'%Python-urllib/%',
'%ScoutJet%',
'%Steeler/%',
'%\(tarakas\)%',
'%Twiceler%',
'%WebServerStats/%',
'%Wget/%',
'%Y!J-[A-Za-z/]+%',
'%YahooCacheSystem%',
'%YahooSeeker%',
'%Yeti/%',
'%Yeti-Mobile/%',
'%\(Y!J-AGENT\)%',
);

(後略)
 なお新しいリストでは通常ブラウザ名をかたっているらしい情報収集アクセスを検索アクセスと判定するのもやめています。
その場合通常ブラウザ用のトップページにはフレームやjavascriptをふんだんにつかっているため、本当に通常のブラウザでなければ記事ページを取得できない可能性が高いのですが、むしろそれでよし!。