Contents
スポンサードリンク
第2章後半の続きです。どうやらここからが本番のようです。正規表現という初めて触れるので他の方のサイトを参考にしながらゆっくりやっていきたいです。
参考にさせていただいたサイトはこちら
第3章: 正規表現
Wikipediaの記事を以下のフォーマットで書き出したファイルjawiki-country.json.gzがある.
- 1行に1記事の情報がJSON形式で格納される
- 各行には記事名が”title”キーに,記事本文が”text”キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
- ファイル全体はgzipで圧縮される
以下の処理を行うプログラムを作成せよ.
20. JSONデータの読み込み
『Wikipedia記事のJSONファイルを読み込み,「イギリス」に関する記事本文を表示せよ.問題21-29では,ここで抽出した記事本文に対して実行せよ.』
1 2 3 4 5 6 7 8 9 10 |
#!/usr/bin/env python # -*- coding: utf-8 -*- import json with open("jawiki-country.json") as f: article_json = f.readline() while article_json: article_dict = json.loads(article_json) if article_dict["title"] == "イギリス": print(article_dict["text"]) article_json = f.readline() |
ファイルを読み込み、一行ずつ処理するところはこれまでもやってきたので問題ないと思います。
ファイルがjson形式なので、json.loads()関数を用いてjson形式に変換しています。
問題はarticle_dict[“title”]==[“イギリス”]の部分です。
どうやらこれは記事内で利用されている、htmlタグを指定するときに利用するらしい。htmlファイルには
実際に
1 |
<p>「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.</p> |
みたいな感じでいろいろなタグが使われている。
今回は要するにファイル内の
1 |
<title>イギリス</title> |
となっている部分について読むということであろう。
実行結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
{{redirect|UK}} {{基礎情報 国 |略名 = イギリス |日本語国名 = グレートブリテン及び北アイルランド連合王国 |公式国名 = {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br/> *{{lang|gd|An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath}}([[スコットランド・ゲール語]])<br/> *{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}([[ウェールズ語]])<br/> *{{lang|ga|Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann}}([[アイルランド語]])<br/> *{{lang|kw|An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh}}([[コーンウォール語]])<br/> *{{lang|sco|Unitit Kinrick o Great Breetain an Northren Ireland}}([[スコットランド語]])<br/> ・ ・ ・ {{イギリス関連の項目}} {{ヨーロッパ}} {{EU}} {{国連安全保障理事会理事国}} {{G8}} {{OECD}} {{イギリス連邦}} {{デフォルトソート:いきりす}} [[Category:イギリス|*]] [[Category:英連邦王国|*]] [[Category:G8加盟国]] [[Category:欧州連合加盟国]] [[Category:海洋国家]] [[Category:君主国]] [[Category:島国|くれいとふりてん]] [[Category:1801年に設立された州・地域]] |
とこんな感じになっていればおっけー。
21. カテゴリ名を含む行を抽出
『記事中でカテゴリ名を宣言している行を抽出せよ.』
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#!/usr/bin/env python # -*- coding: utf-8 -*- import json with open("jawiki-country.json") as f: article_json = f.readline() while article_json: article_dict = json.loads(article_json) if article_dict["title"] == "イギリス": lines = article_dict["text"].split("\n") for line in lines: if "Category" in line: print(line) article_json = f.readline() |
特に難しい処理はしていないが、自分的には11行目の”Category” in line:のところが気になった。
lineはlinesの時点で文字列として扱われているので”Category”としていきなりif文の中で宣言して構わないみたいだ。
if の条件文で””を使うことがなかったので、慣れないが自然言語処理なら普通のことなのだろう。
実行結果:
1 2 3 4 5 6 7 8 |
[[Category:イギリス|*]] [[Category:英連邦王国|*]] [[Category:G8加盟国]] [[Category:欧州連合加盟国]] [[Category:海洋国家]] [[Category:君主国]] [[Category:島国|くれいとふりてん]] [[Category:1801年に設立された州・地域]] |
for文を使っているためlineがリスト型になってしまうのは否めない。
スポンサードリンク
22. カテゴリ名の抽出
『記事のカテゴリ名を(行単位ではなく名前で)抽出せよ.』
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import gzip import json import re fname = 'jawiki-country.json.gz' def extract_UK(): '''イギリスに関する記事本文を取得 戻り値: イギリスの記事本文 ''' with gzip.open(fname, 'rt') as data_file: for line in data_file: data_json = json.loads(line) if data_json['title'] == 'イギリス': return data_json['text'] raise ValueError('イギリスの記事が見つからない') # 正規表現のコンパイル pattern = re.compile(r''' ^ # 行頭 .* # 任意の文字0文字以上 \[\[Category: ( # キャプチャ対象のグループ開始 .*? # 任意の文字0文字以上、非貪欲マッチ(貪欲にすると後半の'|'で始まる装飾を巻き込んでしまう) ) # グループ終了 (?: # キャプチャ対象外のグループ開始 \|.* # '|'に続く0文字以上 )? # グループ終了、0か1回の出現 \]\] .* # 任意の文字0文字以上 $ # 行末 ''', re.MULTILINE + re.VERBOSE) # 抽出 result = pattern.findall(extract_UK()) # 結果表示 for line in result: print(line) |
module化しなくてもいけるが、インデントが多くなるのでモジュール化しました。
正規表現を扱うためにreモジュールをインポートします。正規表現とは文字列の並びやパターンを特定するもの。
どんな使い方するのだろうかとかは、こちらの方のサイトを見ていただけるとわかると思います。
ちょっとこれは新しい情報が多すぎて混乱しそうです。そもそもなんでこんなめんどいことしなきゃいけないのか?など聞きたいことは山ほどあります。
compileというコマンドで正規表現パターンを定義してやるぞって意味。
findall()は上で指定した正規表現パターンを()内の引数から抽出するための関数。
正直なところ^とか. とか意味がわからない。おそらく今回のデータが文章構造になっていて一行ごとに読み込み、処理をするようになっているので行頭を指定してやる必要があるのだと思う。
.*で任意の文字0回以上と指定しています。どんな文字でも何回でもおっけーよってことだと思うが、それ指定する必要ねーじゃんって思ってしまう。
独学の限界を少々感じるが、理解できたらおいおい更新していきます。
23. セクション構造
『記事中に含まれるセクション名とそのレベル(例えば”== セクション名 ==”なら1)を表示せよ.』
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import gzip import json import re fname = 'jawiki-country.json.gz' def extract_UK(): '''イギリスに関する記事本文を取得 戻り値: イギリスの記事本文 ''' with gzip.open(fname, 'rt') as data_file: for line in data_file: data_json = json.loads(line) if data_json['title'] == 'イギリス': return data_json['text'] raise ValueError('イギリスの記事が見つからない') # 正規表現のコンパイル # 正規表現のコンパイル pattern = re.compile(r''' ^ # 行頭 (={2,}) # キャプチャ対象、2個以上の'=' \s* # 余分な0個以上の空白('哲学'や'婚姻'の前後に余分な空白があるので除去) (.+?) # キャプチャ対象、任意の文字が1文字以上、非貪欲(以降の条件の巻き込み防止) \s* # 余分な0個以上の空白 \1 # 後方参照、1番目のキャプチャ対象と同じ内容 .* # 任意の文字が0文字以上 $ # 行末 ''', re.MULTILINE + re.VERBOSE) # 抽出 result = pattern.findall(extract_UK()) # 結果表示 for line in result: level = len(line[0]) - 1 # '='の数-1 print('{indent}{sect}({level})'.format( indent='\t' * (level - 1), sect=line[1], level=level)) |
セクションとは記事の見出しです。第1章とか通常の文章部分よりもでかい文字で書いたりする部分のことです。なのでセクション構造とはその文章の章構成とか目次みたいなもんです。
レベルというのはその章とか目次がどの階層にいるかというのを表したものです。一番上の階層が「1」ということです。
1 2 3 4 5 6 7 8 9 10 |
pattern = re.compile(r''' ^ # 行頭 (={2,}) # キャプチャ対象、2個以上の'=' \s* # 余分な0個以上の空白('哲学'や'婚姻'の前後に余分な空白があるので除去) (.+?) # キャプチャ対象、任意の文字が1文字以上、非貪欲(以降の条件の巻き込み防止) \s* # 余分な0個以上の空白 \1 # 後方参照、1番目のキャプチャ対象と同じ内容 .* # 任意の文字が0文字以上 $ # 行末 ''', re.MULTILINE + re.VERBOSE) |
正規表現部分は慣れませんが、実際に自分でやった方が早いのでしょう。一発でなんか絶対うまく行かないと思いますが笑
1 |
(={2,}) # キャプチャ対象、2個以上の'=' |
の部分はセクション名が==セクション名==という形式で書かれているため、2個以上という意味で2が入ります。もう一個階層が下がると=== サブセクション名===という感じでしょうか。
1 |
(.+?) # キャプチャ対象、任意の文字が1文字以上、非貪欲(以降の条件の |
これがないと、例えば
==セクション構造== ===サブセクション=== ====サブサブセクション====みたいな階層構造の場合、最後まで一気に読み込んでしまうということだろうか。?は直前の正規表現で指定したパターンの最小回数(1回)でのマッチングを意味するものなので、次に==が出てきた瞬間とこまでしかマッチングしない。
1 |
\1 # 後方参照、1番目のキャプチャ対象と同じ内容 |
こちらのサイトを参考にすると理解できそうです。
後方参照というものらしく、終わりと開始が同じパターンならそれを利用して\numberで開始を指定してマッチングさせることができるものらしい。
== セクション名 ==みたいな感じで空白があるものがデータに含まれていたため以下のような空白除去が必要らしい
1 |
\s* # 余分な0個以上の空白 |
実行結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
国名(1) 歴史(1) 地理(1) 気候(2) 政治(1) 外交と軍事(1) 地方行政区分(1) 主要都市(2) 科学技術(1) 経済(1) 鉱業(2) 農業(2) 貿易(2) 通貨(2) 企業(2) 交通(1) 道路(2) 鉄道(2) 海運(2) 航空(2) 通信(1) 国民(1) 言語(2) 宗教(2) 婚姻(2) 教育(2) 文化(1) 食文化(2) 文学(2) 哲学(2) 音楽(2) イギリスのポピュラー音楽(3) 映画(2) コメディ(2) 国花(2) 世界遺産(2) 祝祭日(2) スポーツ(1) サッカー(2) 競馬(2) モータースポーツ(2) 脚注(1) 関連項目(1) 外部リンク(1) |
こんな感じ。
24. ファイル参照の抽出
『記事から参照されているメディアファイルをすべて抜き出せ.』
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# coding: utf-8 import gzip import json import re fname = 'jawiki-country.json.gz' def extract_UK(): '''イギリスに関する記事本文を取得 戻り値: イギリスの記事本文 ''' with gzip.open(fname, 'rt') as data_file: for line in data_file: data_json = json.loads(line) if data_json['title'] == 'イギリス': return data_json['text'] raise ValueError('イギリスの記事が見つからない') # 正規表現のコンパイル pattern = re.compile(r''' (?:File|ファイル) # 非キャプチャ、'File'か'ファイル' : (.+?) # キャプチャ対象、任意の文字1文字以上、非貪欲 \| ''', re.VERBOSE) # 抽出 result = pattern.findall(extract_UK()) # 結果表示 for line in result: print(line) |
1 |
(?:File|ファイル) # 非キャプチャ、'File'か'ファイル' |
メディアファイルが何を意味しているのかわからなかったのですが、jpgとかsvgファイルのことらしいです。そしてそれらのファイルの前にファイルとかfileとか書かれているのでそれを指定する正規表現が上の一行です。
verboseは正規表現を便利に書きやすくするための関数です。公式ドキュメントにも
「パターンの論理的なセクションを視覚的に区切り、コメントも入れることができます。」と書かれています。
今回はこれまでのができていれば簡単にできる問題です。
実行結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
Royal Coat of Arms of the United Kingdom.svg Battle of Waterloo 1815.PNG The British Empire.png Uk topo en.jpg BenNevis2005.jpg Elizabeth II greets NASA GSFC employees, May 8, 2007 edit.jpg Palace of Westminster, London - Feb 2007.jpg David Cameron and Barack Obama at the G20 Summit in Toronto.jpg Soldiers Trooping the Colour, 16th June 2007.jpg Scotland Parliament Holyrood.jpg London.bankofengland.arp.jpg City of London skyline from London City Hall - Oct 2008.jpg Oil platform in the North SeaPros.jpg Eurostar at St Pancras Jan 2008.jpg Heathrow T5.jpg Anglospeak.svg CHANDOS3.jpg The Fabs.JPG PalaceOfWestminsterAtNight.jpg Westminster Abbey - West Door.jpg Edinburgh Cockburn St dsc06789.jpg Canterbury Cathedral - Portal Nave Cross-spire.jpeg Kew Gardens Palm House, London - July 2009.jpg 2005-06-27 - United Kingdom - England - London - Greenwich.jpg Stonehenge2007 07 30.jpg Yard2.jpg Durham Kathedrale Nahaufnahme.jpg Roman Baths in Bath Spa, England - July 2006.jpg Fountains Abbey view02 2005-08-27.jpg Blenheim Palace IMG 3673.JPG Liverpool Pier Head by night.jpg Hadrian's Wall view near Greenhead.jpg London Tower (1).JPG Wembley Stadium, illuminated.jpg |
とこんな感じです。
次は第3章後半です。
スポンサードリンク