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

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

ただいまの
回答率

89.06%

beautiful soup4 WEBスクレイピング list index out of rangeの対処方法

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 348

nasu0922

score 17

前提・実現したいこと

特定のサイト内の全国データをbeautiful soup4で収集しています。
情報収集の中に「更新日」(変数はup_date)というものがありますが、特定店舗にて「list index out of range」のエラーが発生しました。
何故エラーが発生しているかが知りたいのと、エラー発生店舗の更新日が取得できるソースコードがあればご教授いただきたいです。

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

例外が発生しました: IndexError
list index out of range
up_date = shop.find_all('div',class_='lead')[0].get_text(strip=True)

該当のソースコード

if __name__ == "__main__":

    # requestsを使って、webから取得
    base_url = 'https://p-town.dmm.com'
    target_url = '/'
    r = requests.get(base_url + target_url, headers=headers)
    soup = BeautifulSoup(r.text, 'lxml')

    selector = 'body > div.o-layout > div > div.o-container > main > section.default-box.-shop > div > div li'
    # 都道府県ループ
    for pref_ in soup.select(selector):
        tmp_dict = {}
        string_ = pref_.text
        target_url = pref_.next_element.attrs.get('href')
        #target_url = '/shops/saga'    #デバッグ用
        area_name = target_url.rsplit('/', 1)[1]
        tmp_dict = {area_name:{}}
        r = requests.get(base_url + target_url, headers=headers)
        todofuken = target_url
        soup= BeautifulSoup(r.text, 'lxml')
        num = 0
        # 市区町村ループ
        for city_ in soup.find_all('a', class_='link', href=re.compile(r'/shops/' + area_name + '/area/\d+')):
            target_url = city_.attrs.get('href')
            city_id = target_url.rsplit('/', 1)[1]
            tmp_dict[area_name].update({city_id:{}})
            print(city_.text + ':' + base_url + target_url)
            r = requests.get(base_url + target_url, headers=headers)
            soup = BeautifulSoup(r.text, 'lxml')
            selector = 'body > div.o-layout > div > div.o-container > main > section li'
            nextpage = True
            while nextpage:
                # 次ページ有無チェック
                for pages_ in soup.select(selector):
                    if pages_.attrs.get('class')[0] == 'item':
                        if pages_.text == '>':
                            if pages_.get('href') is not None:
                                nextpage = True
                                break
                        else:
                            nextpage = False
                # 登録ホールループ
                for pages_ in soup.select(selector):
                    kisyu_list = {}
                    if pages_.attrs.get('class')[0] == 'unit':
                        # ホール情報収集
                        num += 1
                        target_url = pages_.next_element.attrs.get('href')
                        hall_id = target_url.rsplit('/', 1)[1]
                        time.sleep(random.randint(1, 10))   #スリープ(1秒~10秒)
                        r2 = requests.get(base_url + target_url, headers=headers)
                        get_date = datetime.now(JST)
                        soup2 = BeautifulSoup(r2.text, 'lxml')
                        tmp_dict[area_name][city_id].update({hall_id:{}})
                        shop_url = base_url + todofuken + '/' + hall_id
                        print(str(num) + '[' + hall_id + ']:' + shop_url)
                        r3 = requests.get(shop_url, headers=headers)
                        soup3 = BeautifulSoup(r3.text, 'lxml')
                        selector = 'body > div.o-layout > div > div.o-container > main > div:nth-child(4)'
                        #機種情報取得
                        for shop in soup3.select(selector):
                            #更新日取得
                            up_date = shop.find_all('div',class_='lead')[0].get_text(strip=True)
                            up_date = up_date.replace('更新日:', '')
                            up_date = "".join(up_date.split())
                            tmp_dict[area_name][city_id][hall_id].update({up_date:{}})
                            #種別取得
                            for type_ in soup3.find_all('h4', class_='title', id=re.compile(r'anc-machine-rate-icon-\d+')):
                                machine_type = type_.text
                                tmp_dict[area_name][city_id][hall_id][up_date].update({machine_type:{}})
                                #機種ID取得
                                for a in shop.select('a[class="link"]'):
                                    if 'href' in a.attrs:
                                        machine_url = a.attrs['href']
                                        machine_id = machine_url.rsplit('/', 1)[1]
                                    else:
                                        machine_id = '機種ID無'
                                    tmp_dict[area_name][city_id][hall_id][up_date][machine_type].update({machine_id:{}})
                                    #台数取得
                                    machine_num = a.parent.next_sibling.next_element.get_text(strip=True)                                    kisyu_list['台数'] = machine_num
                                    tmp_dict[area_name][city_id][hall_id][up_date][machine_type][machine_id].update(kisyu_list)
                    # 次ページ読込、なければループ終了
                    elif pages_.attrs.get('class')[0] == 'item':
                        if pages_.text == '>':
                            if pages_.next.attrs.get('href') is not None:
                                target_url = pages_.next.attrs.get('href')
                                r = requests.get(target_url, headers=headers)
                                soup = BeautifulSoup(r.text, 'lxml')
                            else:
                                nextpage = False
                            break

試したこと

サイト内でも該当エラーに関する質問が多く、色々調査させていただきました。
エラー内容は、「取得しようとしているデータはないのでエラーですよ」と解釈しております。
そのため、エラー発生店舗とそうでない店舗の更新日の情報を比較しましたが、特に違いはないように見えました。
■エラー発生店舗
https://p-town.dmm.com/shops/hokkaido/1048
■該当の更新日
<div class="lead">更新日: 06/11 (火)</div>

■エラー未発生店舗
https://p-town.dmm.com/shops/hokkaido/1038
■該当の更新日
<div class="lead">更新日: 06/12 (水)</div>

何故エラーが発生しているかが知りたいのと、エラー発生店舗の更新日が取得できるソースコードがあればご教授いただきたいです。

また、もし取得できない場合は、下記if文で対処することを検討しておりますが、他にいい方法があればご教授いただきたいです。

#更新日取得
date_ = shop.find_all('div',class_='lead')
if len(date_) == 0:
    up_date = ''
else:
    up_date = date_[0].string
    up_date = up_date.replace('更新日:', '')
    up_date = "".join(up_date.split())

補足情報(FW/ツールのバージョンなど)

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

更新日の情報取得を以下に変更したところ、解決しました。
お騒がせして申し訳ありませんでした。

for shop in soup3.find_all('div',class_='lead'):
    up_date = shop.get_text(strip=True)
    up_date = up_date.replace('更新日:', '')
    up_date = ''.join(up_date.split())

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 89.06%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る