to be continued ~とあるプログラマーの実験的開発日誌~ -20ページ目

to be continued ~とあるプログラマーの実験的開発日誌~

WEBデザイナー兼プログラマーである管理人が日々のトラブル解決に向けて奮闘する開発日誌。

Perlでスクリプトを書いていて、少々迷ったので備忘録的にメモ。


ある文字列から特定の文字列をすべて抜き出すためのスクリプト。例えばHTMLから<a>リンクタグのみ抜き出したい場合など。



決まった文字列のみを検索するだけなら普通に、


$str =~ m/(検索したい文字)/g;


とでもすれば良いのだが、タグのように中身が決まっていない文字列の場合、これでは都合が悪い。


index関数でマッチングする文字列の位置をすべて把握できれば、のちのち利用しやすいだろう。


幸いindex関数はマッチする文字列がなければ-1を返すので、これを利用してwhileで廻してやれば、上手くいきそうだ。


というわけで以下のようにスクリプトを記述してみた。



my $str = "検索の対象となる文字列たとえばHTML等";
my $pos;
my $match = "<a href"; #検索したい文字
my $int = 0; #開始位置
while(1)
{
$pos = index($str,"$match",$int); #開始位置$intから検索文字$matchの位置を$posに格納
if ($pos1 != -1) #-1以外=検索文字が見つかった場合
{
#ここに実行したい処理を記述
#単純に位置のみを格納するならpush @temp,$pos;とでもすれば良い
$int = $pos1+1; #次の開始位置
}
else #-1のとき=検索文字が見つかせない
{
last; #whileループを抜ける
}



あとは">"などを目印にタグの末尾の位置を取得して、substrなどで抜き出すなり削除するなり加工すれば良さそうだ。


前回、JavaScriptでクリックされたアドレスを取得する方法を書いた。


ところが、このスクリプトだとカウントされる回数が1ずつではないという問題が発生。


取得したアドレスをHTMLで表示させるテストなどをして受け渡しに問題ないことは確認している。


で、ふと思いついたのが、CGIが連続して呼び出されているのではないかという可能性。


そこで、次のようなHTMLとCGIを使ってテストしてみた。



********** HTML側の記述 **********


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis" />
<title></title>
<script type="text/javascript">
window.onload=init;
function init()
{
var e=document.getElementsByTagName('a');
for (var i=0; i<e.length; i++) e[i].onclick=function()
{
location.href="test_outcount.cgi?" + this.href;
}
}
</script>
</head>
<body>
<p><a href="http://www.yahoo.co.jp/index.html" target="_blank">Yahoo</a>
</body>
</html>


********** CGI側(test_outcount.cgi )の記述 **********


#!/usr/bin/perl
#クリック元URL取得
$url = $ENV{"QUERY_STRING"};
#チェック用
open(OUT, ">>data.dat"); # 上書きオープン
print OUT $url; # data.dat に $url の内容を出力
close(OUT);


これでCGIの使える環境下であれば、HTMLのリンクをクリックすると対象となっているyahooのアドレスがCGIに送られてdata.datというファイルを作成し、そこに受け取ったアドレスを書き込むはずである。連続して呼び出されていればその回数分、書き込まれる。


実際にテストしてみた結果は、まさにビンゴだった。


しかも、ブラウザを変えてテストしてみた結果、ブラウザ毎にJavaScriptの実行回数が違うこともわかった。


具体的にはIEとGoogle Chromeでは2回、Operaでは5回、FireFoxは1回のみという結果であった。


ブラウザがJavaScriptを繰り返し呼び出しているというのは盲点だった。ネットで同様の報告がないか調べてみたが、少なくとも見つけられなかったし、ブラウザの設定にもそれらしき項目はないようだ。


まあ、原因がわかったので良しとする。


対処法としてあまりスマートとは言えないが、CGIが受け取ったアドレスを外部ファイルとして書き出しておき、前回のアドレスを参照することで同じアドレスだった場合はカウントアップしないという処理にした。これで正常にカウントアップされるようになった。


現在、Perlでクリックされた回数に応じて上位に表示されるというランキング機能付リンク集を制作している。


そこでリンク先をクリックされると、そのアドレスをCGI側に送るスクリプトが必要になった。通常であればアドレスに引数をつけるとか、隠しフォームで対処するとかすれば良いのだが、ある事情からHTML側のタグはいじらずそのままにしておきたい。


いろいろ調べた結果、JavaScriptで行えることがわかったので、その方法を紹介しよう。


HTMLの<head>~</head>間に以下のスクリプトを記述するか、外部ファイル化してリンクするだけで利用できる。


<!—ここから –>
<script type="text/javascript">
window.onload=init;
function
init()
{
var e=document.getElementsByTagName('a');
for
(var i=0; i<e.length; i++) e[i].onclick=function()
{
location.href="test_outcount.cgi?" + this.href;
}
}
</script>
<!—ここまで –>


これでtest_outcount.cgiというスクリプトにクリックされたアドレスがGET METHODで送られる。


CGI側の取得はGET METHODなので$ENV{"QUERY_STRING"}で行えば良い。


あとは取得したリンク先を基にデータベースから対象となるデータを検索して、アウトカウントを加算するだけである。


と、ここまでは順調だったのだが、実際に試してみるとカウントの回数がおかしい。


カウントアップはされているので、呼び出しに問題はないはずだが、1ずつカウントアップされていないのだ。


この問題の解決は次回に続く。


有名なJavaScriptのライブラリjQuery(ジェイクエリー) 」を使って、画像にマウスを合わせると画像の背景を暗くして(別に他の色でも良いが)コメントを表示させるスクリプトを紹介する。なお、jQueryをブログ等で利用する方法については、すでに様々なサイトで紹介されているのでここでは割愛させてもらう。


複数の画像を<ul>タグでリスト化して表示しているという前提で、


HTMLのheadもしくはbodyに以下のJavaScriptを記述


<script type="text/javascript">
$(function (){
$("li").hover(function(){
$(".layer", this).show();
},
function(){
$(".layer", this).hide();
});
});
</script>


.layerは画像に重ねる部分のclass指定。


今回はリスト化してある前提なので、<li>タグをスクリプト内で指定してあるが、例えばテーブル表示などでは<td>タグにすれば良い。


表示部分のHTMLは以下の通り。


<div>
<ul id="photograph">
<li>
<a href="http://www1.ocn.ne.jp/~daworks" target="_blank"><span class="layer"><p>こういう時のサンプル素材ってテーマがあるわけではないので何を選ぶか困ります。そういう意味ではこの文章も同じ。</p></span><img src="img/120530002.jpg" width="200" height="140" alt="サンプル" /></a></li>
</ul>
</div>


<ul>タグのid="photograph"は単に適用させる要素を指定しているだけなので、何でも良い。


ポイントは画像を<a>要素でネストして、その間にclass指定したspanで表示させたいテキストを記述すること。


次にスタイルシートの記述だが、少々注意が必要だ。まず先にサンプルを見て欲しい。


サンプルページへのリンク


透過処理以外のCSSの記述は以下の通り。


ul {
list-style: none;
}
#photograph a {
border:0;
display:block;
text-decoration:none;
position: relative;
}
#photograph img {
border-radius: 10px; /*画像の角を丸くする*/
}
.layer p { /*画像に重ねる文字の装飾*/
text-align:center;
color:white;
padding:0;
margin:10px;
}


