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

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

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

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

Q&A

解決済

2回答

177閲覧

Pythonで正規表現を使って似たような文字列を複数個取得する際に、それを取得した場所によって場合分けを行うことができるのかどうか

nsmt

総合スコア17

Python 3.x

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

0グッド

1クリップ

投稿2019/04/03 00:51

※まずタイトルがこれでいいのかどうか不安なので、もしこのタイトルよりもこの質問に適切だと思われるタイトルがございましたらそれについてもコメントいただけると嬉しいです。

前提・実現したいこと

とあるゲームの棋譜からデータを抽出しようとしています。
変数textに格納された文字列から「occupation "〇〇"」の部分の〇〇を正規表現を用いてプレイヤーごとにリストの形式で取り出そうとしているのですがうまくいきません。この問題の解決に力を貸していただけると助かります。

text = ''' <table> <tr> <td>3</td> # 3ターン目(ここから先のコメントは全て実際のtextには含まれておりません。) <td>...</td> # player1 <td>occupation "Clay Seller"</td> # player2 <td>...</td> # player3 </tr> <tr> <td>2</td> # 2ターン目 <td>occupation "Field Watchman"</td> # player1 <td>occupation "Tutor"</td> # player2 <td>...</td> # player3 </tr> <tr> <td>1</td> # 1ターン目 <td>...</td> # player1 <td>occupation "Cook"</td> # player2 <td>...</td> # player3 </tr> </table> ''' # 望んでいる結果: player1 = [Field Watchman] player2 = [Clay Seller, Tutor, Cook] player3 = []

試したこと・発生している問題

Python3

1player1 = [] 2try: 3 for a in range(1,4): 4 player1_regex = re.compile(r''' 5 (<tr>.*?){%s} 6 # {}内に変数aを入れて、occupation\s"(.*?)"までにある 7 # (<tr>.*?)の個数でターン数を管理し、 8 # forループで各ターンから「occupation "〇〇"」を抽出していこうとしている 9 (<td>.*?</td>.*?){1} 10 # 何ターン目かが書いてある行から、「occupation "(.*?)"」までの間にある 11 # (<td>.*?</td>.*?)の個数で何番目のプレイヤーが選択した行動かを判別しようとした 12 occupation\s"(.*?)" 13 ''' % a, re.VERBOSE | re.DOTALL) 14 player1.append(player1_regex.search(text).group(3)) 15except AttributeError: # 例外処理でマッチしなかった場合(<td>...</td>となっている部分が該当する時)にエラーが出ないようにした。 16 pass 17print(player1) 18# ['Clay Seller', 'Field Watchman', 'Cook'] 19 20player2 = [] 21try: 22 for b in range(1,4): 23 player2_regex = re.compile(r''' 24 (<tr>.*?){%s} 25 (<td>.*?</td>.*?){2} 26 occupation\s"(.*?)" 27 ''' % b, re.VERBOSE | re.DOTALL) 28 player2.append(player2_regex.search(text).group(3)) 29except AttributeError: 30 pass 31print(player2) 32# ['Clay Seller', 'Tutor', 'Cook'] 33 34player3 = [] 35try: 36 for c in range(1,4): 37 player3_regex = re.compile(r''' 38 (<tr>.*?){%s} 39 (<td>.*?</td>.*?){3} 40 occupation\s"(.*?)" 41 ''' % c, re.VERBOSE | re.DOTALL) 42 player3.append(player3_regex.search(text).group(3)) 43except AttributeError: 44 pass 45print(player3) 46# ['Field Watchman', 'Cook']

上記コードで試しましたが、結果は期待したものとは違っていました。
これがうまくいかない理由は、{}は直前のグループの内容を管理するものではあるが
それ以降の正規表現に影響を与えるものではないからだと分かりました。

次はこのように試しました。

Python3

