前提・実現したいこと
現在、「機動戦士ガンダム」シリーズに関するWebページ25個のHTMLデータを一つにまとめたデータ'/home/urai/crawling/gandamu/gandamu uchuuseiki/26.html'を入力として、BeautifulSoupでテキスト部分のみを抽出したテキストから「作中の年代」「作中の出来事」「作品名」の三つの要素に適合するテキストを正規表現を用いて抽出し、年表形式にまとめるための前段階として、抽出した要素をリストとして出力するプログラムを作成しています。
「作中の年代」のリストに要素が格納された際の処理として、「作中の出来事」「作品名」に適合するテキストも同時に抽出したいと考えており、その際の抽出範囲を「「作中の年代」のリストに要素が格納された行、あるいはその行から±5行以内」に絞って抽出した上で単純に抽出された順番でリストに格納、そのリスト内の重複を排除する処理を施し、重複が排除されたリストを出力したいと考えています。
発生している問題・エラーメッセージ
しかし、「作中の出来事」「作品名」を抽出できる範囲を「「作中の年代」のリストに要素が格納された行」と「入力データ全体から無差別」に指定はできているのですが、「「作中の年代」のリストに要素が格納された行から±5行以内の要素も抽出」という処理が実現できずに困っています。
該当のソースコード
Python3
1#coding:utf-8 2import re 3from bs4 import BeautifulSoup 4import pandas as pd 5 6results_hi = []#「作中の年代」を格納しておく関数 7results_t = []#「作品名」を格納しておく関数 8results_ha = []#「作中の出来事」を格納しておく関数 9count_line = -1#行数をカウントするための関数 10 11#抽出で用いる正規表現 12absobe_pattern_history = 'U.C.[0-9]{4}|宇宙世紀[0-9]{4}'#宇宙世紀の年号 13absobe_pattern_title = '機動戦士ガンダム[ \t\n\r\f\v一-龥ぁ-んァ-ンa-zA-ZΖ0-9_ー・.]{1,}|機動戦士ガンダム|機動戦士[\t\n\r\f\v一-龥ぁ-んァ-ンa-zA-ZΖ0-9_ー・.]{1,}ガンダム|機動戦士[\t\n\r\f\v一-龥ぁ-んァ-ンa-zA-ZΖ0-9_ー・.]{1,}ガンダム[ \t\n\r\f\v一-龥ァ-ンa-xA-ZΖ0-9_ー・]{1,}|ガンダム・センチネル'#作品タイトル抽出パターン 14absobe_pattern_happen = '[\t\n\r\f\v一-龥ァ-ンa-zA-Z0-9_ー・]{1,}戦争|[\t\n\r\f\v一-龥ァ-ンa-zA-Z0-9_ー・]{1,}戦役|[\t\n\r\f\v一-龥ァ-ンa-zA-Z0-9_ー・]{1,}紛争|[\t\n\r\f\v一-龥ァ-ンa-zA-Z0-9_ー・]{1,}抗争|[\t\n\r\f\v一-龥ァ-ンのa-zA-Z0-9_ー・]{1,}事件|[\t\n\r\f\v一-龥ァ-ンのa-zA-Z0-9_ー・]{1,}反乱|[\t\n\r\f\v一-龥ァ-ンのa-zA-Z0-9_ー・]{1,}計画|[\t\n\r\f\v一-龥ァ-ンa-zA-Z0-9_ー・]{1,}動乱'#出来事抽出パターン 15 16def math(c,t):#要素抽出を担うモジュール 17 matchedList_h = re.finditer(absobe_pattern_history,t)#条件に適合した文(年号)をイテレータとして返す 18 matchedList_ha = re.findall(absobe_pattern_happen,t)#条件に適合した文(年号)を抽出、matchedList_haに格納 19 matchedList_t = re.findall(absobe_pattern_title,t)#条件に適合した文(年号)を抽出、matchedList_tに格納 20 if matchedList_h:#もし適合していたら 21 for hi in matchedList_h: 22 results_hi.append(hi.group())#マッチした文字列をresults_hiに格納 23 print('math:',c,' ',hi.start(),' ',text,' ',hi.group())#マッチした行を擬似的に表示できてるか確認 24 print(' ') 25 results_ha.append(matchedList_ha)#esults_haに格納 26 results_t.append(matchedList_t)#results_tに格納 27 else:#ここ以下はやけくそに書いてるので根本的な解決策ではない 28 results_ha.append(matchedList_ha)#esults_haに格納 29 results_t.append(matchedList_t)#results_tに格納 30 return 31 32# encoding は取得したページの文字コードを選択 33filepath = '/home/urai/crawling/gandamu/gandamu uchuuseiki/26.html' 34with open(filepath , encoding='utf-8') as f: 35 html = f.readlines()#入力データの各行を要素としたリストを取得 36 for i in html: 37 soup = BeautifulSoup(i, 'html.parser')#Soupでhtmlからデータを取る 38 for text in soup.find_all(text=True):#テキストのみを抽出 39 text.strip()#空白、改行を排除 40 count_line += 1#行数をカウント 41 math(count_line,text) 42 f.close() 43 44 UC_title = [t[0] for t in results_t if t != []]#リスト内の空のリストを消して入れ子構造を解消 45 UC_happen = [ha[0] for ha in results_ha if ha != []]#上に同じ 46 47 UCH = ",".join(results_hi)#置き換え処理のために一度リストを一つの文字列にする 48 UC_history2 = re.sub(r'宇宙世紀([0-9]{4})', r'U.C.\1',UCH)#re.subで"宇宙世紀"の部分を"U.C."に置き換え 49 UCH = UC_history2.split(",")#リストに戻す、これで表記を統一 50 51 UCHA = [i for i in UC_happen if not re.search('.*画像.*', i) and not re.search('.*Blu.*', i)\ 52 and not re.search('.*内容.*', i) and not re.search('.*DVD.*', i) and not re.search('.*BOX.*', i)\ 53 and not re.search('.*ボックス*', i) and not re.search('.*コミック*', i) and not re.search('.*公式*', i)\ 54 and not re.search('.*作画*', i) and not re.search('.*シリーズ*', i)]#「作中の出来事」を内包したリストからノイズとなる単語を含んだ要素の排除したリストの生成 55 56 UCT = [i for i in UC_title if not re.search('.*画像.*', i) and not re.search('.*Blu.*', i)\ 57 and not re.search('.*内容.*', i) and not re.search('.*DVD.*', i) and not re.search('.*BOX.*', i)\ 58 and not re.search('.*ボックス*', i) and not re.search('.*コミック*', i) and not re.search('.*公式*', i)\ 59 and not re.search('.*作画*', i) and not re.search('.*シリーズ*', i) and not re.search('.*図鑑*', i)\ 60 and not re.search('.*年表*', i) and not re.search('.*時系列*', i) and not re.search('.*ガンプラ*', i)\ 61 and not re.search('.*設定*', i)and not re.search('.*ナラティブ*', i)and not re.search('.*辞典*', i)\ 62 and not re.search('.*全集*', i)and not re.search('.*記録*', i)and not re.search('.*アニメ*', i)]#「作品名」を内包したリストからノイズとなる単語を含んだ要素の排除したリストの生成 63 64 his_list = sorted(set(UCH), key=UCH.index)#sortedでリスト内部の重複を排除 65 tit_list = sorted(set(UCT), key=UCT.index)#上に同じ 66 hap_list = sorted(set(UCHA), key=UCHA.index)#上に同じ 67 68 his_list.sort()#his_listの中身を古い>新しいの順番に並び替える 69 70 print(his_list) 71 print(hap_list) 72 print(tit_list) 73 print(count_line)
(上部省略、リスト以外の部分は入力データの内容と抽出されているかの確認のために出力しています) (math:に続いている数字で「作中の年代」が抽出された行、列、テキストを確認) math: 40784(行) 22(列) 機動戦士ガンダム閃光のハサウェイ(時系列:宇宙世紀0105) 宇宙世紀100年代。 宇宙世紀0105(抽出された要素を表示) math: 40791(行) 13(列) ガンダムF91(時系列:宇宙世紀0123-0128) 長引く平和の間に地球連邦政府は再び腐敗しており、これに対しマイッツァー・ロナは「人の上に立つべき者は、人々の規範となるような高貴な精神を持つ者でなければならない」とする思想「コスモ貴族主義」を掲げ、地球連邦政府の打倒と理想とする貴族主義社会の実現のために、秘密裏に軍事組織 クロスボーン・バンガード(C・V)を設立していた。 宇宙世紀0123(抽出された要素を表示) math: 40797(行) 83 (列) 機動戦士ガンダムF91 [Blu-ray] posted with カエレバ 辻谷耕史 バンダイビジュアル 2011-06-24 Amazon Vガンダム(時系列:U.C.0153) 主人公ウッソ・エヴィンが幼馴染のシャクティ・カリンを守るためにリガ・ミリティアに参加するが、戦いは熾烈さを増し、オイ・ニュング伯爵のギロチン刑や、ガンイージを駆る女性部隊・シュラク隊のメンバーが次々と戦死していく様など、生々しい戦場の現実を見せつけられる。 U.C.0153(抽出された要素を表示) (his_list)['U.C.0001', 'U.C.0058', 'U.C.0063', 'U.C.0068', 'U.C.0071', 'U.C.0074', 'U.C.0077', 'U.C.0078', 'U.C.0079', 'U.C.0080', 'U.C.0081', 'U.C.0082', 'U.C.0083', 'U.C.0084', 'U.C.0085', 'U.C.0087', 'U.C.0088', 'U.C.0089', 'U.C.0090', 'U.C.0091', 'U.C.0092', 'U.C.0093', 'U.C.0094', 'U.C.0095', 'U.C.0096', 'U.C.0097', 'U.C.0099', 'U.C.0100', 'U.C.0103', 'U.C.0104', 'U.C.0105', 'U.C.0106', 'U.C.0107', 'U.C.0120', 'U.C.0121', 'U.C.0122', 'U.C.0123', 'U.C.0133', 'U.C.0136', 'U.C.0140', 'U.C.0143', 'U.C.0149', 'U.C.0153', 'U.C.0168', 'U.C.0169', 'U.C.0203', 'U.C.0218', 'U.C.0223', 'U.C.0653', 'U.C.1000'] (hap_list)['ラプラス事件', '一年戦争', '南洋同盟の反乱', 'デラーズ紛争', 'グリプス戦役', 'ペズンの反乱', '第一次ネオ・ジオン抗争', 'レジオン建国戦争', '第二次ネオ・ジオン抗争', '強化人間量産化計画', 'ラプラス戦争', 'バーナム動乱', 'マフティー戦争', 'オールズモビル戦役', '第一次オールズモビル戦役', '第二次オールズモビル戦役', 'ゼブラゾーン事件', 'コスモ・バビロニア建国戦争', '木星戦役', '神の雷計画', 'ザンスカール戦争', 'マハの反乱', 'ガイアの光事件', '独立戦争', (以下省略)] (tit_list)['機動戦士ガンダム', '機動戦士Ζガンダム', '機動戦士ガンダムUC', '機動戦士クロスボーン・ガンダム', '機動戦士ガンダム0080 ポケットの中の戦争', '機動戦士Vガンダム', '機動戦士ガンダム 逆襲のシャア', '機動戦士ガンダム THE ORIGIN', '機動戦士ガンダム サンダーボルト', '機動戦士ガンダム 逆襲のシャア ベルトーチカ・チルドレン', '機動戦士ガンダム 閃光のハサウェイ', '機動戦士ガンダム シルエットフォーミュラ91', '機動戦士ガンダムF91', '機動戦士ガンダム 第08MS小隊', '機動戦士ガンダム MS IGLOO', '機動戦士ガンダム\u3000サンダーボルト', '機動戦士ガンダム0083 STARDUST MEMORY', '機動戦士ガンダム カタナ', '機動戦士ガンダム0083 REBELLION', (以下省略)] 40961(入力データの行数)
試したこと
モジュールmath(c,t)のelse以下の部分で暫定的に抽出できた要素を無差別に格納できているようにしていますが、この抽出範囲をある程度制限することが目的なので、解決策にはなっていません。
行数を計算するcount_line関数を実装し、「作中の年代」がどの行で抽出されたか確認できるようにはしていますが、これをどう活用すればいいのかもわからないです。
def math(c,t):#要素抽出を担うモジュール matchedList_h = re.finditer(absobe_pattern_history,t)#条件に適合した文(年号)をイテレータとして返す matchedList_ha = re.findall(absobe_pattern_happen,t)#条件に適合した文(年号)を抽出、matchedList_haに格納 matchedList_t = re.findall(absobe_pattern_title,t)#条件に適合した文(年号)を抽出、matchedList_tに格納 if matchedList_h:#もし適合していたら for hi in matchedList_h: results_hi.append(hi.group())#マッチした文字列をresults_hiに格納 print('math:',c,' ',hi.start(),' ',text,' ',hi.group())#マッチした行を擬似的に表示できてるか確認 print(' ') results_ha.append(matchedList_ha)#esults_haに格納 results_t.append(matchedList_t)#results_tに格納 else:#ここ以下はやけくそに書いてるので根本的な解決策ではない results_ha.append(matchedList_ha)#esults_haに格納 results_t.append(matchedList_t)#results_tに格納 return
補足情報(FW/ツールのバージョンなど)
pythonのバージョンは3.6.5です。
入力データである'/home/urai/crawling/gandamu/gandamu uchuuseiki/26.html'の中身は「機動戦士ガンダム」内の歴史である「宇宙世紀」について記された複数のWebページのHTMLデータを一つにまとめた物になります。(「ガンダム 宇宙世紀」とグーグル検索して出てきた上位25ページのHTMLをまとめている)
抽出の際にはHTMLに記載されたテキストは一行ずつ読み取られ、文字列となります。
事前の実験の結果、各要素に該当するテキストの数は均一ではありません。
また、3つ全ての要素の数が揃っていない場合は考えなくてよいものとしています。
あなたの回答
tips
プレビュー