コマンドラインでPRODRG
通常はWebでアクセスして使用するPRODRG。この便利なサイトへのアクセスを自動化します。
自動化手順は以下の通り。
- 低分子のPDBファイルを用意する
- 内容をPRODRGに送信し、結果を取得する
- 結果を解析してファイルに保存する
という手順をスクリプトで実現します。
PDBファイルを準備するのはスクリプトとは関係ないので2と3の部分を実装してみます。HTTPによるアクセスはPerlのモジュールではなくcURLコマンドを使用することにします。cURLはVine Linuxの場合apt-getでインストール可能で、wgetと似たようなHTTPアクセスなどでファイルを取得するために使用する、コマンドラインのクライアントプログラムです。スクリプティング言語Curlとは関係ありません。
# apt-get install curl
それでは実装を考えてみます。
内容をPRODRGに送信し、結果を取得する
PDBファイルのURLエンコードおよびパラメータ設定
新しいcURL(7.18.0以降)には送信パラメータのURLエンコード機能があるようですが、Vineに収録されているバージョンではその機能はありませんので自力でURLエンコードします。
- 下に示すreserved文字はデリミタとして競合するのでパーセントエンコーディングする
- reserved = gen-delims / sub-delims
- gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
- sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
- それ以外のALPHA / DIGIT / "-" / "." / "_" / "~"はそのまま使用できる
- スペース文字は"+"に置き換える
- 上記に当てはまらない文字はパーセントエンコーディングする
ということらしいです。
Perlで変換するルーチンを書くと以下のようになります。
s/([^\w-\._ ])/"%".unpack("H2",$1)/ego; s/ /+/g;
cURLによるHTTPアクセス
エンコードができたらcURLでサーバにアクセスします。cURLのコマンドラインは以下の通り
% curl --data "@-" URL
@-でポストするデータを標準入力から読み込みます。
結果を解析してファイルに保存する
PRODRGで取得される結果は当然HTML形式です。XMLパーサを使えば簡単に解析できると思うのですが、Perlでそれを実現しようとすると外部モジュールが必要になります。
あくまで「簡単な」スクリプトなのでできるだけ外部モジュールに頼らずに実現します。
正規表現で必要な部分だけ解析
HTMLなのでパラメータなどが取り出しやすいです。
- ファイル名の部分 <a name='ファイル名'>
- ファイルの説明 <font size=6>ファイルの説明</font>
- 内容 <textarea ...>ファイル内容</textarea>
解析結果をファイルに出力
上に挙げた部分を正規表現で取り出し、各ファイルに分けて出力します。出力先ディレクトリを指定できるようにした方がいいかもしれません。
スクリプト
ソース
以上の内容をPerlで書いてみます。
エラー処理などはしてません。
#!/usr/bin/perl use strict; use IPC::Open2; my $DIR = "./prodrg-out"; my $CMD = 'curl --data "@-"'; my $URL = "http://davapc1.bioch.dundee.ac.uk/cgi-bin/prodrg/prodrg.cgi"; main(); sub main { my $pdb = $ARGV[0]; if($ARGV[1]){ $DIR = $ARGV[1] } if(!-d $DIR){ mkdir $DIR } my $post_str = encode_pdb_file($pdb); $post_str = "clax_cha=VAC&clax_chir=&clax_em=EM&coords=$post_str"; my $lines = get($post_str); process($lines); } sub encode_pdb_file { # ファイルを開いて内容をURLエンコードする my $file = shift; my $str = ""; open(my $fh,$file) || die "Can't open file : $!\n"; while(<$fh>){ unless(/^ATOM /){ next } s/([^\w-\._ ])/"%".unpack("H2",$1)/ego; s/ /+/g; $str .= $_; } return $str; } sub get { # cURLを使用して結果を取得する # プロセスのオープンにはIPC::Open2を使用する my $str = shift; my $pid = open2(my $rh,my $wh,"$CMD $URL"); print {$wh} $str; # cURLの標準入力に送信文字列を書き込む close $wh; # EOFを送信し出力をフラッシュ my $lines = []; while(<$rh>){ push(@$lines,$_) } # 結果の読み取り close $rh; waitpid($pid,0); # 子プロセスの終了を待つ return $lines; } sub process { my $lines = shift; my $chk = 0; my $cif = ""; my $current = ""; my $summary = []; my $desc = ""; foreach(@$lines){ # ファイル名とそれに対する説明を取得する if(/<a name='(.+?)'>/){ $current = $1; next } if(/<font color=yellow size=6>(.+?)<\/font>/){ $desc = $1; next } # ファイル内容を取得する if($chk){ if(/<\/textarea>/){ $chk = 0; open(my $out,">$DIR/$current"); print {$out} $cif; close $out; push(@$summary,[$current,$desc]); }else{ $cif .= $_; } }else{ if(/<textarea .+?>(.+)/){ $cif = "$1\n"; $chk = 1; } } } # 結果を出力 foreach(@$summary){ printf("%-11s : %s\n",@$_) } }
使い方
% ./prodrg TEST.pdb outdir
TEST.pdbの内容をPRODRGで生成し、outdirに出力します。outdirを省略した場合は./prodrg-outが指定されたのと同じです。