言語処理100本ノックをやってみる 第1章:準備運動 の続き。
言語処理100本ノックの第2章: UNIXコマンドの基礎は、プログラミングの問題ではないので、飛ばして第3章をやります。
今回はScalaに頼らず全問Rubyで書いた。(Rubyの正規表現って便利ですね)
前回に引き続き、Rubyだと一般的にこう書く、とか、こう書いた方が良い、とかコメントもらえると大変ありがたいです。
注意
※ この回答が間違ってる可能性が有ります。
※ 言語処理の知識が無いので、そもそも問題文の解釈を間違えている可能性も有ります。
準備
Wikipediaの記事を以下のフォーマットで書き出したファイルjawiki-country.json.gzがある.
1行に1記事の情報がJSON形式で格納される
各行には記事名が"title"キーに,記事本文が"text"キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
ファイル全体はgzipで圧縮される
以下の処理を行うプログラムを作成せよ.
※ 上記jawiki-country.jsonは、 1行毎にjson になっていることに注意
ファイルをまとめてパースするとエラーになります。ハマった。。
require 'json'
def getParsed
file_path="./jawiki-country.json"
i = 0
parsed = []
open(file_path) do |f|
f.each_line do |line|
parsed.push(JSON.parse(line))
end
end
parsed
end
20. JSONデータの読み込み
Wikipedia記事のJSONファイルを読み込み,「イギリス」に関する記事本文を表示せよ.問題21-29では,ここで抽出した記事本文に対して実行せよ.
getParsed.select {|item| item["title"].scan(/イギリス/) != [] }.each{|item| puts item["text"]}
21. カテゴリ名を含む行を抽出
記事中でカテゴリ名を宣言している行を抽出せよ.
uk = getParsed.select {|item| item["title"].scan(/イギリス/) != [] }[0]
uk["text"].scan(/\[\[Category:.+?\]\].*?/)
22. カテゴリ名の抽出
記事のカテゴリ名を(行単位ではなく名前で)抽出せよ.
uk["text"].scan(/(\[\[Category:)(.+?)(\|.*?)*(\]\].*?)/).map{|c| c[1] }
23. セクション構造
記事中に含まれるセクション名とそのレベル(例えば"== セクション名 =="なら1)を表示せよ.
uk["text"].scan(/(^=+)(\s*)(.*?)(\s*)(=+)/).map{|s| [s[2], s[0].length - 1]}.each{ |nl| puts "#{nl[0]}:#{nl[1]}"}
24. ファイル参照の抽出
記事から参照されているメディアファイルをすべて抜き出せ.
uk["text"].scan(/(\[\[)(ファイル:)(.*?)(\|)(.*?)(\|)(.*?)(\]\])/).map{|i| i[2]}
25. テンプレートの抽出
記事中に含まれる「基礎情報」テンプレートのフィールド名と値を抽出し,辞書オブジェクトとして格納せよ.
basic = uk["text"].scan(/(({{基礎情報.*\n)(|.*?\n)*(}}))/)[0][0].scan(/(\|)(.*?)(=)(.*?)(\n)/).map{|i| [i[1].strip, i[3].strip]}.to_h
26. 強調マークアップの除去
25の処理時に,テンプレートの値からMediaWikiの強調マークアップ(弱い強調,強調,強い強調のすべて)を除去してテキストに変換せよ(参考: マークアップ早見表).
no_bold = basic.map{|k, v| [k, v.gsub(/(''''')(.*?)(''''')/){$2}.gsub(/(''')(.*?)(''')/){$2}.gsub(/('')(.*?)('')/){$2}] }.to_h
27. 内部リンクの除去
26の処理に加えて,テンプレートの値からMediaWikiの内部リンクマークアップを除去し,テキストに変換せよ(参考: マークアップ早見表).
no_inner_link = no_bold.map{|k,v| [k, v.gsub(/(.*?)(\[\[)([^\]]*?)(\|)(.*?)(\]\])([^\[]*)/){|i|"#{$1}#{$5}#{$7}"}.gsub(/(.*?)(\[\[)([^\]]*?)(\]\])([^\[]*)/){|i|"#{$1}#{$3}#{$5}"}]}.to_h
28. MediaWikiマークアップの除去
27の処理に加えて,テンプレートの値からMediaWikiマークアップを可能な限り除去し,国の基本情報を整形せよ.
外部リンクの除去
no_outer_link = no_inner_link.map{|k,v| [k, v.gsub(/(.*?)(\[)(http[s]*:\/\/[^\s\]]*)([^\]]*?)(\])(.*?)/){|i| if $4 == '' then "#{$1}#{$3}#{$6}" else "#{$1}#{$4}#{$6}" end}]}
29. 国旗画像のURLを取得する
テンプレートの内容を利用し,国旗画像のURLを取得せよ.(ヒント: MediaWiki APIのimageinfoを呼び出して,ファイル参照をURLに変換すればよい)
require 'net/http'
require 'uri'
uri = URI.parse(URI.escape("http://www.mediawiki.org/w/api.php?action=query&format=json&titles=Image:#{no_inner_link["国旗画像"]}&prop=imageinfo&iiprop=url"))
JSON.parse(Net::HTTP.get(uri))['query']['pages']['-1']['imageinfo'][0]['url']