data dari KBBI Daring

先日書いたように、KBBI Daringで公開されている辞書データのエントリはダウンロードできた。親項目で4万近いエントリがあり、KBBI Daringの置いてあるサーバが偶にアクセスできない(落ちてる?)ようなので、その追込項目をカウントするスクリプトを書いても時間のかかる処理はできなさそう。それならデータを全部ローカルにダウンロードしてから処理すれば良いじゃん、ということになった。(というか、そういう言い訳にする。意味は汲み取られたし。)

先日のスクリプトで得られた項目リストのあるファイルをWORDLISTとして、末尾に記すスクリプトを get_def.pl とする(実行属性付けるか、perlから呼び出すかしてね)。ちなみに、WORDLISTは適当な数に分割して作業を数度に分けた方がトラブルに遭ったとき(あっちのサーバが落ちるとか)、その後の作業を続けやすいだろう。あらかじめ、カレントディレクトリに、tmp というディレクトリを作成する(ここに、各単語の定義が保存される)。その上で、

get_def.pl WORDLIST

とすれば良い。時間がかかるので、寝るまえに仕込んで寝てしまいましょう。翌朝起きてみると、tmp以下に単語ごとに以下のようなファイルが4万ほどできているでしょう。全部で143MBほどになります。

MIDASHI: nyen·trik
NUMBER: 0
KENSAKUGO: nyentrik
IMI:<b>nyen&#183;trik</b> /ny&#233;ntrik/ <i>v cak</i> berperilaku, bergaya eksentrik, aneh, tidak wajar (tt anak muda msl anak laki-laki memakai subang, atau memakai celana jin yg di bagian tumit dirobek, dng baju digulung ke atas sampai pendek sekali, dsb)

あとは、煮くなり焼くなりお好きに調理してください。ちなみに、KBBI Daring のデータのhtmlはとっても汚いので、それぞれのデータをかなり修正しないと、別の形式(EPWINGとかね)に変換するのは大変そうです。

以下、スクリプト(get_def.pl)。

#!/usr/bin/perl

use HTTP::Request::Common qw(POST);
use LWP::UserAgent;
#use HTML::TreeBuilder;
use HTML::TokeParser::Simple;
use HTML::Entities;
use Data::Dumper;
#use File::Temp;

@tmparr = ();
$filename = $ARGV[0];
open (BASEFH, “<$filename”) or die “Cannot open $filename.\n”;

@wlist = ;
$wnum = @wlist;
print “Going to process $wnum words.\n”;
my $i = 1;
foreach my $val ( @wlist ){
chomp ($val);
#print $val;
#print “$i, $wnum\n”;
#print “@tmparr\n”;
push @tmparr, $val;
# 15語ずつ処理
if ( (( $i / 15 ) – int ( $i / 15 ) ) == 0
|| $i == $wnum ) {
$dftkata= join (‘;’, @tmparr);
#print $dftkata;
$j=1;
foreach my $valu ( @tmparr ) {
#print $valu;
$counter = ( 15 * int ( ($i -1 ) / 15 ) ) + $j;
print “Searching: $valu ($counter\/$wnum).\n”;
$j ++;
my ($midashi, $wordnum, $kensakugo, $definition) = &get_definition( $valu );
open ( TMPFILE, “>tmp/$valu.dat” );
print TMPFILE “MIDASHI: $midashi\nNUMBER: $wordnum\nKENSAKUGO: $kensakugo\nIMI:$definition\n\n”;
close (TMPFILE);
sleep 3;
}
@tmparr = ();
if ( $i != $wnum ) {
print “Wait for 10s”;
foreach (0..9) {
sleep 1;
print ‘.’;
}
print “\n”;
}
}
# 100 語 やったら 30秒休憩
if ( (( $i / 100 ) – int ( $i / 100 ) ) == 0 ){
print “Wait for 30s………\n”;
foreach (0..29) {
sleep 1;
print ‘.’;
}
print “\nNow back to work!!!\n”;
}
$i ++;
}

close(BASEFH);

