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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Beautiful Soup

Beautiful Soupは、Pythonのライブラリの一つ。スクレイピングに特化しています。HTMLデータの構文の解析を行うために、HTMLタグ/CSSのセレクタで抽出する部分を指定することが可能です。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

selenium

Selenium(セレニウム)は、ブラウザをプログラムで作動させるフレームワークです。この原理を使うことにより、ブラウザのユーザーテストなどを自動化にすることができます。

Q&A

解決済

3回答

654閲覧

pythonでのseleniumとBeautifulSoupのselect()を使用したループ時のIndexErrorについて

ishiha

総合スコア6

Beautiful Soup

Beautiful Soupは、Pythonのライブラリの一つ。スクレイピングに特化しています。HTMLデータの構文の解析を行うために、HTMLタグ/CSSのセレクタで抽出する部分を指定することが可能です。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

selenium

Selenium(セレニウム)は、ブラウザをプログラムで作動させるフレームワークです。この原理を使うことにより、ブラウザのユーザーテストなどを自動化にすることができます。

0グッド

0クリップ

投稿2022/08/17 02:38

編集2022/08/17 04:11

前提

PythonのseleniumとBeautifulSoupを使ってWebサイトを操作するプログラムを作っています。

HTMLテーブルのtr要素をsoup.find_all("tr")でリスト化し、while i <= len(array) - 1で繰り返し処理しています。

繰り返し処理内の最後で使用した配列の要素をdel array[i]削除して、配列の要素がなくなるまで繰り返すという形です。

tr要素内には数字を含むIDセレクタが割り当てられており、変数iと同じ数字を持っています。

ループの1回目はうまく機能するのですが、2回目になるとsoup.select()の部分でIndexError:list index out of rangeが発生してしまいます。

発生している問題・エラーメッセージ

python

1Traceback (most recent call last): 2 File "C:\Users\Administrator\osbot\test_os-bot_2.1.py", line 99, in <module> 3 os_td_id = str(os_td_id_pre[0].contents).lstrip("['").rstrip("']") 4IndexError: list index out of range

該当のソースコード

python

1# 前略 2# 2.1.1 HTMLをBeautifulSoupで解析 3driver.get('https://example.com') 4os_html = driver.page_source.encode('utf-8') 5os_soup = BeautifulSoup(os_html, 'lxml') 6 7# サイト内メインテーブル 8os_table = os_soup.find('table', id="table1") 9os_tr_array = os_table.tbody.find_all('tr', recursive=False) 10 11# tr要素で不要な最初の要素を削除 12del os_tr_array[0] 13 14# ループ前に変数初期化 15i = 0 16 17# ループ用の値をセット 18os_tr_array_len = len(os_tr_array) - 1 19 20# メインループ(while版) 21# テーブルのtr要素が存在する場合にループ 22while i <= os_tr_array_len: 23 # 配列から処理する行を指定 24 os_tr = os_tr_array[i] 25 # ウインドウを所定の位置へ 26 driver.switch_to.window(driver.window_handles[0]) 27 # ソースコードを取得してパーサー処理 28 os_tr_html = os_tr.encode('utf-8') 29 os_tr_soup = BeautifulSoup(os_tr_html, 'lxml') 30 31 # メモ欄を取得して変数に格納 32 os_td_memo_css = 'memo' + str(i) + 'txtmemo_' + str(i) + '_I' 33 os_td_memo_box = driver.find_element(By.ID, os_td_memo_css) 34 35 # 例外処理:メモ欄に何か入力されている場合はスキップする 36 os_td_memo_box_val = os_td_memo_box.get_attribute('value') 37 if os_td_memo_box_val: 38 i += 1 39 continue 40 41 # 注文Noを取得 42 os_td_id_css = '#no + str(i) + 'id_' + str(i) 43 os_td_id_pre = os_tr_soup.select(os_td_id_css) 44 print(os_td_id_pre) 45 os_td_id = str(os_td_id_pre[0].contents).lstrip("['").rstrip("']") 46 print("商品の情報を入手します...") 47 print(os_td_id) 48 49 driver.refresh() 50 51 # 次のループ前処理 52 del os_tr_array[i] 53 os_tr_array_len = len(os_tr_array) - 1

