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

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

ただいまの
回答率

88.32%

Beautiful Soup 取得したい階層にたどり着けない(見つからない)

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 1,264

metrobaroque

score 1

前提・実現したいこと

pythonのBeautiful Soupを用いてスクレイピングを行っております。
用いているサイトはこちら(netkeibaへのリンク)で、実現したいことは、レース表を取得することです。
Chromeのデベロッパーツールを用いてレース表の場所を探し出し、find_all()で適宜条件を指定すれば取得できるはずだと思い実行してみたのですが、なぜかうまくいきません。
取得したいレース表と、該当する階層の画像を以下に添付します。
レース表
デベロッパーツール

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

ホームページ上で確認できる階層にたどり着けない(見つけられない)。
find_all()で検索をしても[]が返ってくる。

取得したい場所は

<dl class="RaceList_DataList">...</dl>


なのですが、find_all()関数では、その場所を見つけることができませんでした。

該当のソースコード

def func(x):
    http = urllib3.PoolManager()
    response = http.request('GET',x)

    return BeautifulSoup(response.data, "html.parser")

url = "https://race.netkeiba.com/top/race_list.html?kaisai_date=20200412"
soup.find_all("dl", class_="RaceList_DataList")
# 返り値
# []

試したこと

他にもいくつかの方法で探してみましたが、手がかりは見つかりませんでした。

soup.find_all("div", id="date_list")
# [<div class="RaceList_Area" id="date_list"></div>]
soup.find_all("div", id="race_list")
"""
[<div id="race_list">
 <div class="RaceList_Area" id="date_list"></div>
 <div id="JraDigestPlaceHolder"></div>
 <div class="Contents_Box Top_RaceDigest" id="JraDigest"></div>
 <script type="text/javascript">
 〜〜省略〜〜
 </script>
 <div class="FileLoader"><img alt="" src="https://cdn.netkeiba.com/img.racev3/common/img/common/gif-load.gif?2019080101"/></div>
 </div>]
"""

また、selectorを使ってみましたが、同じ結果を返しました。

soup.select('#RaceTopRace > div > dl:nth-child(1)')
# []

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

ここにより詳細な情報を記載してください。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

こちらのnetkeiba.comさんのページですが
HTMLをJavScriptで色々と処理をしています
urllibをそのまま使ってしまうとJavaScriptで色々と処理をする前の
HTMLを取得してしまいます
※この色々な処理のことをブラウザレンダリングと呼んだりします
※詳細はググってください

引用されているリンクを開き「ページのソースを表示する」を押して
RaceList_DataListを検索していただければ、検索結果が0/0になると思います
なぜならレンダリング前のソースが表示されているからです
urllibで取得しているHTMLというのはそれと同じものです

response = http.request('GET',x)を実行した時には
レンダリングされていない生のHTMLを取得しているため
そもそも<dl class="RaceList_DataList">...</dl>が存在しない
という感じになっています

他の回答者様が仰っている
「CSSが複雑だからBeautifulSoupで要素が見つからない」 のではなくて
「そもそも取得したHTMLにRaceList_DataList要素が無い」
=> 当然 BeautifulSoupでは要素が見つからないということです
その他のタグも同様にそもそもHTMLにタグが無いので見つからないです

よってただ生のHTMLではなくてレンダリング後のHTMLを取得する必要があります
Pythonではいくつか方法はありますがseleniumを使う方法がすごくおすすめです

私の環境(Ubuntu18.04 Python3.6.9)で実行できることを確認しています
※ Windowsをお使いの場合は環境変数の設定等が必要かもしれません

環境

# パッケージのバージョンです
$ pip freeze | grep -e selenium -e beautifulsoup4 -e chromedriver-binary
beautifulsoup4==4.8.2
chromedriver-binary==83.0.4103.14.0
selenium==3.141.0

コード

"""
Scraping with Selenium

$ pip install selenium
$ pip install beautifulsoup4
$ pip install chromedriver-binary
"""

from selenium import webdriver
from bs4 import BeautifulSoup as bs
import time


