質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

0回答

129閲覧

正規表現を用いて要素の抽出する際に、連動して複数行から他の要素を抽出したい

gran-1123

総合スコア18

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2019/02/13 07:29

編集2022/01/12 10:55

前提・実現したいこと

現在、「機動戦士ガンダム」シリーズに関する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つ全ての要素の数が揃っていない場合は考えなくてよいものとしています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

wwbQzhMkhhgEmhU

2019/02/13 19:13

前回も書きましたが、コーディング方法以前の段階なんですよ。 まずは自分の要件に合うように、かつ自分が出来る範囲の抽出仕様を決めないといけません。 HTMLは定形の文章ではなく、自然文の散文なんですよね? だとすると、「作中の年代」「作中の出来事」「作品名」の出現順序は一意にならないのではないでしょうか?しかも、「作中の年代」や「作品名」って一回しか出ないうちに作中の出来事は複数出てしまいませんか? さらに、「作中の出来事」の記述が「作中の年代」より前に書いてあったら、それはいつの出来事かどうやって特定するんでしょう? これらには一定のルールが必要なんですよ。 それを元に正規表現でどう抽出するか?できるのか?を考える必要があります。 まずはルールが先なんです。 それが決まったらコーディングが出来るので、実際に自分でコーディングをして、その中で技術的に出来ないと思しきことを他人に尋ねることができるようになります。 現状はルールが未確定です。前回よりはかなり良くなったと思いますが、もう一度よーく考えてから質問してください。 以下気づいたことだけ。 ・複数行からマッチングをかけたいなら、同じHTMLから抽出された文字列は一度joinしちゃって1つの文字列にしとけばいいと思います。行数とか要らなくないですか?恐らく必要なのは順序です。 ・順序を決めれば後はそれに従って抽出すればいいかと思います ・どちらかに決められ場合は、両方抽出してしまうというのも手です  (人間が後から判断するとか、検索にしか使用しないからそれほどの精度は要らないとか、用途により判断) 頑張ってください
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問