#———————————
sub get_definition (){
my ($kata)=@_;
#print $kata;
my $opkode=2; #固定 前方一致
my $param=’a’;
my $perintah2=”Tampilkan”;
#my $dftkata=’A (1);a- (3);ab (1);ab (2);ab- (3);aba;aba-aba;abad;abadi;abadiah;abadiat;abah (1);abah (2);abah-abah;abai’;
#my $dftkata=”;
#print $dftkata;
my $more=1;
my $head=0;
my @arr = ();
# リクエストの生成
my $url = ‘http://pusatbahasa.diknas.go.id/kbbi/index.php’;
my %formdata = (‘OPKODE’ => $opkode,
‘PARAM’ => $param,
‘PERINTAH2’ => $perintah2,
‘DFTKATA’ => $dftkata,
‘MORE’ => $more,
‘HEAD’ => $head,
‘KATA’ => $kata);
my $request = POST($url, [%formdata]);

# UserAgentを生成して処理
my $ua = LWP::UserAgent->new;
my $res = $ua->request($request);
#print $res->as_string;
@arr = &extract_definition($res->as_string);
#foreach my $vvv ( @arr ) {
# print “$vvv \n”;
#}
return @arr;
}

#————————————–
sub extract_definition (){
my $p = HTML::TokeParser::Simple->new(string => @_);
# 変数の初期化
#print $p;
my $midashimark=0;
my $wordnummark=0;
my $wordnum=0;
my $startdef=0;
my $bnum = 0;
my $supnum = 0;
my @kensakugolist = ();
my @kensakugo = udnef;
my @deflist = ();
my $midashigo = undef;
# Token に分けてループ
while ( my $token = $p->get_token ) {
#定義部分の開始をマーク
if ($token->is_start_tag(‘p’) && $token->return_attr->{style} eq ‘margin-left:.5in;text-indent:-.5in’ ) {
$startdef ++;
}

# 定義部分の終了・変数書き出し
if ($token->is_end_tag(‘p’) || $token->is_end_tag(‘BODY’) ) {
my @arr = ();
#print “$midashigo $wordnum”;
push @arr, $midashigo;
push @arr, $wordnum;
push @arr, join(‘|’,@kensakugolist);
push @arr, join(”,@deflist);
return @arr;
exit;
}

# 定義部分開始以降
if ($startdef > 0){
# 開始Tag の場合
if ( $token->[0] eq ‘S’ ) {
# b tagの数をチェック。最初のものは見出し。
if ( $token->[1] eq ‘b’ ){
$midashimark ++;
$bnum ++;
push @deflist, $token->[4];
}elsif ( $token->[1] eq ‘sup’ ){
$supnum ++;
$wordnummark ++;
push @deflist, $token->[4];
}elsif ( $token->[1] eq ‘i’ ){
push @deflist, $token->[4];
}elsif ( $token->[1] eq ‘br’ ){
push @deflist, $token->[4];
}
}

# 終了Tag の場合
elsif ( $token->[0] eq ‘E’ ) {
if ( $token->[1] eq ‘b’ ){
$bnum –;
push @deflist, $token->[2];
}elsif ( $token->[1] eq ‘sup’ ){
$supnum –;
push @deflist, $token->[2];
}elsif ( $token->[1] eq ‘i’ ){
push @deflist, $token->[2];
}
}
# Text の場合
elsif ( $token->[0] eq ‘T’ ) {
$thistext = $token->[1];
#print “$thistext\n”;
chomp ( $thistext );
#push @deflist, decode_entities($thistext);
push @deflist, $thistext;
#見出し語・単語番号
#print “$midashimark, $bnum, $wordnummark, $supnum\n”;
if ( $midashimark == 1
&& $bnum ==1 ){
if ($wordnummark == 1
&& $supnum ==1
&& $thistext =~ m/[0-9 ]+/){
#単語番号
$wordnum = $thistext;
$wordnum =~ s/\s//g;
}else{
#見出し語
$midashigo = decode_entities($thistext);
$thistext=~ s/\&\#183;//g;
$thistext= decode_entities($thistext);
@kensakugo = split (/, /, $thistext);
push @kensakugolist, @kensakugo;
#print “MIDASHI:$midashigo\n”;
}
}else{
# 語幹、派生語
if ( $thistext =~ m/\&\#183;/ ) {
$kensakugo = $thistext;
$kensakugo=~ s/\&\#183;//g;
$thistext= decode_entities($thistext);
#print “KENSAKUGO:$kensakugo\n”;
push @kensakugolist, $kensakugo;
}
}
}
}
}
}