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

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

ただいまの
回答率

90.61%

  • Python 3.x

    5886questions

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

PythonによるDOMの取得

受付中

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 192

jerald

score 2

 前提・実現したいこと

PythonでサイトのDOMを取得するプログラムを作成中です。
対象
"http://www2.keiba.go.jp/KeibaWeb/TodayRaceInfo/RaceMarkTable?k_raceDate=2012%2f01%2f01&k_raceNo=11&k_babaCode=21"

元にしたもの
https://qiita.com/suecharo/items/cc2b0758bf549f0dbb29
これをもとに上記対象のサイトを読むようにしたのですが、エラーがでます。

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

Traceback (most recent call last):
  File "C:\app\py\keiba.py", line 120, in <module>
    main()
  File "C:\app\py\keiba.py", line 83, in main
    seiseki()
  File "C:\app\py\keiba.py", line 93, in seiseki
    tuple_get_text = seisekicrawling(dom)
  File "C:\app\py\keiba.py", line 74, in seisekicrawling
    get_text = make_text_from_xpath(dom, '//*[@id="container00"]/table/tbody/tr[2]/td/table/tbody/tr/td/table[4]/tbody/tr/td/table/tbody/tr[' + str(tr
num) + ']/td[2]/text()')
  File "C:\app\py\keiba.py", line 44, in make_text_from_xpath
    text = dom.xpath(xpath)[0].text
IndexError: list index out of range

 該当のソースコード

# coding: utf-8
import requests  # urlにGETリクエストしてhtmlを取得
from fake_useragent import UserAgent  # headerを生成
import lxml.html  # xmlパーサ
import time  # 遅延生成用


def make_scraping_url(page_num):
    scraping_url = "http://www2.keiba.go.jp/KeibaWeb/TodayRaceInfo/RaceMarkTable?k_raceDate=2012%2f01%2f01&k_raceNo=11&k_babaCode=21"
    return scraping_url


counter_200 = 0
counter_404 = 0


def make_html_text(scraping_url):
    global counter_200
    global counter_404

    ua = UserAgent()  # fakeuser-agentのオブジェクト
    ran_header = {"User-Agent": ua.random}  # 呼び出されるたびにランダムなheaderを生成
    html = requests.get(scraping_url, headers=ran_header)  # requestsにより得られたhtmlのオブジェクト
    html_text = html.text

    if html.status_code == 200:
        counter_200 += 1
    elif html.status_code == 400:
        counter_404 += 1

    return html_text


# Document Object Model、htmlをxml形式に変換して生成される
def make_dom(html_text):
    dom = lxml.html.fromstring(html_text)
    return dom


# domとxpathを受け取ってtextを返す
def make_text_from_xpath(dom, xpath):

    try:
        text = dom.xpath(xpath)[0].text
    except AttributeError:
        text = str(dom.xpath(xpath)[0])

    return text


# domとxpathを受け取ってlinkを返す
def make_link_from_xpath(dom, xpath):
    link = dom.xpath(xpath)[0].attrib["href"]
    return link


# 取得したいテキストがtext()に内包されている場合
def make_text_from_xpath_function(dom, xpath):
    text = dom.xpath(xpath)[1]
    return text


# scrapingしたい情報の項目とそのxpathをまとめたクラス、最初に個別記事が上から何番目かを渡す
class Xpath:
    def __init__(self, article_num):
        self.article_num = article_num
        self.info_list = []

def seisekicrawling(dom):
    list_get_text = []  # 1記事分の情報をまとめるためのlist、MySQLへのinsertにはtuple型を使うため、後ほど変換する
    trnum = 3

    while trnum <= 15:
        get_text = make_text_from_xpath(dom, '//*[@id="container00"]/table/tbody/tr[2]/td/table/tbody/tr/td/table[4]/tbody/tr/td/table/tbody/tr[' + str(trnum) + ']/td[2]/text()')
        list_get_text.append(get_text)
        trnum = trnum + 1
    return list_get_text


def main():
    global counter_200

    seiseki()

def seiseki():
    # アクセス
    scraping_url = 'http://www2.keiba.go.jp/KeibaWeb/TodayRaceInfo/RaceMarkTable?k_raceDate=2012%2f01%2f01&k_raceNo=11&k_babaCode=21'
    html_text = make_html_text(scraping_url)
    tfile = open('html_text.html', 'wt')#
    tfile.write(html_text)#
    tfile.close()#
    dom = make_dom(html_text)
    tuple_get_text = seisekicrawling(dom)


def debug_check_where_link_number(page_num, article_num):
    scraping_url = make_scraping_url(page_num)
    html_text = make_html_text(scraping_url)
    dom = make_dom(html_text)
    xpath = Xpath(article_num)

    thumbnail_link = check_thumbnail_link(dom, article_num)
    if thumbnail_link[0] is True and thumbnail_link[1] is True:
        xpath.with_thumbnail()
    elif thumbnail_link[0] is False:
        xpath.no_thumbnail()
    elif thumbnail_link[1] is False:
        xpath.deleted()

    get_link = make_link_from_xpath(dom, xpath.info_list[1])
    l_get_link = get_link.split("/")
    counter = 0
    for item in l_get_link:
        print(counter)
        counter += 1
        print(item)


if __name__ == "__main__":
    main()
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

+3

IndexError: list index out of range

エラーメッセージを読みましょう

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

dom.xpath(xpath)で空リストが返っています。取得がうまくいっていないのでしょう。
サイトがちゃんと取れていることと、xpathの正しさを確認してください。

 追記

chromeが勝手にtbodyタグを挿入しているため、取得したxpathと本来のデータが一致しないためです。

xpath = '//div[@id="container00"]/table/tr[2]/td/table/tr/td/table[4]/tr/td/table/tr[3]/td[2]'

これで動きました
参考
スクレイピングにChromeのディベロッパーツールが便利 | あっかぎのページ

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/05/16 15:03

    90-92行目で作成されるhtml_text.htmlは正常に取得されています
    42行目にprint(xpath)を入れると
    //*[@id="container00"]/table/tbody/tr[2]/td/table/tbody/tr/td/table[4]/tbody/tr/td/table/tbody/tr[3]/td[2]/text()

    キャンセル

  • 2018/05/16 16:04

    あー、わかりました。chromeの検証でやったらtbodyがあるんだけど、オリジナルのソースではぜんぶ省略されています。chromeが勝手に挿入してるだけで
    xpath = '//div[@id="container00"]/table/tr[2]/td/table/tr/td/table[4]/tr/td/table/tr[3]/td[2]'
    これで動きました
    参考
    http://akkagi.info/20150228_web/

    キャンセル

  • 2018/05/17 22:39

    質問にも追記しました。気づいて・・・

    キャンセル

0

dom.xpath(xpath) の結果が、配列ではない。
そのため、dom.xpath(xpath)[0]で「list index out of range」エラーが発生した。

という事だと思われます。

dom.xpath(xpath)で、どんな値が返されるのかを確認することからデバッグに着手しては如何でしょうか。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/05/16 14:43

    42行目に print(dom.xpath(xpath))
    で[]が表示されます。取得されていません。
    print(xpath)は
    //*[@id="container00"]/table/tbody/tr[2]/td/table/tbody/tr/td/table[4]/tbody/tr/td/table/tbody/tr[3]/td[2]/text()

    キャンセル

  • 2018/05/16 15:42

    実際のDOMオブジェクトの構造と、xpathの指定が合っていないのでしょうね。取得したDOMオブジェクトの構造を確認されましたか?

    キャンセル

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

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

関連した質問

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

  • Python 3.x

    5886questions

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