肝心の透過処理でまず考えたのが、CSSの透過度を指定するopacity要素を利用する方法。


サンプルでは一番上の画像になる。具体的な記述は以下のようになる。



.layer {
text-align:center;
position: absolute;
cursor: pointer;
display: none;
width: 200px; /*重ねる画像の幅に合わせる*/
height: 140px; /*重ねる画像の高さに合わせる*/
border-radius: 10px; /*画像に合わせて背景の角を丸くする*/
/*ここから透過処理*/
background: #000;
opacity:0.50;
filter: alpha(opacity=50);
-moz-opacity:0.25;*/
}


しかし、この方法だとサンプルでもわかるように表示させる文字まで透過されてしまう。


そこで次にbackground-colorをrgbaで指定する方法。サンプルでは2番目の画像となる。


.layer {
text-align:center;
position: absolute;
cursor: pointer;
display: none;
width: 200px; /*重ねる画像の幅に合わせる*/
height: 140px; /*重ねる画像の高さに合わせる*/
border-radius: 10px; /*画像に合わせて背景の角を丸くする*/
/*ここから透過処理*/
background-color: rgba(50,50,50,0.5);
}


これで一応上手くいったが、
background-colorではなく、backgroundで指定する方法というのもあったので、ついでに試してみる(サンプル一番下)。


.layer {
text-align:center;
position: absolute;
cursor: pointer;
display: none;
width: 200px; /*重ねる画像の幅に合わせる*/
height: 140px; /*重ねる画像の高さに合わせる*/
border-radius: 10px; /*画像に合わせて背景の角を丸くする*/
/*ここから透過処理*/
background: rgb(0, 0, 0); /* IE6, 7向け */
background: rgba(0,0,0,0.5);
}


ブラウザによる表示の違いは各自で確認して欲しいが、結論を言うとOpera、FireFox,Google Chromeではすべて上記の通りの表示になったが、IEだけは
backgroundを使用した場合に限り、透過されずに黒一色で画像が見えなくなってしまった。


というわけで、現状では
background-colorをrgbaで指定する方法がベストと言える。



開設してわずかだというのにこんなことを書くのは何だが、


タイトルのまんま。


とにかくエディタ(新旧どちらも)が使いにくい。


まあ、改行するたびに<p>タグで囲われるのはともかく、


「HTMLタグを表示」にするたびに空行が入るのは何なのか。


毎回、ソースで余分なタグを消したり、行間を詰めたりで面倒この上ない。


どうやら使用するブラウザによって挙動がかわるようだ。


ちなみに管理人が普段使用しているブラウザはOperaである。


他のブラウザで試した結果、どうやらIEが一番問題なさそうである。


しかし、ブログのためにいちいちIEを使ってログインするのも面倒なので、


現在はWindows Live Writerで書いて


コピペしている(Live Writerの投稿はいまいち信用できない)。


もう少し簡単な方法はないものだろうか……。