1player1 = [] 2try: 3 player1_r1 = re.compile(r''' 4 <tr>.*? 5 <td>.*?</td>.*? 6 occupation\s"(.*?)" 7 ''', re.VERBOSE | re.DOTALL) 8 player1.append(player1_r1.search(text).group(1)) 9 player1_r2 = re.compile(r''' 10 <tr>.*?<tr>.*? 11 <td>.*?</td>.*? 12 occupation\s"(.*?)" 13 ''', re.VERBOSE | re.DOTALL) 14 player1.append(player1_r2.search(text).group(1)) 15 player1_r3 = re.compile(r''' 16 <tr>.*?<tr>.*?<tr>.*? 17 <td>.*?</td>.*? 18 occupation\s"(.*?)" 19 ''', re.VERBOSE | re.DOTALL) 20 player1.append(player1_r3.search(text).group(1)) 21except AttributeError: 22 pass 23print(player1) 24# ['Clay Seller', 'Field Watchman', 'Cook']

長いので、player1の分だけ載せました。
{}を使わずに「<tr>.?」をそのまま連ねることで「<tr>.?」の個数を指定して
3ターン目...該当無し...実際はClay Sellerを取得しちゃってる
2ターン目...Field Watchman...予定通りField Watchmanを取得
1ターン目...該当無し...実際はCookを取得
思い通りに動かない理由は、「<tr>.?」1つ連ねただけだと、これが1つ以上ある時はいつでもマッチしてしまうからです。
<tr>.
?<tr>.?」なら、「<tr>.?」が2つ以上ある時はマッチする。

では、どうすれば「<tr>.*?」がtextの最初から数えて1つしかない場合にしかマッチしないようにしたりするのでしょうか?
正規表現を使ったもの以外でもよいので、何か方法があれば教えてくださると助かります。
よろしくお願いします。

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

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

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

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

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

guest

回答2

0

ベストアンサー

pandas 使って2次元テーブルのまま処理したほうが簡単な気がしますが、駄目でしょうかね?

Python

1import pandas as pd 2import io 3 4text = ''' 5<table> 6 <tr> 7 <td>3</td> 8 <td>...</td> 9 <td>occupation "Clay Seller"</td> 10 <td>...</td> 11 </tr> 12 <tr> 13 <td>2</td> 14 <td>occupation "Field Watchman"</td> 15 <td>occupation "Tutor"</td> 16 <td>...</td> 17 </tr> 18 <tr> 19 <td>1</td> 20 <td>...</td> 21 <td>occupation "Cook"</td> 22 <td>...</td> 23 </tr></table> 24''' 25 26df = pd.read_html(io.StringIO(text))[0] 27df.columns = ['ターン','Player1','Player2','Player3'] 28# Player1~Player3の列に対して正規表現を実施 29for col in ['Player1','Player2','Player3']: 30 df[col] = df[col].str.extract(r'occupation\s"(.*)"') 31print(df) 32# ターン Player1 Player2 Player3 33#0 3 NaN Clay Seller NaN 34#1 2 Field Watchman Tutor NaN 35#2 1 NaN Cook NaN 36 37# Player1 を表示 38print(df['Player1'].dropna().values) 39#['Field Watchman'] 40# Player2 を表示 41print(df['Player2'].dropna().values) 42#['Clay Seller' 'Tutor' 'Cook'] 43# Player3 を表示 44print(df['Player3'].dropna().values) 45#[]

投稿2019/04/03 02:24

magichan

総合スコア15898

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

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

nsmt

2019/04/03 03:06

教えていただいたコードを入れてみたところ望んでいたリストを得ることができました。 何を勉強すればこの問題を解決できるのかさえ分からなかったので、本当に助かりました。 今からpandasについて学び、似たような問題で躓かないようにします! 本当にありがとうございました。
guest

0

Python は分からないのですが, 正規表現を使わなくても宜しいのでしたら, 一行ずつ読んで状況に応じて取り込めばいいだけではないでしょうか.

投稿2019/04/03 01:09

jimbe

総合スコア12625

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

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

nsmt

2019/04/03 03:00

そういった方法もあるかもしれないのですね。 既に別の方が回答してくださった内容で問題は解決できましたが、今後のためにその方法での解決法も模索してみます。 解答してくださり本当にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問