今月14日に作ってみたHyperEstraierを利用した青空文庫全文検索kwic表示をウェブブラウザで利用出来るようにしてみた(つもりだった)。ファイル名を表示させようとするとどうしても14日に示したような結果になってしまう。日本語のファイル名なのが原因だ。ファイルは5000個もあるので、一つ一つアルファベットのみからなるファイル名に変更するのは不可能だ。少なくとも私にとっては。エスケープされた文字列のようなのだが、元に戻す方法が判らない。文字コードもUTF-8ではないようなので、iconvとunescapeを組み合わせていろいろ試してみたがどうしても駄目。escapeされているならこのまま使ってみようと思って、これを表示させるのではなく、リンクにしてしまった。行末の出現行数値をクリックすると、目的のファイルが別窓で表示されるようにしてみたところ、うまくいった。納得できないけれど、まあいいや。「万難を」を検索してみた結果は以下のとおり。ファイルはShift_JISなので、一回目の表示は文字化けしてしまう(Safariではそうなのだが、FireFoxでは大丈夫だった)。
リンクをクリックしないと作品名・著者名が判らないなど、不便である。近いうちになんとかしたい。
Kwicである。Quickではない。Keyword in contextである。
さて、今日はRubyで。XMLの扱い方がなかなか判らずに苦労した。最初はどうして最初の一件しか表示されないのだろう、どうやったら全件表示するのだろうと悩んだ末に、自分で検索件数を1に指定していた。相変わらず莫迦である。
<%
$>.content_type = 'text/html; charset=UTF-8'
%>
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="yhkwic.css" />
<title>Yahoo KWIC</title>
</head>
<body>
<form action="yhkwic.rhtml">
Keyword:<input name="Keyword">
<input type=submit value="Search">
</form>
<%
require 'cgi'
require 'net/http'
require 'rexml/document'
require 'uri'
cgi = CGI.new
if cgi['Keyword'] == nil
puts "Keyword欄に検索したい語を入力してください。"
elsif
cgi['Keyword'] == ""
puts "Keyword欄に検索したい語を入力してください。"
else
print "<hr />\n<ol>\n"
word = Regexp.new(cgi['Keyword'])
url = URI.escape("http://api.search.yahoo.com/WebSearchService/V1/webSearch?
appid=yahooappid&query=#{word}&results=50")
begin
res = Net::HTTP.get_response(URI.parse(url))
results = REXML::Document.new(res.body)
results.root.each_element do |result|
summary = result.elements[2]
click = result.elements[4]
line = " " + summary.text
position = line.index(word)
if position != nil
print "<li><a href='"
print click.text
print "' target='_blank'>#</a> "
res = line[position-25, 50+word.to_s.length]
res.grep(word){|kwic|
print kwic.gsub(word){|s| "<kywd>" + s +
"</kywd>"}.gsub(/ /,' ')
}
print "</li>"
end
end
rescue
print "<br />Connection error."
end
end
%>
</ol>
<hr />
</body>
</html>
yahooappidの箇所は、自分のYahoo APIのIDを入れる場所なのでそのまま使わないように。結果表示の上限は50件にしています。実際に表示される件数が50よりも少なかったら、Summaryに検索に使った語が含まれていなかったのかも知れません。そのかわりTitleに含まれていたのかも。ときどき、理由は判りませんがエラーが出ることがあります。実際に使えるようにしてみたのが、ここ。#をクリックすると該当ページが別窓で表示されます。検索には欧文のみ使用でいます。日本語を入れても結果には何も表示されないでしょう。ところで、PHP版にも同じ語を入れて検索してみると、検索結果が違うのはなぜだろう。表示順の指定がちがうのだろうか。指定なんかしただろうか。検索語をYahoo.comに送って、XML形式で帰ってきた結果のSummaryの部分を検索語を中心に配置されるようにしているだけです。このところずっと作ってきたものとほとんど変わらないので、説明は不要でしょう。
Yahoo! Developer NetworkのRuby Developer Centerを見て少し試してみたのだが、どうもうまく結果が取得できない。あれこれ試行錯誤して、何とか検索結果を受けとるところまで漕ぎ着けたが、XMLをどう扱ったらいいのか判らないので、今日はそれまで。
ところで、私のubuntuでは、rhtmlがpublic_htmlの第一層にないと(フォルダの中に入っていると)GETで単語を次のページに送れないのだ。それが五回に一回くらいうまく行くのが不思議なところだ。
<?php
$key = $_POST['word'];
$limit = $_POST['limit'];
if ($key==""){
echo "検索語を入れて、<strong>Search</strong>ボタンを押してみてください。
検索は<a href='http://www.yahoo.com/'>Yahoo!</a>のウェブサービス(API)を利用します。<br />";
}else{
echo "<hr /> 検索語(欧文の場合は右矢印)をクリックすると該当記事が別窓で表示されます。<ol>";
$url="http://api.search.yahoo.com/WebSearchService/V1/webSearch?
appid=yahooappid&query=".urlencode($key)."&results=".$limit;
$xml = file_get_contents($url);
$xs = simplexml_load_string($xml);
$len=strlen($key);
foreach ($xs->Result as $value){
$summary=$value->Summary;
$summary=" ".$summary;
$result = strpos($summary,$key);
if ($result == true){
$sentence=substr($summary,$result-25,$len+58);
$sentence=preg_replace("/\n/"," ",$sentence);
$sentence=str_replace($key,"<em>".$key."</em>",$sentence);
$sentence=str_replace(" "," ",$sentence);
echo "<li><a href='".$value->ClickUrl."' target='_blank'><em>→</em></a>";
echo $sentence."</li>";
}
}
echo "</ol>";
}
?>
一部だけ、抜粋。PHPでできたんだから、いまさらRubyで作らなくてもいいかなという気分になってきている。
さて、PubMed検索シリーズの最後は単語の集計である。WebLSDでの単語集計は、昨日作った頻度別の並べ替えに近い。今日作るのは、関心のある単語で検索したら、その論文の要旨にはどのような単語がどれくらい使用されているのかを全部数え上げようというものだ。この単語の種類と頻度をさまざまなキーワードで検索した結果を使って解析すると、単語同士の結びつきの組合わせと強さが判るのである。そういうことはいろいろなところで利用されている方法である。
まずは、最初の検索結果の画面の下に、頻度集計用のリンクを追加するために、この一行を挿入。
print "<td style='background-color:lightgray'rowspan='2'><a href='pmkwic3.rhtml?kwrd=#{cgi.params['Keyword'][0]}' target='_blank'>集計</td>"
これで下のようなリンク表になる。
<%
$>.content_type = 'text/html; charset=UTF-8'
%>
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="pmkwic.css" />
<title>PubMed KWIC</title>
</head>
<body>
<%
require 'bio'
require 'cgi'
cgi = CGI.new
count = Hash.new(0)
keywords = cgi.params['kwrd'].to_s.gsub(/_/,'-')
options = {
'mindate' => '1996',
'maxdate' => '2006',
'retmax' => 3000,
}
def ab(str = nil)
@abstract ||= str
lines = []
lines << @abstract
return lines.join("\n\n")
end
text = ""
entries = Bio::PubMed.esearch(keywords, options)
Bio::PubMed.efetch(entries).each do |entry|
biomed = Bio::MEDLINE.new(entry)
abstract = biomed.ab
text << abstract + " "
end
words = text.gsub(/\.|,|:|;|\"|\'|\?|\(|\)/,'').split
words.each{|word|
count[word] += 1
}
print "<div align='center'><table border='0' cellpadding='15'><tr>"
print "<td style='background-color:lightgray'>頻 度 順</td><td
style='background-color:lightgray'>アルファベット順</td></tr><tr>"
print "<td style='background-color:beige'><ol>"
count.sort{|a,b|
a[1] <=> b[1]
}.reverse.each{|key, value|
print "<li>#{key}: #{value}</li>\n"
}
print "</ol></td><td style='background-color:beige'><ol>"
count.sort.each{|key, value|
print "<li>#{key}: #{value}</li>\n"
}
print "</ol></td></tr></table></div>"
%>
<hr />
</body>
</html>
単語集計のときには引用符とかピリオド、コロンなどは消してから数えている。大文字小文字を区別するので文頭で大文字になったものは別に集計されてしまう。気に入らない人は自分で検討して下さい。私は文頭かどうかを知りたいことがあるから、それはこのままにしておきたい。ピリオドを消してしまったので、文末かどうかの識別が困難になった。試してみるとちゃんと数え上げてくれて、以下のような結果が出た。OpenOffice.orgが使えなくなってしまったことが前にもあって、libGL.so.1.2を入れ替えればいいことを思い出し、やってみたらちゃんと動くようになった。
どうしてelseifが使えないんだろうと思っていたら、Rubyではelsifだったと今日初めて知った。驚いた。elseifでもelse ifでもなく、elsifだとは。
さて、用例検索を前後の語(すなわち共起する語である)の出現頻度の高い順に並べかえてみよう。最初のページには、
print "<div align='center'><table border='0' cellpadding='15'><tr>" print "<td stype='background-color:grey'>ABC順</td>" print "<td style='background-color:beige'><a href='pmkwic2.rhtml? pos=wb2;ord=abc' target='_blank'>2語前でソート</td>" print "<td style='background-color:beige'><a href='pmkwic2.rhtml? pos=wb1;ord=abc' target='_blank'>1語前でソート</td>" print "<td style='background-color:beige'><a href='pmkwic2.rhtml? pos=wf1;ord=abc' target='_blank'>1語後でソート</td>" print "<td style='background-color:beige'><a href='pmkwic2.rhtml? pos=wf2;ord=abc' target='_blank'>2語後でソート</td>" print "</tr><tr>" print "<td stype='background-color:grey'>頻度順</td>" print "<td style='background-color:beige'><a href='pmkwic2.rhtml? pos=wb2;ord=frq' target='_blank'>2語前でソート</td>" print "<td style='background-color:beige'><a href='pmkwic2.rhtml? pos=wb1;ord=frq' target='_blank'>1語前でソート</td>" print "<td style='background-color:beige'><a href='pmkwic2.rhtml? pos=wf1;ord=frq' target='_blank'>1語後でソート</td>" print "<td style='background-color:beige'><a href='pmkwic2.rhtml? pos=wf2;ord=frq' target='_blank'>2語後でソート</td>" print "</tr></table></div>"というのを加えて、並べ替えの種別と、並べ替えに使う語の位置を次のページに送信するようにする。それを受けたら、アルファベット順か頻度順かで表示方法を変えるようにすればいい。こんな感じに:
<%
$>.content_type = 'text/html; charset=UTF-8'
%>
<html>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="pmkwic.css" />
<body>
<ol>
<%
require 'mysql'
require 'cgi'
cgi = CGI.new
position = cgi.params['pos'].to_s
srtordr = cgi.params['ord'].to_s
my = Mysql::new("host", "userid", "password", "paperdb")
if srtordr == 'abc'
res = my.query("select kw, sentence, #{position}, pmid from pmkwic
order by #{position}")
res.each do |row|
kwic2 = row[1].gsub(/#{row[0].gsub(/_/," ")}/,"<kywd>#{row[0]
.gsub(/_/," ")}</kywd>")
if /\A\w+\Z/ =~ row[2]
kwic2 = kwic2.gsub(/#{row[2]}/,"<mrkd>#{row[2]}</mrkd>")
end
print "<li>"
print kwic2.gsub(/ /,' ')
print " <a href='http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?
db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=" +
row[3] + "&' target='_blank'>" + row[3] + "</a>"
print "</li>\n"
end
elsif srtordr == "frq"
res = my.query("select #{position},count(*) as number from pmkwic
group by #{position}
order by number desc")
res.each do |row|
res2 = my.query("select kw,#{position},sentence,pmid from pmkwic where
#{position} = '#{row[0]}'")
res2.each do |row2|
kwic2 = row2[2].gsub(/#{row2[0].gsub(/_/," ")}/,"<kywd>#{row2[0]
.gsub(/_/," ")}</kywd>")
if /\A\w+\Z/ =~ row2[1]
kwic2 = kwic2.gsub(/#{row2[1]}/,"<mrkd>#{row2[1]}</mrkd>")
end
print "<li>"
print kwic2.gsub(/ /,' ')
print " <a href='http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?
db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=" +
row2[3] + "&' target='_blank'>" + row2[3] + "</a>"
print "</li>\n"
end
end
end
my.close
print "</ol>"
print "<div align='center'><table border='0' cellpadding='15'><tr>"
print "<td style='background-color:lightgray'>ABC順</td>"
print "<td style='background-color:beige'><a href='pmkwic2.rhtml?
pos=wb2;ord=abc'>2語前でソート</td>"
print "<td style='background-color:beige'><a href='pmkwic2.rhtml?
pos=wb1;ord=abc'>1語前でソート</td>"
print "<td style='background-color:beige'><a href='pmkwic2.rhtml?
pos=wf1;ord=abc'>1語後でソート</td>"
print "<td style='background-color:beige'><a href='pmkwic2.rhtml?
pos=wf2;ord=abc'>2語後でソート</td>"
print "</tr><tr>"
print "<td style='background-color:lightgray'>頻度順</td>"
print "<td style='background-color:beige'><a href='pmkwic2.rhtml?
pos=wb2;ord=frq'>2語前でソート</td>"
print "<td style='background-color:beige'><a href='pmkwic2.rhtml?
pos=wb1;ord=frq'>1語前でソート</td>"
print "<td style='background-color:beige'><a href='pmkwic2.rhtml?
pos=wf1;ord=frq'>1語後でソート</td>"
print "<td style='background-color:beige'><a href='pmkwic2.rhtml?
pos=wf2;ord=frq'>2語後でソート</td>"
print "</tr></table></div>"
%>
<hr />
</body>
</html>
検索結果はこんな感じ。size standardという語を検索して、一つ前の語の頻度順に並べたもの。一つ前の語が「a」のときに、ちょっとみっともない着色がなされていることが一目瞭然である。下のリンクを押せば、何度でも好きな並べ替えをしてくれる。
さて、PubMed検索のキーワード検索の結果を、キーワードの前後の単語に従って並べ替えてみよう。今回はMySQLを使うことにするので(使わなくてもできるような気もするが、いろいろな並べ替えには便利なのでこれを使う。PHPでやってみたときの経験からそう判断した)、Ruby/MySQLをインストールする。
まずMySQLにデータベースとテーブルを作る。テーブルには、最初の検索結果の一行、pmid、検索語(keyword)、検索語の一つ前の単語、二つ前の単語、一つ後の単語、二つ後の単語を収納する場所を用意する。そのテーブルをpmidtableと名付けよう。データベースはpaperdbという名である。もちろん、これらの名前は好き勝手につけて構わない。
my = Mysql::new("localhost", "userid", "password", "paperdb")
init = my.query("delete from pmidtable")
とやって、データベースに接続準備をして、pmidtableを空っぽにする。このテーブルは何度も使い回すのだ。もしも一般公開するなら、そうはいかないだろうが、これは自分専用だからいいのだ。
昨日と同様に一回目の検索結果を表示すると同時に、文をばらばらにしてテーブルに収納する。こんな感じ:
devided = kwic.gsub(/\"|\'/,"").split(/#{word}/)
bwords = devided[0].gsub(/\.|,|:|;|(|)/,"").strip.split(/ /).reverse
fwords = devided[1].gsub(/\.|,|:|;|(|)/,"").strip.split(/ /)
insrt = my.query("insert into pmidtable (pmid,kw,sentence,wb1,wb2,wf1,wf2) values ('#{pmid}','#{cgi.params['Keyword'][0]}','#{my.quote kwic}','#{bwords[0]}','#{bwords[1]}','#{fwords[0]}','#{fwords[1]}')")
ここで、kwが検索に使った語、wb1が一つ前、wb2が二つ前、wf1が一つ後、wf2が二つ後の単語である。並べ替えるときに、ピリオドやコンマを含めるかどうかで悩む。残しておいた方が文末で使われるとかいう情報が得られていいのではないかと思ったが、今回はピリオド、コンマ、コロン、セミコロン、括弧を除外することにした(が、括弧は実際には除外されなかった。なぜだろう)。最後に並べ替えの結果を評するページへのリンクを置く。ここでURLに送り込みたい文字列をつけて、次のページで受け取る方法がなかなか判らず時間がかかってしまった。出来上がったのは、以下のようなものである。
<%
$>.content_type = 'text/html; charset=UTF-8'
%>
<html>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="pmkwic.css" />
<body>
<%
require 'mysql'
require 'bio'
require 'cgi'
cgi = CGI.new
%>
<form action="pmkwic.rhtml">
Keyword:<input name="Keyword">
<input type=submit value="Search">
</form>
<%
if cgi.params['Keyword'][0] == nil
puts "Keyword欄に検索したい語を入力してください。"
else
print "<hr />\n<ol>"
word = Regexp.new(cgi.params['Keyword'][0].gsub(/_/,' '))
keywords = cgi.params['Keyword'][0].gsub(/_/,'-')
options = {
'mindate' => '1996',
'maxdate' => '2006',
'retmax' => 3000,
}
def pmid
return @pubmed
end
def ab(str = nil)
@abstract ||= str
lines = []
lines << @abstract
return lines.join("\n\n")
end
my = Mysql::new("localhost", "userid", "password", "paperdb")
init = my.query("delete from pmidtable")
entries = Bio::PubMed.esearch(keywords, options)
Bio::PubMed.efetch(entries).each do |entry|
biomed = Bio::MEDLINE.new(entry)
pmid = biomed.pmid
abstract = " " + biomed.ab
startpos = 0
while position = abstract.index(word,startpos) do
if position != nil
kwic = abstract[position-42, 80+keywords.length]
result = kwic.gsub(word){|s| "<key>" + s + "</key>"}
result = result.gsub(/ /,' ')
print "<li>" + result
print " <a href='http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve
&dopt=AbstractPlus&list_uids=" + pmid + "&' target='_blank'>" + pmid + "</a>"
print "</li>\n"
devided = kwic.gsub(/\"|\'/,"").split(/#{word}/)
bwords = devided[0].gsub(/\.|,|:|;|(|)/,"").strip.split(/ /).reverse
fwords = devided[1].gsub(/\.|,|:|;|(|)/,"").strip.split(/ /)
insrt = my.query("insert into pmidtable (pmid,kw,sentence,wb1,wb2,wf1,
wf2,sentence_b,sentence_f) values ('#{pmid}','#{cgi.params['Keyword'][0]}',
'#{my.quote kwic}','#{bwords[0]}','#{bwords[1]}','#{fwords[0]}','#{fwords[1]}',
'#{my.quote devided[0]}','#{my.quote devided[1]}')")
startpos = position + 1
end
end
end
my.close
print "</ol>"
end
%>
<div align="center"><table border="0" cellpadding="15"><tr>
<td style="background-color:beige"><a href="pmkwic2.rhtml?wb2" target="_blank">2語前でソート</td>
<td style="background-color:beige"><a href="pmkwic2.rhtml?wb1" target="_blank">1語前でソート</td>
<td style="background-color:beige"><a href="pmkwic2.rhtml?wf1" target="_blank">1語後でソート</td>
<td style="background-color:beige"><a href="pmkwic2.rhtml?wf2" target="_blank">2語後でソート</td>
</tr></table></div>
<hr />
</body>
</html>
ちょっと長くなってしまった。さて、pmkwic2.rhtmlというファイルへと情報を送ったら、自分の調べたい位置の語で並べ替えて表示するのは、MySQLがやってくれるので簡単だ。
<%
$>.content_type = 'text/html; charset=UTF-8'
%>
<html>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="pmkwic.css" />
<body>
<ol>
<%
require 'mysql'
srtw = ENV['QUERY_STRING']
my = Mysql::new("localhost", "userid", "password", "paperdb")
res = my.query("select kw, sentence, #{srtw}, pmid from pmidtable order by #{srtw}")
res.each do |row|
kwic2 = row[1].gsub(/#{row[0].gsub(/_/," ")}/,"<zzz>#{row[0].gsub(/_/," ")}</zzz>")
if /\A\w+\Z/ =~ row[2]
kwic2 = kwic2.gsub(/#{row[2]}/,"<qqq>#{row[2]}</qqq>")
end
print "<li>"
print kwic2.gsub(/ /,' ')
print " <a href='http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?
db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=
" + row[3] + "&' target='_blank'>" + row[3] + "</a>"
print "</li>\n"
end
my.close
%>
</ol>
<div align="center">
<table border="0" cellpadding="15"><tr>
<td style="background-color:beige"><a href="pmkwic2.rhtml?wb2">2語前でソート</td>
<td style="background-color:beige"><a href="pmkwic2.rhtml?wb1">1語前でソート</td>
<td style="background-color:beige"><a href="pmkwic2.rhtml?wf1">1語後でソート</td>
<td style="background-color:beige"><a href="pmkwic2.rhtml?wf2">2語後でソート</td>
</tr></table>
</div>
<hr />
</body>
</html>
あとは何度でもソートのやり直しができる。ExoIIIという語で検索して、一つ前の語で並べ替えた結果を例として示す(クリックすると大きくなります)。
WebLSDと比べてみると、さまざまな違いに気づく。WebLSDは検索結果数が少ないのに、論文の発表年代は幅広い。PubMedで検索するときには科学的なキーワードでないと検索結果がうまく出てこないが、WebLSDでは一般的な単語でもちゃんと結果が表示される。これは、英語の用例を検索するという目的のためには不可欠な条件である。私の作ったものはその根本的なところで失格である。しかし、それは私のせいではないのではなかろうか。PubMedの検索方式の特徴ではないだろうか。WebLSDは、PubMedのabstractなどの情報を自前のサーバーに保存しているのではないだろうか。各年代からランダムに抽出して。だから、metagenomeという語で検索しても、「該当する表現がありませんでした」という結果が返ってくるのではないだろうか。勝手に保存したらいけないのではないかということを糾弾するつもりはまったくない。なくなったら私も困るし。
次は、並べ替えをアルファベット順ではなく、頻度順に表示するのにも挑戦してみよう。一番良く使われる言い回しは何かを知りたいことも多いからだ。
Yahoo! Developer NetworkにRuby Developer Center発足のお知らせが掲載されていた。Rubyを使ってやりとりする方法が紹介されているようだけど、まだ記事は少ないような気がする。そのうち、少し真面目に検索結果をkwic表示するサイトを作ってみようか。前に試しに作ってみたものがあるけど、あまり出来が良くなくて。
さて、昨日作ったもののhtml化を行ないました。キーワードをcgiで送り込むことと、結果表示をhtmlで行なうとともに、該当論文のリンク先を作ることが、追加されたところ。検索オプションとして対象を1996年から2006年の10年に限定してみた。これは、入力画面から設定できるようにした方がいいだろう。スペースはいくらあってもWebブラウザでは1個分にしかならないので、 に変換すると同時に、検索語に色を付ける。このところが実は厄介で、タグの中のスペースが変換されてしまうと思うような結果が得られないことになる。そこでちょっと強引だが、<key>というタグを作ってしまい、cssでフォントを指定した。それからフレーズとして検索したい場合は、単語間にアンダーバーを入れることにする。スペースで区切られたものは、AND検索になってしまうから。これをPubMed検索にはハイフンに置き換えて送りだし、kwic表示のためにはスペース置き換えて利用する。そうしないと、本当にハイフンを含む語の検索ができなくなってしまうから。こんな簡単なものでも結構苦労したのだ。一応できあがったのが以下のようなもの(リンクのところは本当は一行で)。
<%
$>.content_type = 'text/html; charset=UTF-8'
%>
<html>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="pmkwic.css" />
<body>
<%
require 'bio'
require 'cgi'
cgi = CGI.new
%>
<form action="pmkwic.rhtml">
Keyword:<input name="Keyword">
<input type=submit value="Search">
</form>
<%
if cgi['Keyword'][0] == nil
puts "Keyword欄に検索したい語を入力してください。"
else
print "<hr />\n<ol>"
word = Regexp.new(cgi['Keyword'][0].gsub(/_/,' '))
keywords = cgi['Keyword'][0].gsub(/_/,'-')
options = {
'mindate' => '1996',
'maxdate' => '2006',
'retmax' => 3000,
}
def pmid
return @pubmed
end
def ab(str = nil)
@abstract ||= str
lines = []
lines << @abstract
return lines.join("\n\n")
end
entries = Bio::PubMed.esearch(keywords, options)
Bio::PubMed.efetch(entries).each do |entry|
biomed = Bio::MEDLINE.new(entry)
pmid = biomed.pmid
abstract = " " + biomed.ab
startpos = 0
while position = abstract.index(word,startpos) do
if position != nil
kwic = abstract[position-42, 80+keywords.length]
result = kwic.gsub(word){|s| "<key>" + s + "</key>"}
result = result.gsub(/ /,' ')
print "<li>" + result
print " <a href='http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&
dopt=AbstractPlus&list_uids=" + pmid + "&' target='_blank'>"
+ pmid + "</a>"
print "</li>\n"
startpos = position + 1
end
end
end
print "</ol>"
end
%>
<hr />
</body>
</html>
これで検索してみたときの結果例を示す。bacterial communityという語句を検索したときのものである。
またRuby on Railsの本を買ってしまった。薄いけれども判りやすそうな気もする。しかし、英語だ。
![]() |
Amazon.co.jp Ruby on Rails: Up And Running Oreilly (2006/08) ¥ 3,120 (税込) 在庫あり |
WebLSDという素敵なサイトがあって、ここの共起検索が素晴らしい。何年も前から利用している。PubMed検索のキーワード検索の結果として得られた論文の要旨の文章が、kwic検索の結果となって表示される。前後の語で並べ替えたりできるところがいいのだ。ということで、今日からこれを自分で作ってみることにしよう。
まずはコマンドラインで使うものから始める。前に保存してあるテキストファイルでkwic検索するものをすでに作っているから、BioRubyでPubMedの論文を検索した結果に適用してみる。
#! /usr/bin/ruby
require 'bio'
keywords = ARGV[0]
if ARGV[1] == nil
word = ARGV[0]
else
word = Regexp.new(ARGV[1])
end
options = {
'retmax' => 1000,
}
def pmid
return @pubmed
end
def ab(str = nil)
@abstract ||= str
lines = []
lines << @abstract
return lines.join("\n\n")
end
entries = Bio::PubMed.esearch(keywords, options)
Bio::PubMed.efetch(entries).each do |entry|
biomed = Bio::MEDLINE.new(entry)
pmid = biomed.pmid
abstract = " " + biomed.ab
startpos = 0
while position = abstract.index(word,startpos) do
if position != nil
kwic = abstract[position-42, 84]
print kwic.sub(word){|s| " " + s + " "}
print " "
p pmid
startpos = position + 1
end
end
end
とまあこんな具合である。いきなりできたように見えるが、何時間もかかっている。BioRubyは使い方の詳しい説明がどこにもないから大変だ。そのうえ、Rubyがまだよく判っていないのだから、なおさらである。このファイルをpubmed.rbとかいう名前で保存し、検索したい語を指定する。今回は、PubMedで検索する語と、Kwic検索の語を別に指定できるようにしてみた。例えば、ruby pubmed.rb exonucleaseIII restrictionとすれば、exonucleaseIIIという語を要旨に書いていた論文の要旨の文の中でrestrictionという言葉がどのように使われているかを調べられるというわけだ。そんなことをしたがる人がいるかどうかは私は知らない。結果は、こんな感じ。
$ ruby pubmed.rb ExonucleaseIII restriction
The type I DNA restriction and modification systems of en "9799635"
as cloned in E. coli was cleaved with the restriction endonucleases Eco RI, Hind III "788917"
ns. Total sea urchin DNA was cleaved with restriction endonucleases, fractionated on "788917"
コマンドラインではやはり寂しい。せっかくpmidを書きだしているのだから、これをクリックしたら、PubMedの該当ページが表示されるようにしたりしたいと思うのが人間というものであろう。
今日届いたPHP Cookbook, 2nd Editionは、760ページに及ぶ大著で、字がびっしり詰まっていて嬉しくなってしまう。もちろん、コマンドラインでの利用についての記述もあり、$argvと$argcについても書いてあった。このCookbookシリーズは一体誰がそんなもの使うんだろうというような利用法も詳しく書いてあって本当に役立つのだ。第1版のときに買っていれば、かなりの時間の節約ができたかも知れないと思う。どうして$argv[0]がプログラム名なんだろうと思っていたのだが、phpという文字に続く文字列はすべてphpというコマンドに続く引数で、その最初のものがプログラム名に相当するという考え方らしい(本当か?)。
今日は予想外の事件が起きて、いろいろ試せず。
![]() |
Amazon.co.jp Php Cookbook 2nd Edition Oreilly (2006/08) ¥ 4,681 (税込み) 通常1~3週間以内に発送 |
PHPでもargvを使えると初めて知った。ほら、コマンドラインで何かさせるときに、コマンドの後ろに検索語とかファイル名とか書いてプログラムに知らせる、あれですよ。$argv[0]がプログラム名で、$argv[1]以降が引数、$argcが引数の数だといふ。全然知らなかった。これまで、結構PHPの本は読んできたのだけど、書いていなかったような気がする。確かにPHPはコマンドラインでの利用は少ないかも知れないが、これくらい教えてくれたっていいではないか。私はコマンドラインでも使うのだ。この頃は、Rubyなんか練習しているけれど、つい数週間前まではPHP一筋だったのだから。Rubyであれこれ試しているときに、どうしてPHPではこうやっていろいろな指示をプログラムに送り込めないのだろうと思っていたのだった。この貴重な情報を偶然見つけたのは、MySQLの本。素晴らしい本である。
![]() |
Amazon.co.jp MySQLクックブック〈VOLUME1〉 オライリージャパン (2003/11) ¥ 5,670 (税込み) 在庫あり |
ubuntuでだけrhtmlファイルがうまく動かず「ダウンロードしますか」という表示が出てしまう問題の解決法が載っているサイトを見つけた(しかし、そこのURLを記録していなかったので、もう二度と見つけられないのが残念)。解決法は、スクリプトの一番最初に、
<% $>.content_type = 'text/html; charset=UTF-8' %>を追加するというもの。charsetは自分の環境に合わせて。最初の$>.はよく判らない。なぜubuntuでのみ起こる現象なのかも判らないが、解決したらいいことにする。
さて、rhtmlファイルが動くようになったので、簡単なページを作ってみる。コメントを送って表示するというもの。
<html><head><title>eRuby test</title></head><body>
<form action="test3.rhtml">
コメント欄:<input name="comment"><br>
<input type=submit value="送信">
</form>
<%
require 'cgi'
cgi = CGI.new
if cgi['comment'][0] == nil
print "まだ何も入力していませんね。"
else
print "あなたがコメント欄に入力した値は、<strong>『"
print cgi['comment'][0]
print "』</strong>です。<br />"
end
%>
</body>
</html>
自分のコメントを送って、入力しているときのみその内容を表示しようと思ったわけだ。だから、最初にこのページを開くと、「まだ何も入力していませんね。」と表示される。ところが、何も入れずに送信ボタンを押すと「力した値は、『』です。」と表示されてしまう。そこで、elseif cgi['comment'][0] == ""などという条件を入れるとInternalServerErrorになってしまうのだ。単独でやれば大丈夫なのだが、elseifにすると駄目だ。何なんだ、これは。どういうことだかさっぱり判らないぞ。
Javascriptってのは訳が判らない。PHPとかRubyなら何となく判るのだが。しかし、今日はどうしても画像を移動させたくなった。今までひたすら文字情報だけを追究して、画像を忌み嫌っていたのだが、突然本の表紙画像を動かしたくなったのだ。わらわらと本の表紙画像が画面に現れたり、大量の表紙画像が上から降ってきたりしたら嬉しいのではないかと思ったのである。思ってしまったのだから、仕方がない。理由なんて知らない。調べてみると、画像を勝手に移動させたりするのはJavascriptという仕組みを使うらしい。PHPやRubyじゃできないのかと思ったが、どうやらできないらしい。何か参考書はないかと書棚を探してみると、『JavaScript & DHTMLクックブック』を持っているではないか。そういえば以前、買ってみたけれど訳が判らないので書棚に放り込んでおいたのだった。本の表紙画像を上から降らせるという例は載っていなかったが、直線上を動かすというのがあった。例は横の移動だったが、これを上から下にすれば、降ってくるようになるんじゃないかと本のページを睨みつけながら考えたわけだ。まずは素直に左から右へと動かしてみよう。
……
判らない、どうにも判らない。この呪文のような文字の羅列は何なのだ。繰り返し呪文を唱えること数時間。やっと呪文が効くようになった。Amazon.co.jpからダンセイニの本の(もちろんダンセイニでなくてもいいのだが)書影を取得して、それをクリックを合図に右から左へと動かすだけのしょうもないページである。このページに埋め込もうと思ったが、明日か明後日には位置が変わってしまうので面倒臭い。そこで、別ファイルとした。できあがったしょうもないページはこちら。15インチの画面上で、幅8割くらいのウィンドウで開いてみるとちょうどよい位置になると思う。あまり大きい画面だとみっともない動きになってしまう。LinuxのFirefox、MacOSXのSafariとFirefoxでしか動作確認をしていない。万が一、ちゃんと動かなくても、何の損もしないのでそんなにがっかりしないでいただきたい。そのうち、わさわら降らせますから(いや、できないかも)。
![]() |
Amazon.co.jp JavaScript & DHTMLクックブック オライリージャパン (2004/01) ¥ 4,830 (税込み) 在庫あり |
自動更新されていた! 昨夜のことでしたがね。河出書房新社近刊図書RSSのことです。次は何を作ってみようか。
なぜかエラーが出てインストールできなかったBioRuby 1.0.0は
% ruby install.rb config % ruby install.rb setup % sudo ruby install.rb installで問題なくインストール。あのエラーは何だったのだろう。これで気分が良くなったので、長い塩基配列のなかから特定の短い配列を見つけるスクリプトを作ってみる。grepでいいんじゃないかと思うかも知れないが、塩基配列なので、相補鎖も同時に調べないと意味がないのだ。今月4日のスクリプトをBioRubyを使って書き換えた(というよりも書き加えたと云った方がいいか)。
require 'bio'
q_seq = Regexp.new(ARGV[1])
data = ""
File.open(ARGV[0]){|file|
while line = file.gets do
if (/\A>/=~ line) == nil
line.chomp!
data.concat(line)
end
end
}
len = data.length
naseq = Bio::Sequence::NA.new(data)
c_seq = naseq.complement
startpos = 0
while position = naseq.index(q_seq,startpos) do
if position != nil
result = naseq[position-25, 60]
print result.sub(q_seq){|s| " " + s + " "}
print " "
p position
startpos = position + 1
end
end
startpos = 0
while position = c_seq.index(q_seq,startpos) do
if position != nil
result = c_seq[position-25, 60]
print result.sub(q_seq){|s| " " + s + " "}
print " c"
p len - position
startpos = position + 1
end
end
まず、FASTA配列の第一列の$ ruby testbio1.rb AE007317.fna ggagg..cctcc cccgtattccagcaggtgttctttt ggagggacctcc ggggacaggtaagactttgcttg 12847 agcaagcaaagtcttacctgtcccc ggaggtccctcc aaaagaacacctgctggaatacg c12859という結果が得られて、位置も合っている。数値が一致しないのは標的配列の塩基数分である。BioRubyの機能を使っているのは、「c_seq = naseq.complement」ということろだけ。ちょっと自分の探したい配列があるかどうかを見つけたいというときに使う手頃なものがなかったので、ちょっと嬉しい。私が気づいていないだけで、みんな知っているのがあるのかも知れないが、私はこれでとりあえずいい。ちなみにこれは、複数のFASTA配列を含むファイルを検索対象にするのには不向きです。
昨日書いたように、私のubuntuではrhtmlファイルをどうしてもerubyで実行してくれない。ところが、
# for Apache::ERubyRun
RubyRequire apache/eruby-run
# handle files under /eruby as eRuby files by eruby.
<Location /eruby>
SetHandler ruby-object
RubyHandler Apache::ERubyRun.instance
</Location>
# handle *.rhtml as eruby files.
<Files *.rhtml>
SetHandler ruby-object
RubyHandler Apache::ERubyRun.instance
</Files>
という記述をruby.loadの
<Location /~lege/eruby>
SetHandler ruby-object
RubyHandler Apache::ERubyRun.instance
</Location>
という設定を追加(legeはユーザ名)すると、目的どおりの動きをするようにできた。しかし、rhtmlにすると駄目。何なのだ。練習用だから、まあいいか。それにしても、ずいぶん時間を使ってしまった、こんなことに。
少しrubyにも慣れてきたから、erubyでウェブ・ブラウザに表示させることも始めようと決意しました。すっかり涼しくなった秋の朝のことでした。ubuntuはsynapticで簡単にerubyもmod_rubyもインストールできる。素晴らしい。ということでいとも簡単に両者ともインストール。apacheの設定を書き換えようと思って困った。どこに書けばいいのだ。Debian系は馴染がないのでよく判らないのだ。mod_ruby.loadに付け加えたのだけど、うまく動かない。端末ではerubyはちゃんと動くのでそちらの問題ではなく、もうapacheの設定だけだろう。apache2.confに書き込んでみたり、新たにmod_ruby.confなんてのを作ってみたりしたが、できない。ブラウザは画面を表示する代わりにファイルをダウンロードしますかと訊いてくる。もう何十回設定を書き換え、apacheをrestartしたことだろう。おかげで一日仕事が捗らなかった。
悔しいのでVine Linux 3.2にインストールしてみた。Vineでも両者ともSynapticでインストールできるが、Apacheはソースからインストールしているので、erubyのみSynaptic経由でインストール。mod_rubyはmodruby.netから1.2.6をダウンロードして、
./configure.rb --with-eruby --with-apxs=/usr/local/apache2/bin/apxs make make installとしたが、--with-erubyは不要だったようだ。httpd.confのLoadModuleがたくさん並んでいるところに、
<IfModule mod_ruby.c> RubyRequire apache/eruby-run <Location /eruby> SetHandler ruby-object RubyHandler Apache::ERubyRun.instance </Location> <Files *.rhtml> SetHandler ruby-object RubyHandler Apache::ERubyRun.instance </Files> </IfModule>というのを最後の方に追加した。今回は「<Location /eruby>」の部分は必要なかったが、まあいいや。 apache2を再起動し、問題なくrhtmlファイルが期待通りの画面をブラウザに提示したことを確認。
さて、今度はMacOSXである。こちらはerubyもmodruby.netから1.0.5をダウンロードしなければならない。通常通り、
./configure.rb make make installだけで簡単にインストール完了。次はmod_rubyだが、私は標準搭載のapacheではなくて、2.0系を別に入れて使用しているので、./configure.rbだけだと使っていないほうへインストールされてしまう。そこで、
./configure.rb --with-apxs=/Library/Apache2/bin/apxs make make installとしてインストール。httpd.confには、「LoadModule ruby_module modules/mod_ruby.so」と、
<IfModule mod_ruby.c>
RubyRequire apache/eruby-run
<Files *.rhtml>
SetHandler ruby-object
RubyHandler Apache::ERubyRun.instance
</Files>
</IfModule>
を追加(今度は後半だけ)。こちらもapacheを再起動して、問題なくrhtmlが提示する画面を確認できた。こんな簡単にできるのに、ubuntuはどうなっているんだ!
オレンジ色の瞬きにしたがっていくつかアップデート。再起動後にOpenOffice.org2が立ち上がらなくなってしまった。大事な書類を出さなければならないのに、書きかけの文書を完成させることができなくなってしまう。困る。仕方がないのでアップデートをしていない隣の部屋のubuntuを使って文書を完成させ、印刷した。もうVine 4.1が出たら乗り換えようかな。
何としてもMacOSXでHyperEstraierの検索結果をkwic表示したい。諦める訳にはいかない。私はそのためにPowerMac G5を買ったのではなかったか(そんなわけはない)。ところが、HyperEstraier附属のrubynativeやrubypureをコンパイルしようとしても、makeで、
can't find header files for ruby. make: *** [all] Error 1というエラーが出てしまう。header filesがないのかあ。ということで、ruby最新版(1.8.5)をソースからインストールすることに。
./configure --prefix=/usr make make test sudo make install make cleanと云われるがままに実行してみると、難なく成功。HyperEstraierも再インストールすることにした。qdbmがなぜかないので(前にインストールしたはずなのに!)これを先に再インストール。続いてHyperEstraier。これも成功。ところが、rubynativeは、make installのところで、
mkdir -p /usr/lib/ruby/site_ruby/1.8 ( cd src && cp -Rf estraier.so /usr/lib/ruby/site_ruby/1.8 ) cp: estraier.so: No such file or directory make: *** [install] Error 1というエラーが出てしまう。なぜ?
$ sudo make install /usr/bin/install -c -m 0755 estraier.bundle /usr/lib/ruby/site_ruby/1.8/powerpc-darwin8.7.0ということになりestraier.bundleというファイルがインストールされた模様。こんなことどこにも書いていないんですけど、どういうことなんでしょうか。
estcmd gather -il ja -sd casket /Library/Apache2/htdocs/aozoraこんな具合。量が多いので時間がかかる。ようやくインデックスの作成が終って、再度検索すると……できた! あっという間に5087個のファイルを検索して結果を出したではないか。
$ ruby estkwic.rb '水菓子' URI: file:///Library/Apache2/htdocs/aozora/%E3%81%AA%E3%81%A4%E3%83%BB%E5%A4%8F%E7%9B%AE%E6%BC%B1%E7%9F%B3%EF%BC%8896%EF%BC%89/%E5%A4%A2%E5%8D%81%E5%A4%9C.txt 被《かぶ》って、夕方になると 水菓子 屋《みずがしや》の店先 451 通らない時は、往来を見ないで 水菓子 を見ている。水菓子には 733 を見ないで 水菓子 を見ている。水菓子にはいろいろある。水蜜 760 と云っている。商売をするなら 水菓子 屋に限ると云っている。 1144 も、かつて銭《ぜに》を出して 水菓子 を買った事がない。ただ 1417 ょうと云って、女といっしょに 水菓子 屋を出た。それぎり帰っ 2339 URI: file:///Library/Apache2/htdocs/aozora/%E3%81%B2%E3%81%8F%E3%82%99%E3%83%BB%E6%A8%8B%E5%8F%A3%E4%B8%80%E8%91%89%EF%BC%8814%EF%BC%89/%E3%81%AB%E3%81%93%E3%82%99%E3%82%8A%E3%81%88.txt 座りなさいと手を取りて、あの 水菓子 屋で桃を買ふ子がござん 8774 URI: file:///Library/Apache2/htdocs/aozora/%E3%81%97%E3%81%BE%E3%83%BB%E5%B3%B6%E5%B4%8E%E8%97%A4%E6%9D%91%EF%BC%8832%EF%BC%89/%E5%AE%B6%EF%BC%88%E4%B8%8B%E5%B7%BB%EF%BC%89.txt 家の若主人が、東京に出て仮に 水菓子 屋を始めているとは。加 1230 。加《おまけ》に、若い細君が 水菓子 を売ると聞いた時は、榊 1308 話の様子では、普通《ただ》の 水菓子 を売る家の内儀《おかみ 1722 ゃ有りませんか。僕もネ、今の 水菓子 屋なぞはホンの腰掛です 3890本当は結果はもっと続く。ファイル名が見苦しいのはあとで考えよう。とにかく嬉しい。はいぱ〜えすとれいや〜〜〜と勝利の雄叫びをあげる私でした。
10日に閉じ忘れを訂正した、あれ。よく見たら無駄な一行が。「result.grep(word){|final|」なんて書いているけれども、indexで調べたい単語の位置を決めているのだから、その語が含まれるのは当然、全く不要な一行である。
word = Regexp.new(ARGV[1])
nuc = " "
File.open(ARGV[0]){|file|
while line = file.gets do
line.chomp!
nuc.concat(line)
end
}
startpos = 0
while position = nuc.index(word,startpos) do
result = nuc[position-25, 60]
print result.sub(word){|s| " " + s + " "}
print " "
p position
startpos = position + 1
end
これだけでいい訳ですね。
そもそもこれがやりたくてRubyに手を伸ばしたのでした。すっかり忘れていましたよ。HyperEstraierという素敵な全文検索システムがあって、Ruby用のライブラリがあるのです。ところが私が愛用していたPHPのはないので甚だ悔しい思いをしていた訳です。
どうしてHyperEstraier単独では不満で、Rubyと組み合わせたいかというと、Kwicである。HyperEstraier単独だと、調べたい語が含まれている文書を見つけるのにはいいのだが、調べたい単語の前後の状況を並べて判りやすく表示してくれる訳ではないからだ。だから、そんなに使用頻度の高くない言葉を含む文書を多数のファイルからHyperEstraierで抜き出して、それをRubyで整形して表示させたいのである。ubuntuでは簡単にHyperEstraierもRuby用ライブラリもインストールできる。HyperEstraierについてきた見本に、さっき訂正した整形スクリプトを組み合わせてこんなのを作ってみた。
require "estraier"
include Estraier
word = Regexp.new(ARGV[0])
# create the database object
db = Database::new
# open the database
unless db.open("casket", Database::DBREADER)
printf("error: %s\n", db.err_msg(db.error))
exit
end
# create a search condition object
cond = Condition::new
# set the search phrase to the search condition object
cond.set_phrase(ARGV[0])
# get the result of search
result = db.search(cond)
# for each document in the result
dnum = result.doc_num
for i in 0...dnum
# retrieve the document object
doc = db.get_doc(result.get_doc_id(i), 0)
next unless doc
# display attributes
uri = doc.attr("@uri")
printf("URI: %s\n", uri) if uri
title = doc.attr("@title")
printf("Title: %s\n", title) if title
# display the body text
doc.texts.each do |text|
contents = text.gsub(/(?:\r|\r\n|\n)\z/, "").gsub(/ /, "")
contents = " " + contents
startpos = 0
while position = contents.index(word,startpos) do
if position != nil
kwic = contents[position-42, 84]
print kwic.sub(word){|s| " " + s + " "}
print " "
p position
startpos = position + 1
end
end
end
end
# close the database
unless db.close
printf("error: %s\n", db.err_msg(db.error))
end
これをestkwic.rbという名前で保存して、「だけど」という語を探すときには、ruby estkwic,rb 'だけど' と打ってみる。すると、
$ ruby rubytest5.rb 'だけど' URI: file:///home/ynakano/ruby_test/kwic_test/oa050409.htm いつ、この通りを通るはずなん だけど なー、なかなか通んない 233 ?女私、病気で死んじゃったん だけど ね、どうしても私を裏切 5285 ・それで道で待ち伏せしてたん だけど ね。男しかし、地球人っ 6815 んでしょ。だから用意してたん だけど 。男おー、地球の食事っ 8086 !女ただのハムエッグとサラダ だけど ね。男ハムエッグとサラ 8176 URI: file:///home/ynakano/ruby_test/kwic_test/oa050226.htm 言ってたら気がまぎれていいん だけど ・・・一人で店じまいし 1111 何かが始まるぞって期待するん だけど 、何も起きなくてさ、ま 3154 の写真を見せてね、一億二千万 だけど 売れないから思い切って 6946 たら、ふらーっとなるばい。女 だけど その人、山川って名前な 7378 けどその人、山川って名前なん だけど 、いくつだと思う?男は 7423 今から新幹線で大阪まで行くん だけど 手元に現金が無いから持 7791 詐欺の一種たいなー。女お金も だけど 心動かされた自分が情け 8410 URI: file:///home/ynakano/ruby_test/kwic_test/oa050528.htm 一生懸命に思い出そうとしたん だけど 、どうしてもその部分だ 3388 めないだろう?涼子そりゃそう だけど ・・・・。あ〜あ、なん 4052 !と思い出せそうな気がするん だけど なぁ。幸一じゃあ連想テ 4169という具合に一応目的通りの結果を表示してくれた。嬉しい!
Rubyにも少しだけ慣れてきたような気がするので、もう一冊本を買ってみた。Ruby on Railsの本。手に取ってみて、字が多くて説明が丁寧なような印象を受けたので、コンピュータ店のポイントで入手。私にもRailsが使える日が来るだろうか。
![]() |
Amazon.co.jp Ruby on Rails入門 秀和システム (2006/08) ¥ 2,940 (税込み) 通常24時間以内に発送 |
手で更新してしまいました。cronで自動実行するかどうかの確認をしなければならないのに、河出書房新社の近刊情報ページが更新されたのを知ったので、ついruby kawade.rbとか打ち込んでしまって……。なんて莫迦なんだ。
『Perl→PHPらくらく移行ガイド』っていう本を見つけたんですがね、この反応は可逆なんでしょうか。「どちらか一方しか知らなくても、無理なくもう片方の言語を習得できるよう工夫。」ということは、PHPしか知らなくても、Perlが判るようになるんでしょうか。だとしたら、買ってみたい。でも、今はRubyに集中したいという気持もあるし。どうしよう。
![]() |
Amazon.co.jp Perl→PHPらくらく移行ガイド 毎日コミュニケーションズ (2006/09) ¥ 2,940 (税込み) 通常24時間以内に発送 |
なぜなんだ! 今度はちゃんと動くと思ったのに。
あれ? 新しいファイルが保存されてゐないのに、FTPでプロバイダ上のファイルは書き換へられているようだ。これまた、なぜだ! どうなっているんだ!
ubuntuはシステムのアプリケーション管理でBioRubyをインストールできるではないか。これには驚いた。でもちょっとヴァージョンが古い。0.6だか何か。最新版は1.0.0だというのに。使えないよりはずっといいのだけど。
閉じ忘れていましたよ。ドアとか水道の蛇口ではなくて、ファイルです。まあ、Rubyの話題なんで普通そうでしょうけどね。今月4日の塩基配列を正規表現で検索するスクリプト、よく見れば(よく見なくてもすぐに判りますが)、ファイルを開いたまま閉じていないではありませんか。みっともないので、書き直してみました。これでいいでしょうか。
word = Regexp.new(ARGV[1])
nuc = " "
File.open(ARGV[0]){|file|
while line = file.gets do
line.chomp!
nuc.concat(line)
end
}
startpos = 0
while position = nuc.index(word,startpos) do
result = nuc[position-25, 60]
result.grep(word){|final|
print final.sub(word){|s| " " + s + " "}
print " "
p position
}
startpos = position + 1
end
動かしてみたら、前と同様の結果を返してくれました。練習だから実際にはほとんど使わないのだけど、大事なことを忘れたままに放置しているのはみっともないし、練習になりませんからね。書き変えたっていったって「close」がないじゃないかよと呆れている方もいらっしゃるかも知れませんが。ちゃんと「 }」で閉じていますので、ご安心を。
昨日の河出書房新社近刊情報RSS、夜11時を過ぎてわくわくしながら確認してみたところ、実行された形跡がないではありませんか。うー、なぜなんだーと泣きながら涙で歪むモニタ画面を睨みつけていたら気づきました。ruby kawade.rbなんてコマンドを打ち込む訳ではないので、ファイルに実行権を与え、スクリプトの第一行に「#! /usr/bin/ruby」って書いておかなければならないのを忘れていたのです。今晩の11時に再度挑戦です。
MacOSXでPHPを書くときにはいつもmiを使っているのですが、これにRubyモードを追加してみたものの、残念ながら私の目にはあまり見やすくないのでした。JeditにRubyカラーを導入してみると、なかなかすっきりしていますが、ものたりないのですね(勝手なことばかり云ってすみません)。結局、iTerm上でvimを使おうかなと思っているところです。
こんな感じ↓
昨日の河出書房新社新刊情報取得スクリプトを改良した。新しいものが上に来るようにするには、
line.each{|elem|
のところを
line.reverse.each{|elem|
とするだけで簡単に解決できた。新刊情報らしくなったような気がする。
自分で使うだけならこれで完了だが、人にも利用してもらいたいとか、自分も外出先からも参照したい(そんなことがあるかどうかは知らないが)と思うかも知れないので、サーバに置くことにしよう。自宅サーバを持っている人は簡単だが(実は私は持っている)、プロバイダの自分のファイル置き場に転送するのが面倒臭いので、その機能も組み込むことにする。
最初に
require 'net/ftp'
としてから、
Net::FTP.open('ftp00.nifty.com') do |ftp|
ftp.login('user_id', 'password')
ftp.chdir('public_html')
ftp.puttextfile('kawadenew.xml')
end
という5行を追加した。実際にはopen以下の括弧内に自分の契約しているプロバイダのftpサーバのアドレスを、login以下の括弧の中にユーザIDとパスワードを、chdir以下の括弧内にファイルを置くディレクトリを書く(ちなみにniftyはpublic_htmlではなくhomepageだ)。さて、毎日自分で実行させてもいいのだけど、疲れて忘れたりすることもあるかも知れないから、自動的に実行するように設定しよう。MacOSXとかLinuxでは/etcにcrontabというファイルがあって、ここに指示を書き込めば定期的に実行してくれる。ファイルの自動バックアップなんかを設定しておくと便利で、私もそうしている。ただ、このPowerMac G5は24時間運用ではないので、何時に設定しようかちょっと悩んだ。毎晩10時にはバックアップを実行するので、11時にしようか。ということで、
00 23 * * * root /Users/nakano/Sites/kawade/kawade.rbと書き込む。root権限で毎晩11時に実行する。もしかしたら、rootじゃない方がいいのかな。よく判らない。しばらく様子をみてみよう。この河出書房新社近刊・新刊情報RSSは以下のURLで公開した。ちゃんと動けば毎晩更新される。
最終的に出来上がったものは以下のとおりである。
require 'open-uri'
require 'iconv'
require 'rss/2.0'
require 'net/ftp'
rss = RSS::Rss.new("2.0")
chan = RSS::Rss::Channel.new
chan.title = "RSS for Kawade forthcomming books"
chan.description = "Kawade forthcomming books"
chan.link = "http://www.kawade.co.jp/np/forthcoming.html"
chan.language = "ja"
rss.channel = chan
conv = Iconv.new("UTF-8","Shift_JIS")
open('http://www.kawade.co.jp/np/forthcoming.html') do |f|
line = f.read.split(/detailCopy/)
line.shift
line.reverse.each{|elem|
ipos = elem.index('/isbn/')
isbn = elem[ipos+6,10]
tpos2 = elem.index('/STRONG',ipos)
title = elem[ipos+18,tpos2-ipos-23]
title = conv.iconv(title)
dpos = elem.index('<STRONG>')
pubdate = elem[dpos+8,10]
apos1 = elem.index('/author/')
if apos1 == nil
description = pubdate
else
apos2 = elem.index('/SPAN',apos1)
author = elem[apos1+15, apos2-apos1-24].gsub(/<.*?>/m, "").gsub(/>\//,"")
author = conv.iconv(author)
description = author + " (" + pubdate + ")"
end
item = RSS::Rss::Channel::Item.new
item.title = title
item.link = "http://www.kawade.co.jp/np/isbn/" + isbn
item.description = description
chan.items << item
}
end
File.open("kawadenew.xml", "w+"){|file|
file.puts(rss.to_s)
}
Net::FTP.open('ftp00.nifty.com') do |ftp|
ftp.login('user_id', 'password')
ftp.chdir('public_html')
ftp.puttextfile('kawadenew.xml')
end
require 'open-uri'
require 'iconv'
require 'rss/2.0'
require 'net/ftp'
rss = RSS::Rss.new("2.0")
chan = RSS::Rss::Channel.new
chan.title = "RSS for Kawade forthcomming books"
chan.description = "Kawade forthcomming books"
chan.link = "http://www.kawade.co.jp/np/forthcoming.html"
chan.language = "ja"
rss.channel = chan
conv = Iconv.new("UTF-8","Shift_JIS")
open('http://www.kawade.co.jp/np/forthcoming.html') do |f|
line = f.read.split(/detailCopy/)
line.shift
line.reverse.each{|elem|
ipos = elem.index('/isbn/')
isbn = elem[ipos+6,10]
tpos2 = elem.index('</STRONG>',ipos)
title = elem[ipos+18,tpos2-ipos-22]
title = conv.iconv(title)
dpos = elem.index('<STRONG>')
pubdate = elem[dpos+8,10]
desc1 = elem.index('</DIV>',tpos2)
description = elem[tpos2, desc1-tpos2].gsub(/<.*?>/m, "")
.gsub(/\207U/, "").gsub(/\s+/, " ")
description = conv.iconv(description)
description = description + " (" + pubdate + ")"
item = RSS::Rss::Channel::Item.new
item.title = title
item.link = "http://www.kawade.co.jp/np/isbn/" + isbn
item.description = description
chan.items << item
}
end
File.open("kawadenew.xml", "w+"){|file|
file.puts(rss.to_s)
}
Net::FTP.open('ftp00.nifty.com') do |ftp|
ftp.login('user_id', 'password')
ftp.chdir('public_html')
ftp.puttextfile('kawadenew.xml')
end
ubuntuにBioRubyをインストールしようとした。そろそろちゃんと仕事に使おうかと思ったわけだ。ダウンロードした圧縮ファイルを展開し、ruby install.rb config → ruby install.rb setup → sudo ruby install.rb installとやったらエラーが出てしまった。最初のconfigのところである。rubyに馴染の薄い私には何が起こったのだか全く理解できない。茫然と涙を流すだけである。
帰宅してから、PowerMac G5で試したみたら簡単にインストールできた。家でインストールに成功してもあまり使わないのだけど。
昨日の河出書房新社新刊情報取得スクリプトを発展させてRSS作成スクリプトにしてみよう。こんなふうに書いてみた。
require 'open-uri'
require 'iconv'
require 'rss/2.0'
rss = RSS::Rss.new("2.0")
chan = RSS::Rss::Channel.new
chan.title = "RSS for Kawade forthcomming books"
chan.description = "Kawade forthcomming books"
chan.link = "http://www.kawade.co.jp/np/forthcoming.html"
chan.language = "ja"
rss.channel = chan
conv = Iconv.new("UTF-8","Shift_JIS")
open('http://www.kawade.co.jp/np/forthcoming.html') do |f|
line = f.read.split(/detailCopy/)
line.shift
line.each{|elem|
ipos = elem.index('/isbn/')
isbn = elem[ipos+6,10]
tpos2 = elem.index('/STRONG',ipos)
title = elem[ipos+18,tpos2-ipos-23]
title = conv.iconv(title)
dpos = elem.index('<STRONG>')
pubdate = elem[dpos+8,10]
apos1 = elem.index('/author/')
if apos1 == nil
description = pubdate
else
apos2 = elem.index('/SPAN',apos1)
author = elem[apos1+15, apos2-apos1-24].gsub(/<.*?>/m, "").gsub(/>\//,"")
author = conv.iconv(author)
description = author + " (" + pubdate + ")"
end
item = RSS::Rss::Channel::Item.new
item.title = title
item.link = "http://www.kawade.co.jp/np/isbn/" + isbn
item.description = description
chan.items << item
}
end
File.open("kawadenew.xml", "w+"){|file|
file.puts(rss.to_s)
}
このファイルをhttpdサーバが見せたいファイルを置く場所に入れて実行させるとkawadenew.xmlというファイルを作り出す。これがRSS2.0の書式に従って作成されたファイルである。RSSリーダで開くと下のようになる(これはMacOSXのSafariで開いたところ。上の部分だけ)。昨日、Amazon.co.jpから日本語の本の検索結果が得られたのは実はMacOSXからであった。Ubuntuでやってみたら、どうもlocale =JPという設定が有効にならないようなのだ。システムからインストールされるAmazon-rubyが0.9.0-2だったので、これを外して 0.9.2にしてみたが、結果は同じ。何かの作業に支障を来すわけではないが、どうも納得できない。なぜだ!
Amazonが思うようにいかず甚だ不愉快なので、先日ISBNを取得しただけで放置しておいた河出書房新社近刊情報リスト取得に手を付けることにした。
文字コードをUTF8にしたのは、後でRSSに使おうかと考えているから。こう書くと簡単に作ったように見えるが、いろいろ困ったりしていたのだ。特に文字コードの扱いは。
require 'open-uri'
require 'iconv'
conv = Iconv.new("UTF-8","Shift_JIS")
open('http://www.kawade.co.jp/np/forthcoming.html') do |f|
line = f.read.split(/detailCopy/)
line.shift
line.each{|elem|
ipos = elem.index('/isbn/')
isbn = elem[ipos+6,10]
print isbn + " 『"
tpos2 = elem.index('/STRONG',ipos)
title = elem[ipos+18,tpos2-ipos-23]
print conv.iconv(title) + "』"
apos1 = elem.index('/author/')
if apos1 == nil
print "\n"
else
apos2 = elem.index('/SPAN',apos1)
author = elem[apos1+15, apos2-apos1-24].gsub(/<.*?>/m, "").gsub(/<\//,"")
puts conv.iconv(author)
end
}end
著者の表示があまり整っていないが、もう面倒臭い。あと価格と刊行予定日を取得して、河出書房新社新刊情報RSSでも作ろうか。PHPではもうできているのだが。結果表示はこんな感じである。
4309951449 『ディズニープリンセス vol.24 2006年10-11月号 』 4309017789 『浮世でランチ』山崎 ナオコーラ 4309243916 『心理学論文の書き方』松井 豊 4309243932 『性犯罪の心理』作田 明 4309269168 『大人の塗り絵 秋の花編』佐々木 由美子 4309280641 『かぎ針1本でできる! あみぐるみcollection VOL.5』河出書房新社編集部 430928065X 『ハワイアン布でつくるカルトナージュ』駒澤 由美子 4309280676 『こどもニットの本』渡部 サト 4309715656 『マックス・エルンスト』サラーヌ・アレクサンドリアン 著 大岡 信 訳 マックス・エルンスト 4309252052 『世界でいちばん良い医者に出会う「患者学」』小林 一哉 著 前田 稔本当はもっと長い。著者が複数いるときに、ちょっと変な感じになってしまう。どうすればいいんだろう。
4日の文字列検索スクリプトでは「if position !=nil」と一つ目の「end」が余計だった。その前の日の条件がそのまま残っていました。この条件はwhile以下に含まれています。
Ruby/Amazonをインストールして、Amazon.co.jpで検索してみた。
require 'amazon/search'
dev_token = "xxxxxxxxx"
locale = 'jp'
keyword = ARGV[0]
req = Amazon::Search::Request.new(dev_token,locale)
res = req.keyword_search(keyword, 'books', Amazon::Search::LITE) { |product|
print product.asin + " "
puts product.product_name
}
練習なので表示させるのはISBNと書名のみ。これをamz.rbという名で保存して、
$ ruby amz.rb ロード・ダンセイニと打ち込んでみたら、
4480028668 魔法使いの弟子 415020005X ペガーナの神々 4309462545 時と神々の物語 4309462634 最後の夢の物語 4309462421 世界の涯の物語 4480021515 短篇集 妖精族のむすめ 4309462472 夢見る人の物語 4150200475 魔法の国の旅人 4150200300 魔法使いの弟子 4480025588 影の谷物語と表示された。なるほどねえ。これをどう使おうか、まだ検討中。
![]() |
Amazon.co.jp ライド・オン・Rails ソフトバンククリエイティブ (2006/6/30) ¥ 3,129 (税込み) 通常24時間以内に発送 |
phpでgdを使うためにphp5-gdをインストールしたのに、全然有効にならない。いろいろ余計なものをインストールしてしまったが、結局php.iniのextension=gd.soのところのコメントアウト#を外していなかったのだった。前は--with-gdでコンパイルしていたときはこんなことしなかったような気がするのだけど。慣れないと何でも難しい。
また本を買ってしまった。高いなと思いながら註文したのだが、これは分厚い。873ページもあるのだから当然だが。さまざまな状況に応じたいろいろな例が載っているので役立ちそう。日本語ならもっと判りやすかったのに。
今悩んでいるのは実は日本語の処理なのだが、それはこの本には載っていない。
![]() |
Amazon.co.jp Ruby Cookbook Oreilly & Associates Inc (2006/7/28) ¥ 5,201 (税込み) 通常24時間以内に発送 |
昨日のでは行の切れ目に求める配列があると見つけ出せないので、全部一列につないでから探すようにしてみた。改行コードを削除しながらconcatで全部つないで、文字列検索の開始点を、前に見付けた場所の次の位置からにして、どんどん先に進めていくというのはPHPで慣れているので簡単である。最初に20個の空白を置いているのは、探したい配列が最初の方にあったときのため。ついでに、行の右端に検索文字列の位置を表示させてみた。
filename = ARGV[0]
word = Regexp.new(ARGV[1])
nuc = " "
file = open(filename)
while line = file.gets do
line.chomp!
nuc.concat(line)
end
startpos = 0
while position = nuc.index(word,startpos) do
if position != nil
result = nuc[position-25, 60]
result.grep(word){|final|
print final.sub(word){|s| " " + s + " "}
print " "
p position
}
startpos = position + 1
end
end
こんなふうに結果が出る。昨日よりも増えているから、ちゃんと行に跨る部分も拾っているようだ。ちなみに、正規表現で検索しているから、GAAとTTCで挟まれた4文字を見付けるとかいうこともできる。
CATTCTTTATACTTATAAGATTAAT AAGGAGGA AACTAACTGTGAAAATCCTATGCGAGA 1713 AACATTGAAGTAGTCAGTCTCTACC AAGGAGGA GGCCGGATATACTGTTTTGTCCGGAAA 32688 TCAGGCTGCACTAGACTCACCCGAC AAGGAGGA GTAGCGTGCGCAGAACGCGCGGCGAGA 71126 TTAGCAGCGGGGTTGCTCATCCTTC AAGGAGGA TTGAGAGTCCTGCCCTCAGGGTGTGCA 106809 TGCCCGGTACCCACTCCTTGTCGAC AAGGAGGA GGAAATCATCCCAGGAGAGGGATGTGA 107626 TTCCACCCTTTTTCCGAGAGGAAAT AAGGAGGA TGTGCACGATGCACGCGCTCATGCGTC 186529 AGTTTTTGGCTCGTGTTGATTTAAA AAGGAGGA CGAAATAATGCCAAAACAAGTAGTCGT 229250 CGAGCGTAGCAGCGAGTATGTATCC AAGGAGGA ATCCTCCCGTAGGGGCAAAAAGCGCGG 237045今度はオンライン情報を取得してみたくなった。そこで、河出書房の近刊情報を取りにいってみることにした。
require 'open-uri'
open('http://www.kawade.co.jp/np/forthcoming.html') do |f|
line = f.read.split(/\/isbn\//)
line.shift
line.each{|elem|
p elem[0,10]
}
end
たったこれだけである。練習だから。open-uriでhtmlファイルの内容を取得して、/isbn/という文字列で内容を分割して配列にする。最初の要素には書籍情報が入っていないので、array.shiftで先頭の要素を外して各要素の最初の10文字を表示させたら、近刊図書のISBNがずらりと表示された。うまくいったけど、全然面白くない。 結果はこんな感じ(実際にはもっと長い)。
"4309408095" "4309408117" "4309408125" "4309408133" "4309408184" "4309462774" "4309462782" "430948090X"今日届いたこの本も少し参考にしてみた。
![]() |
Amazon.co.jp プログラミングRuby 第2版 言語編 オーム社 (2006/8/26) ¥ 3,990 (税込み) 通常24時間以内に発送 |
週末になったのでRubyの練習である。今日はバクテリア・ゲノムの塩基配列の中から、調べたい配列を含む領域を抜きだして表示するものを作ってみよう。このゲノム配列は、1行70字のテキストファイルで、全部で300万文字ほどある。バクテリアなので、真核生物に比べると短いものである。
ファイル名と調べたい塩基配列を打つと、その文字列を含む行を返して欲しいということと、求める文字列を中心にきれいに並べて欲しいという二点が今回の要求だ。まず、求める文字列を含む行を表示させるために、こんなものを書いた。
filename = ARGV[0]
word = Regexp.new(ARGV[1])
file = open(filename)
while text = file.gets do
text.grep(word){|line|
p line.sub(word){|s| "<" + s + ">"}
}
end
file.close
ターミナルに例えばruby test.rb AE000520.fna AAGGAGGAと打ち込んでみる。test.rbが上記のスクリプトのファイル名、AE000520.fnaが塩基配列のファイル名、AAGGAGGAが今回求める塩基配列である。ARGV[0]がファイル名、ARGV[1]が求める塩基配列として取り込まれる訳だが、「Regexp.new」というところが何だか判らない。そうすると動く(そうしないと動かない)のだけれど、馴染めないというか……。あとは一行ずつ取り込んでgrepで検索文字列が含まれているかどうかを判定して、検索文字列の両側に<と>を置いて強調してみた。
"CTTATAAGATTAAT<AAGGAGGA>AACTAACTGTGAAAATCCTATGCGAGAAAGAAGCCTTTCTGAAGGAAA\n" "GACTGCGTCTGCATATACACTCATAACATTGAAGTAGTCAGTCTCTACC<AAGGAGGA>GGCCGGATATACT\n" "CTCATAATAACTCCTATGGACGGCTCACCTTATCAGGCTGCACTAGACTCACCCGAC<AAGGAGGA>GTAGC\n" "GGTGCTTGCTCCTCTTTAGCAGCGGGGTTGCTCATCCTTC<AAGGAGGA>TTGAGAGTCCTGCCCTCAGGGT\n" "ACCCACTCCTTGTCGAC<AAGGAGGA>GGAAATCATCCCAGGAGAGGGATGTGAGCGCCTTCGCATCCTTGT\n" "ATTAGTTCCACCCTTTTTCCGAGAGGAAAT<AAGGAGGA>TGTGCACGATGCACGCGCTCATGCGTCTGTTC\n"結果はこんな感じ(実際には30行くらいある)。括弧で挟んでも検索文字列がどこにあるのか判りにくい。もう少し何とかしたい。
そこで、少し改良してみた。
filename = ARGV[0]
word = Regexp.new(ARGV[1])
file = open(filename)
while text = file.gets do
line = " " + text
position = line.index(word)
if position != nil
result = line[position-16, 40]
result.grep(word){|final|
p final.sub(word){|s| " " + s + " "}
}
end
end
file.close
まず一行ずつ取り込んで、左側に空白を20個くっつける。これがないと後で困る。それから、検索語が出てくる位置を「index」で調べる。検索語が含まれていなければ「nil」が返ってきているはずなので、nilでなければ、その位置から16文字前の場所から40文字を抜きだし、さらに先ほどのスクリプトのときと同様に、grepとsubで検索語の両脇に空白を一個ずつ挿入して表示させる。さて、結果は、
" CTTATAAGATTAAT AAGGAGGA AACTAACTGTGAAAATCCTATGCGA" "ATAACATTGAAGTAGTCAGTCTCTACC AAGGAGGA GGCCGGATATACT\n" "TATCAGGCTGCACTAGACTCACCCGAC AAGGAGGA GTAGC\n" "CTTTAGCAGCGGGGTTGCTCATCCTTC AAGGAGGA TTGAGAGTCCTGCCCTCAGGGT\n" " ACCCACTCCTTGTCGAC AAGGAGGA GGAAATCATCCCAGGAGAGGGATGT" "AGTTCCACCCTTTTTCCGAGAGGAAAT AAGGAGGA TGTGCACGATGCACGCGCTCATGCG"となった。おお、前よりもずっと見やすいではないか。
練習初日ではこんなところか。この練習の成果が実際の塩基配列の解析に役立つかというと残念ながらそんなことはなくて、求める配列が行をまたいでいると見つけ出せないので役立たずなのだ。一行一文に整形した普通の文章から、探したい用例を見つけるのには使えるかも知れない。いわゆるKWICである。長いテキストでも大丈夫(1.1MBだから大したことはない)だということで、塩基配列なんか使ったが、やはりここはダンセイニの『ペガーナの神々』を使ってみようか。「game」という言葉が出現する箇所を探してみると、
" regard with pale eyes the game s of the small gods, and to w" "ears passed over the first game of the gods.\n" "ib grew weary of the first game of the gods, and raised his " "ars passed over the second game of the gods, and still it wa" "b grew weary of the second game , and raised his hand in the " "ds saw Kib playing his new game They came and played it too." "he gods; We are the little game s of MANA-YOOD-SUSHAI that he" ", saying: 'We now play the game of the gods and slay men for" "ana's gods, and play Their game with men.'\n" "med haunts, and played the game of Life and Death with fishe" "the Worlds, nor any more a game for the small gods to play.\n" "ods endure, and play their game with men.\n" "th, for ye have played the game of the gods too long with th"ちゃんと見つけて表示してくれた。でも、何か物足りないなあ。
今日参考にしたのはこの本。
![]() |
Amazon.co.jp Rubyレシピブック ソフトバンククリエイティブ (2004/05) ¥ 2,940 (税込み) 通常24時間以内に発送 |