試したこと

2回目のループでもprint(os_tr)で値が取得されているかを確認しました。
→そこは正常に取得されていました

独学なのでコードも見づらいかと思いますが、ご教授いただければ幸いです。
よろしくお願いいたします。

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

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

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

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

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

meg_

2022/08/17 02:42

エラーが発生したときの「os_td_id_pre」の値がどうなっているか確認されましたか?
ishiha

2022/08/17 04:54

ありがとうございます。 いま少し離れているので確認できないのですが、戻り次第確認してみます!
ishiha

2022/08/17 10:11

お待たせいたしました。 「os_td_id_pre」の値ですが、print()してみたところ、 初回の値が [<span id="no0id_0" style="display:inline-block;width:170px;">341</span>] 2回目の値が [] ↑空となっていました。 配列の要素は5個以上あるので、2回目でout of rangeとなってしまう理由が不明です。。
meg_

2022/08/17 10:39

ループ2回目のときに、os_tr_array[i]には目的の値がありますか?→os_tr_soupは解析したいhtmlとなっていますか?
guest

回答3

0

ベストアンサー

while i <= os_tr_array_len:で、iを確認した上で、i += 1していますが、
元のios_tr_array_lenと等しい場合、+1するとos_tr_array_len+1となって、
添え字範囲を超えますので、del os_tr_array[i]がエラーになります。

削除する方法でもちゃんと考えれば出来ると思いますが、
「残したい物だけ残し、そうで無い物は削除する」より「もとのリストはそのままにして、残したい物だけからなる新しいリストを作る」方がわかりやすいです。

どうしても削除する場合は、後ろから順に処理してよければ、後ろから処理するとロジックが簡単になります。

投稿2022/08/17 07:01

otn

総合スコア84551

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

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

ishiha

2022/08/18 04:05

回答ありがとうございます。 TakaiYさんの回答を含め、「シンプルにすること」 「元のリストはそのままにして、残したいものだけからなる新しいリストをつくる」 というアドバイスからコードを修正したところ、無事思った挙動にすることができました。 コード自体は自己解決の方に記載しますが、直接的な解決のきっかけとなったotnさんの回答をベストアンサーとさせていただきます。 ありがとうございました。
guest

0

配列の要素を順に処理するにあたって、そのループの中で配列の要素を削除していくのは筋が悪いと思います。

python

1while i <= os_tr_array_len: 2 # 配列から処理する行を指定 3 os_tr = os_tr_array[i] 4 # 処理 5 del os_tr_array[i] 6 os_tr_array_len = len(os_tr_array) - 1

こうでなく、

python

1for os_tr in os_tr_array: 2 # 処理

これで充分だと思います。
これではだめな理由はありますか?

投稿2022/08/17 02:56

TakaiY

総合スコア12765

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

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

ishiha

2022/08/17 04:24

回答ありがとうございます。 このような処理にした理由もあるのですが、確かにシンプルな形に変更した方がうまくいくかもしれませんね。 一旦その形でできないか検討して書き直してみます。 追記があればまた報告させていただきます。
guest

0

ご回答頂いた内容を参考にコードを修正したところ、解決しましたのでこちらにその修正箇所を記載します。

修正前

python

1 driver.refresh() 2 3 # 次のループ前処理 4 del os_tr_array[i] 5 os_tr_array_len = len(os_tr_array) - 1

修正後

python

1 driver.refresh() 2 3 # 次のループ前処理 4 # 更新したページの内容で配列を再生成 5 os_html = driver.page_source.encode('utf-8') 6 os_soup = BeautifulSoup(os_html, 'lxml') 7 os_table = os_soup.find('table', id="PageContent_ASPxSplitter1_GridView_DXMainTable") 8 # 配列を更新 9 os_tr_array = os_table.tbody.find_all('tr', recursive=False) 10 # tr要素で不要な最初の要素を削除 11 del os_tr_array[0] 12 os_tr_array_len = len(os_tr_array) - 1

以上になります。
かなりの長時間悩んでいたので、様々な視点からのアドバイスをいただくことができ、とても参考になりました。
ありがとうございました。

投稿2022/08/18 04:14

ishiha

総合スコア6

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問