def scraping(url):

    # ============================================
    # さっそくおまじないです
    # 説明量が膨大になるため割愛します
    options = webdriver.ChromeOptions()

    # headless モードを有効にします
    # これを付けないと実際にWebブラウザ(Chrome)が立ち上がるのでびっくりします
    options.add_argument('--headless')

    # ブラウザを起動する
    driver = webdriver.Chrome(options=options)

    # url から get します
    driver.get(url)

    # いったん落ち着かせます
    time.sleep(2)

    # レンダリング後のHTMLを取得できます
    html = driver.page_source

    # もし文字化けしたら、こちらでいけると思います(たぶん)
    # html = driver.page_source.encode('euc_jp')


    # ============================================
    # ここからはいつものBeautifulsoupです
    # 取得したhtmlは完全版なのでご自由にタグなりリンクなりを抜いてください

    soup = bs(html, 'lxml')

    filter_tag = '.RaceList_DataList'
    for elem in soup.select(filter_tag):
        print('***' * 8)
        print(elem)


if __name__ == "__main__":
    url = 'https://race.netkeiba.com/top/race_list.html?kaisai_date=20200412'
    scraping(url)



"""出力結果

$ py scraping_with_selenium.py

************************
<dl class="RaceList_DataList">
<dt class="RaceList_DataHeader">
<p class="RaceList_DataTitle"><small>3回</small> 中山 <small>6日目</small></p>
<div class="RaceList_DataDesc">
<span class="Weather">天気:<span class="Icon_Weather Weather02"></span></span>
<span class="Shiba">芝:稍</span>
<span class="Da">ダ:稍</span>
</div> <!-- /.RaceList_DataDesc -->
<a class="LinkHaraimodoshiichiran" href="../top/payback_list.html?kaisai_id=2020060306&amp;kaisai_date=20200412&amp;rf=race_list">払戻一覧</a>
</dt>
<dd class="RaceList_Data">
<ul>
<li class="RaceList_DataItem hasMovieLink">

...(中略)...


************************
<dl class="RaceList_DataList">
<dt class="RaceList_DataHeader">
<p class="RaceList_DataTitle"><small>2回</small> 阪神 <small>6日目</small></p>
<div class="RaceList_DataDesc">
<span class="Weather">天気:<span class="Icon_Weather Weather03"></span></span>
<span class="Shiba">芝:重</span>
<span class="Da">ダ:重</span>
</div> <!-- /.RaceList_DataDesc -->
<a class="LinkHaraimodoshiichiran" href="../top/payback_list.html?kaisai_id=2020090206&amp;kaisai_date=20200412&amp;rf=race_list">払戻一覧</a>
</dt>
<dd class="RaceList_Data">
<ul>

...

"""

お時間がありましたらレンダリング前と後のHTMLを見比べてみてはいかがでしょうか
サイトの構造が全然違って見えるはずです

他の方法ですとPhntomJSを使う、requests_htmlを使う、
そもそもJavaScriptを使うなど方法は色々とあるようですが
seleniumはとても使い勝手が良いので試してみてくだしあ

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/04/26 13:07

    ご回答いただきありがとうございました。
    おっしゃる通り、レンダリングについて理解しないまま取り組んでおりました。
    いただいたソースコードもとても役に立ちました。ありがとうございます。
    最後にBeautifulSoupで整形するところ、自分の環境では
    bs(html, "html.parser")
    とすることで欲しい形になったことを報告させていただきます。

    キャンセル

  • 2020/04/26 20:07 編集

    soup = bs(html, 'lxml') について

    lxmlはpythonの外部ライブラリであり、pip install が必要なことを
    すっかり忘れており説明を記入しておりませんでした
    大変申し訳ありません

    スクレイピングのほうが上手くいったという事で一安心です
    これからも頑張ってください!

    キャンセル

0

BeautifulSoupは、CSS(Webページのスタイル)が複雑だと、要素を見つけられないことがあるようです。
https://gammasoft.jp/blog/how-to-download-web-page-created-javascript/
ここにも書いているように、Seleniumを使うことをお勧めします。Seleniumでは、
BeautifulSoupは、HTMLを読み込むのに対して、
SeleniumはWebDriverでWebページから直接要素を探し出すので、より詳細に調べることが可能です。いちいちWebページが動くので動作が多少遅くなりますが、質問にあるような状況では問題ないかと思います。
また、Seleniumをバックグラウンドで動かすこともできるみたいです。(自分用の備忘録)
https://watlab-blog.com/2019/08/18/selenium-chrome-background/

手前味噌ですが、私も競馬サイトのスクレイピングを実装したことがあり、BeautifulSoupでやろうとしてできなかったので、Seleniumにしました。Seleniumの方がよさそうです!

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/04/26 13:08

    早速のご回答ありがとうございました。
    そもそもSeleniumを用いないと探し出すことすら不可能なんですね。
    いただいたリンクも拝見させていただきます。

    キャンセル

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

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

関連した質問

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