フィンローダのあっぱれご意見番 第107回「移転先不明」
← 前のをみる | 「フィンローダのあっぱれご意見番」一覧 | 次のをみる →
1998年5月号に「らんだむCGりんく」というページをテスト公開している話を書いた。 このページはサンプルのために作ったCGIを使って 「リンクフリー」を自称しているCG系サイトをランダムに選ぶものだ。 実はまだ公開していたりする。 当時は500~1000程度のサイトを登録していたが、 数だけは増えて、最近は3000程度のサイトが登録されている。 単なる趣味でやっているものだから、内容が充実しているわけでもないが、 デッドリンク率だけは多分1~2%程度で、これは結構低いのではないかと思う。 | ||
紹介しているのは3000程度のサイトだが、 候補リストには7000程度のサイトが登録されている。 残りの4000は何かというと、 閉鎖されたとか、工事中だとか、 趣旨変更でCGがなくなったとか、 リンクフリーであることを未確認だとか、 ちょっとアダルトなのであえて自分だけのお楽しみとか、そういったサイトが4000あるのだ。 | ※ アダルトサイトにリンクすることを禁止しているプロバイダもある。 ただし、何をもってアダルトとするのか定義しているプロバイダは見たことがない。 | |
新規サイトの追加は芋蔓方式である。 殆どのCG系サイトには同系統のサイトへのリンクページがある。 その中に「これは見たことがない」と思ったのがあれば調べてみるのだ。 それが未登録サイトなら追加する。 問題は、それが未登録かどうかの判断である。 既に見た何千のサイトを全部覚えられる訳はない。 幸い、候補サイトの一覧データは単なるHTML形式のテキストファイルだから、 WZのようなエディタで開いてURLを検索すればよい。 ところが、この確認作業が、index.htmの有無だとか、最後に「/」があるかとか、 引っ越す前のURLになっていたりとか、 そういった細かい問題もあるので結構面倒なのだ。 | ※ WZ: Windows で動作する軽快なエディタ。 もっと昔、MS-DOS で動作する Vz というエディタがあって、 その操作性が踏襲されていたりするので、よく使っていた。 というか、今も使っている。 | |
リンクページに登録されているサイトが数十もあると、 一つずつチェックするとも大変だ。 そこで、URLだけを指定して、 自動的にリンクをチェックできないか、というアイデアが出てくる。 実はこれが既に実現していた。 VAIO noteでwin32版のapacheを起動し、 perlで書いた検索スクリプトをCGIで呼び出す仕組みだ。 その結果、登録されていない URL だけを振り分けるのである。 | ※ 別に apache がなくても、 perl を使って目的のページに含まれているリンクをチェックするプログラムは書ける。 apache を使った理由は後に出てくる。 | |
これでかなり便利かつ楽になった。 ただ、問題は処理時間である。 マシンの負荷にもよるが、結果を出すのに20~30秒程度かかってしまうのだ。 処理の流れは単純だ。 まず、数千サイトの情報が入った一覧リストを読み込む。 サイズは800KB程度ある。 読み込みながら、ハッシュのテーブルを作る。 最後に、生成されたハッシュに対して、 指定されたそれぞれのURLを使って検索する。 ということは、 CGIを呼び出す毎にファイルを一から読み込み直してハッシュを作ることになる。 この処理の負荷が大きいため、処理時間が長くなるのだと思われる。 | ||
プロバイダは、 あまり負荷の高いCGIをサーバに置くことを禁止している。 それほどでもないとは思うが、 このCGIは外部に公開しないでローカル環境だけで実行していたのだ。 | ※ ここでは RIMNET のこと。 | |
こういう場合、検索用のサーバを立てるのが常識的な手法である。 データファイルはサーバを起動した時に一度だけ読み込む。 後はクライアントからリクエストがある毎に検索処理だけ行えばよい。 考え方は簡単なのだが、 実際にそのようなサーバを立ち上げるのは簡単なのだろうか? そこで思いついたのがサーブレットである。 サーブレットって何? という方はJava系の本を見てください。 既にVAIOでapacheが動いているのだから、 それと同じ程度の手間をかければtomcatという定番のサーブレットエンジンを起動することができる。 後は、サーブレットの検索プログラムを書けばok。 ここで情けない大問題が発生する。 サーブレットの作り方が全然分からないのだ。 しかもJavaだ。 何年も使ったことがない。 まさに大問題でしょ? これが苦労して、つい最近、 何とかサーブレットを動かすのに成功した。 コードは600行程度だから、そんなに大規模でもない。 Javaって何年ぶり? というような状況なので、 超基本的な理解が欠落していて、なかなか完成しなかったのである。 というか、未だによく分かっていない。 例えば、C的にいえば、List 1 のような構造体を作る場合を考えてみる。 この構造体を使ったリストか配列を作り、 URL を strcmp して検索、という感じの処理を作りたい。 定番ですね。 ---- List 1 ---- struct element { char *URL; char *title; int status; }; ---- List 1 end ---- 問題は、Javaでこれをどう実装するかだ。 それを考え始めると、かなり基本的なレベルでコケてしまうのだ。 素人がどれだけ奇妙な発想をするか、という実例になりそうなので、 ちょっとコケた例を紹介してみる。 なお、この後の説明はJava的にウソがあるかもしれないから、 素人が書いていると思って読んでください。 | ||
Javaは1にも2にもクラスの設計なのだ。 ということで、先程紹介したC的構造体に対応するクラス、 UrlElementを定義する。 次に、このクラスをリストとして扱うUrlListというクラスを定義する。 UrlListはLinkedListを継承することにする。 なぜArrayListを継承しなかったのか? 実はどっちが適切か分からなかったので1/2の確率でこっちになったのだ。 余談はさておき、UrlListクラスに、UrlElementに対応したaddメソッドを追加する。 | ※ 今だと ArrayList にするか、 あるいは、単なる HashSet にするか。 | |
このaddメソッドは、リスト全体がURLでソートされた状態になるように、 適切な位置を探して追加するという処理にしてある。 いわゆるバカサーチだとちょっと面白くないので、小技を使ったわけだ。 ただ、そういうaddのやり方がJavaのクラスで既にありそうなものだが…?、 | ※ Comparator を実装すればいい。 | |
検索機能は、UrlListに対してindexOf(String s); というメソッドをoverrideして、 指定した文字列と同じ文字列のURLを持つ要素がリストにあったら、 そのindexを戻すようにした。 ここまでの処理は、おおまかに言って List 2 のような状況になっている。 これがJava的に正しいかとかエレガントかという点については一切知らない。 | ||
---- List 2 ---- class UrlList extends LinkedList { public boolean add(UrlElement u) { int index; // 実際のコードでは、ここで、どこに突っ込むかを決める super.add(index, u); // index が突っ込む位置 return true; } public int indexOf(String s) { int h; // 実際のコードでは、indexを検索してhに入れる return h; } } ---- List 2 end ---- | ※ 今みても何やってるかさっぱり分からん。 単に LinkedList に add すればいいじゃん、みたいな。 | |
こうやって作ったクラスを使えば、List 3 のようにして、リストに情報を追加することができる。 ---- List 3 ---- UrlElement u = new UrlElement(url, title); URLList.add(u); ---- List 3 end --- で、どこでコケたかというと、 こうやって得た index を使って UrlElement を取り出す方法が分からなかったのだ。 結局、List 4 のようにした。 ---- List 4 ---- UrlElement o = (UrlElement) URLList.get(i); ---- List 4 end ---- | ||
ここまで書いても、一体どこでコケたか分からないでしょ? get が返す Object を (UrlElement) のようにダウンキャストすればいい、 ということが分からなかったのである。 しかも、これを発見するのに何日かかった分からない。 というか、今でもよく分かってないような気がする。 実際はこのコードは「Cだったらこうかな」とか想像して、 試しにやったら動いたので、そのまま使っているのだ。 つまり無茶苦茶。 ま、業務レベルの話じゃないから、 無茶で動くコードと正しいが動かないコードのどちらがいいか、考えるまでもない。 | ※ どちらもよくない。 | |
そういうレベルでサーブレット作って動かすというのもちょっと怖いが、 Javaが偉大なのは、それでもとりあえず動いているということだ。 気になる実行速度だが、 perl+CGIだと10~20秒かかっていた検索処理が、 2回目からの検索は一瞬で表示が出るようになって、大成功。 もちろん、初回起動時だけは、リストを構築する処理で20~30秒程度かかるようだ。 | ※ 同じようなプログラムが今も動いているが、 そちらは MySQL + Hibernate で実装してある。 | |
§ このプログラムをさらに進化させれば、 他サイトのリンクページのURLを指定して、読み込ませることができる。 あるページからリンクされているサイトをチェックする機能になる。 言い換えると、あまりよい表現ではないが、 指定したページのリンク情報を根こそぎ盗めることになる。 となると、今度は権利の問題が出てきそうだ。 もちろん「根こそぎ」といっても、単にコピーするわけではない。 大抵の場合、 リンクページに出ているサイトの半分以上が、 既にこちらの一覧リストに登録されているのである。 しかも、そういうリンクページは、こちらのデータベースに比べてデッドリンクが多い。 最初にデッドリンク率が1~2%と書いたが、 通常、この手のリンクページのデッドリンク率は、 1%だと物凄く少ない方で、 多い時は半分以上デッドリンクという場合もあるのだ。 ここでひらめきました。 現在使っているサーブレットには、デッドリンクの検出機能がある。 サイトの移転情報を登録しておけば、移転先を表示するのだ。 画像を多用するサイトは特に容量の制限が厳しいため、引越しが結構よくあるのだ。 では、このサーブレットを「重複チェック」ではなく「移転先チェック」 に改造して公開したら? 移転先の確認は超面倒なものだし、ちょっと油断すると行方不明になってしまう。 ちなみに、一覧リストの7000サイト中、1000程度は行方不明または移転したという情報なのである。 「移転先チェック」サーブレットを使うと、 そのページにあるリンク情報を一覧リストに照らし合わせてチェックするのだから、 当然、そのページのURLは情報としてgetできることになる。 つまり、移転先チェックサイトを開けば、 リンクページを持っている人たちが自分から検査しに来てくれて、 その結果未登録サイトがどんどん集まって…。 | ||
なんて甘い話はないか、やっぱり。 でもまあ、そのうち試してみたいと思う。 | ※ とんでもない。 やはり甘かった。 未登録サイトが集まりすぎて収拾が付かなくなってしまったのである。 |
(C MAGAZINE 2001年5月号掲載)
内容は雑誌に掲載されたものと異なることがあります。
修正情報:
2006-03-11 裏ページに転載。
(C) Phinloda 2001-2006, All rights reserved.