シロッコの青空ぶろぐ

高卒シニアが低学歴コンプレックス脱出のため、放送大学の人間と文化コースで学んでいます。通信制大学で学ぼうとする人を応援したい。学んで成功する人が増えれば、私のやる気も燃えるはず。

Python初心者がWikipedia APIでキネマ旬報のデータ取得に挑戦!途中で発生したエラーとの戦い

(提供:フリーイラスト素材集 ジャパクリップ

目次

今までの流れ

Pythonを知らない私が、GPT-4に聞いて「キネマ旬報ベスト・テン」全ランキングのCSVファイルを作ろうとしました。その記録が以下です。

GPT-4 にプロンプトを送り、プログラムデバッグを行ったのですが、途中で動作がとまる症状が起こり、何が起きているのか把握できませんでした。

そこで、次のようにすることにしました。

  • Wikipedia API を使った動くプログラムを動作を確認しながら修正していく。
  • 修正はソースを渡して変更して貰うのではなく、自分が手作業で行う。

Wikipedia API を使った動くプログラム

以下のプログラムは、GPT-4から提示されたWikipedia APIを使ったプログラムです。

import requests
from bs4 import BeautifulSoup
import wikipediaapi

# 言語設定
wiki = wikipediaapi.Wikipedia("ja")

# 記事タイトル
title = "キネマ旬報"

# 記事の取得
page = wiki.page(title)

if page.exists():
    print("Page - Title: %s" % page.title)
    print("Page - Summary: %s" % page.summary[0:60])
else:
    print("Page not found")
    exit()

# HTMLページの取得
res = requests.get(page.fullurl)
soup = BeautifulSoup(res.text, 'html.parser')

# セクション「各年のベスト・テン結果」の取得
best_ten_section = soup.find('span', {'id': '各年のベスト・テン結果'}).parent

# 各年代の項目を取得
decades = best_ten_section.find_next_siblings(['h3', 'table'])

i = 0
while i < len(decades):
    if decades[i].name == 'h3':
        decade_text = decades[i].text.strip()
        i += 1

    japanese_tables = decades[i]
    i += 1
    if i < len(decades):
        foreign_tables = decades[i]
        i += 1
    else:
        foreign_tables = None

# 各年代の「日本映画ベスト・テン」と「外国映画ベスト・テン」のテーブル数を表示
print("Japanese movie table:", japanese_tables)
print("Foreign movie table:", foreign_tables)

print("正常終了")

「createBestTen.py」を実行する。

前回インストールした「VS Code」を立ち上げます。

GPT-4 からコードを「Copy cpde」をクリックしてコピー。

「VS Code」で「Ctl + v」でペースト。

「createBestTen.py」として保存します。

保存方法は左上「三」のマークのメミューから「ファイル」→「名前をつけて保存...」です。

左の縦に並んだマークから「▷」をクリックし、新しく開いたWindowの「実行とデバッグ」をクリックします。
左上「三」のマークのメミューにも「実行」関連のメニューがあります。

実行すると膨大なデータが表示されました。

page 取得に成功したときは何も表示しなくていい

最初に表示されるのは、pageの取得が成功したかどうかを確認するところです。

if page.exists():
    print("Page - Title: %s" % page.title)
    print("Page - Summary: %s" % page.summary[0:60])
else:
    print("Page not found")
    exit()

成功するのが分かったので、ページが存在するときは何もしないコードを教えて貰った

# Get the page information, and do nothing if the page exists
if not page.exists():
    # Display an error message
    print('Error: Page not found')
else:
    # Do nothing
    pass

このように修正すると、not page.exists() 条件によって、ページが存在しない場合には、エラーメッセージを表示します。一方、ページが存在する場合には、何もせずにプログラムを終了します(pass 文によって何もしないことを示しています)。

print("Japanese movie table:", japanese_tables)の結果が膨大

プログラムの最後にある以下の結果が表示されています。

print("Japanese movie table:", japanese_tables)
print("Foreign movie table:", foreign_tables)

print("Japanese movie table:", japanese_tables) の取得した膨大な結果

Japanese movie table: <table class="navbox nowraplinks mw-collapsible mw-collapsed" style="min-width:100%;border-spacing:2px;border-collapse:separate;"><tbody><tr><th class="navbox-title" style=""><div style="float:left;width:3.5em;text-align:left;"> </div><span style="font-size:110%;">キネマ旬報主催の賞</span></th></tr><tr><td style="padding:0; font-size:111%;"><div aria-labelledby="キネマ旬報ベスト・テン_外国映画ベスト・ワン" class="navbox" style="border-collapse:collapse;padding:3px">(略)

外国映画はデータがない。

Foreign movie table: None

プログラムの処理内容を知りたい

japanese_tables に情報があり、1920年代、1930年代、などの年代ごとに取得した decades があるようなのですが、構造がわかりません。

処理の内容を分かりやすく説明して

以下の【キネマ旬報ベストテン取得プロブラム】が行っている処理の内容を分かりやすく説明してください。

この【キネマ旬報ベストテン取得プロブラム】は、キネマ旬報誌の記事ページから、各年代の日本映画ベスト・テンと外国映画ベスト・テンの表をスクレイピングして、画面に表示するプログラムです。以下に、プログラムの主要な処理内容を説明します。

  1. requests ライブラリを使って、キネマ旬報の記事ページの HTML データを取得します。
  2. BeautifulSoup ライブラリを使って、取得した HTML データをパースして、データを解析するためのオブジェクト(ここでは soup という変数に代入)を作成します。
  3. wikipediaapi ライブラリを使って、Wikipedia 日本語版の記事の情報を取得するためのオブジェクト(ここでは wiki という変数に代入)を作成します。
  4. 記事タイトルを指定して、Wikipedia から記事情報を取得します。
  5. 取得した記事情報に基づいて、記事タイトルと記事サマリーを表示します。
  6. セクション「各年のベスト・テン結果」の範囲を特定して、その範囲内にある各年代の映画ベスト・テン表を取得します。
  7. 取得した各年代の映画ベスト・テン表を表示します。
  8. プログラムの処理が正常に終了したことを表示します。

このプログラムでは、requests ライブラリや BeautifulSoup ライブラリを使って、Web スクレイピングを行っています。また、wikipediaapi ライブラリを使って、Wikipedia 日本語版の記事情報を取得しています。最終的に、取得したデータを整形して、画面に表示しています。

うーん、よく分からない。

page = wiki.page(title) で取得される情報

page = wiki.page(title) で取得されるのはどのようなものですか?

wiki.page(title) は、Wikipedia 日本語版の title で指定された記事に対応するページ情報を取得するためのメソッドです。このメソッドによって返されるオブジェクトは、WikipediaAPIの wikipediaapi.WikipediaPage クラスのインスタンスであり、記事のタイトル、ID、URL、テキスト本文、サマリー、カテゴリー、リダイレクト先などの情報が格納されています。

具体的には、以下のような属性を持ちます。

  • title: ページのタイトル(文字列)
  • pageid: ページのID(整数)
  • fullurl: ページのURL(文字列)
  • text: ページのテキスト本文(文字列)
  • summary: ページのサマリー(文字列)
  • categories: ページのカテゴリー(リスト)
  • links: ページ内に含まれるリンク(リスト)
  • sections: ページ内のセクション(リスト)
  • redirect: ページがリダイレクトしている場合のリダイレクト先のページ情報(wikipediaapi.WikipediaPage クラスのインスタンス)

このプログラムでは、wiki.page(title) によって指定された記事のページ情報を取得し、存在する場合はタイトルとサマリーを表示するために使用しています。

wiki.page(title) で取得した内容を表示

記事のタイトル、ID、URL、テキスト本文、サマリー、カテゴリー、リダイレクト先などの情報を表示するプラグラムを書いてください。

以下は、WikipediaAPIを使ってWikipediaの記事情報を取得し、各種情報を表示するサンプルプログラムです。ここでは、記事のタイトルを "Python" としていますが、任意の記事タイトルを指定することができます。

import wikipediaapi

# WikipediaAPIのインスタンスを作成する
wiki = wikipediaapi.Wikipedia('ja')

# 記事のタイトルを指定する
title = 'Python'

# 記事ページの情報を取得する
page = wiki.page(title)

# ページ情報を表示する
print('タイトル:', page.title)
print('ID:', page.pageid)
print('URL:', page.fullurl)
print('本文:', page.text)
print('サマリー:', page.summary)
print('カテゴリー:', page.categories)
print('リダイレクト先:', page.redirect)

# カテゴリーを表示する
print('\nカテゴリー:')
for category in page.categories:
    print(category)

# リンク先を表示する
print('\nリンク先:')
for link in page.links:
    print(link)

このプログラムでは、WikipediaAPIの wikipediaapi.Wikipedia クラスのインスタンスを作成し、page() メソッドを用いて記事ページ情報を取得しています。その後、各種情報を取得して、表示しています。

記事ページ情報には、titlepageidfullurltextsummarycategoriesredirect などの属性があります。それぞれの属性には、記事のタイトルやID、URL、テキスト本文、サマリー、カテゴリー、リダイレクト先などの情報が格納されています。また、categories や links 属性には、それぞれ記事に含まれるカテゴリーやリンクの情報がリスト形式で格納されています。

ページ情報を表示するプログラムの実行

「VSコード」を立ち上げます。

  • 左上「三」のメニューから「ファイル」→「新しいテキストファイル」
  • GPT-4 の「Copy code」をクリック
  • 「VSコード」で「Ctl +v」
  • 左上「三」のメニューから「ファイル」→「名前をつけて保存...」
    「showPageInf.py」としました。

「▷」で「デバッグと実行」

エラーが表示されます。

例外が発生しました: AttributeError
'WikipediaPage' object has no attribute 'redirect'
  File "C:\Users\kazka\Documents\python\bestten\showPageInf.py", line 19, in <module>
    print('リダイレクト先:', page.redirect)
                      ^^^^^^^^^^^^^
AttributeError: 'WikipediaPage' object has no attribute 'redirect'

ChatGPTが教えるプログラムは間違っている場合があるから、要注意です。

このエラーメッセージは、記事ページにリダイレクトがない場合に、page.redirect 属性が存在しないことを示しています。つまり、エラーが発生したのは、"キネマ旬報"の記事ページにリダイレクトがない場合に、このプログラムがpage.redirect を呼び出したためです。(略)

例えば、以下のように if 文を使用することができます。

# Display redirect information, if it exists
if page.redirect:
    print('Redirect:', page.redirect)

またもエラー。

以下のエラーがでます。対処方法を教えて
エラー:

Traceback (most recent call last):
  File "c:\Users\kazka\Documents\python\bestten\showPageInf.py", line 21, in <module>  
    if page.redirect:
       ^^^^^^^^^^^^^
  File "C:\Users\kazka\AppData\Local\Programs\Python\Python311\Lib\site-packages\wikipediaapi\__init__.py", line 832, in __getattr__
    return self.__getattribute__(name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'WikipediaPage' object has no attribute 'redirect'

GTP-4の返答

このエラーメッセージは、記事ページにリダイレクトがない場合に、page.redirect 属性が存在しないことを示しています。つまり、エラーが発生したのは、リダイレクトがない場合に page.redirect を呼び出したためです。

(略)

したがって、エラーを回避するには、page.redirect 属性が存在する場合にのみ、リダイレクト先の情報を表示するようにプログラムを修正する必要があります。以下のように、hasattr メソッドを使って page.redirect 属性の存在を確認し、存在する場合にのみ、リダイレクト先の情報を表示することができます。

# Display redirect information, if it exists
if hasattr(page, 'redirect'):
    print('Redirect:', page.redirect)

これに差し替えて実行してみます。

バッファがあふれ先に表示したデータが消える

正常終了するようになりました。

しかし、一気に大量のデータが表示され、先に表示したデータはバッファがあふれ消えてしまいます。

ファイルに記録するように変更して

以下のプログラムは表示するデータが多すぎでターミナルのバッファが溢れ、最初の方が消えてしまいます。
「output.txt」というファイルに記録するように変更してください。
そして、最後に「print("正常終了")」を加えます。
プログラム:

(ここにソースコード)

GPT-4の回答

以下のように、print 文を file 引数を使ってファイルに出力するように変更し、最後に print("正常終了") 文を追加することができます。

import wikipediaapi

# WikipediaAPIのインスタンスを作成する
wiki = wikipediaapi.Wikipedia('ja')

# 記事のタイトルを指定する
title = 'キネマ旬報'

# 記事ページの情報を取得する
page = wiki.page(title)

# ファイルに出力するためのファイルオブジェクトを作成する
with open('output.txt', 'w', encoding='utf-8') as f:
    # ページ情報をファイルに出力する
    print('タイトル:', page.title, file=f)
    print('ID:', page.pageid, file=f)
    print('URL:', page.fullurl, file=f)
    print('本文:', page.text, file=f)
    print('サマリー:', page.summary, file=f)
    print('カテゴリー:', page.categories, file=f)

    # Redirect情報をファイルに出力する
    if hasattr(page, 'redirect'):
        print('Redirect:', page.redirect, file=f)

    # カテゴリーをファイルに出力する
    print('\nカテゴリー:', file=f)
    for category in page.categories:
        print(category, file=f)

    # リンク先をファイルに出力する
    print('\nリンク先:', file=f)
    for link in page.links:
        print(link, file=f)

# 処理が正常に完了したことを表示する
print('正常終了')

このコードを「VSコード」に貼り付け、「savePageInf.py」として保存実行しました。

「正常終了」と表示され、「output.txt」が出来ていました。

「output.txt」の内容を Notepad++ で開くと、次のようなもので、正常に取得できているようです。

タイトル: キネマ旬報
ID: 171948
URL: https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%8D%E3%83%9E%E6%97%AC%E5%A0%B1
本文: 『キネマ旬報』(キネマじゅんぽう)は、キネマ旬報社が発行する映画雑誌である。1919年7月創刊。毎月5日・20日刊行。通称「キネ旬」。

概要
1919年7月に東京高等工業学校(現・東京工業大学)の田中三郎ら学生4人が、アート紙4ページ、毎月1日、11日、21日発行の外国映画専門誌として発刊。

(略)

Portal:映画
プロジェクト:映画

4643行もあるファイルです。

これから、「日本映画ベスト・テン」の内容を取得するのにはどうすればよいのか、検討していきます。

今回はここまでとします。

まとめ

今回は、キネマ旬報のデータ取得にWikipedia APIを使って挑戦してみました。

最初は、GPT-4で作ったプログラムでエラーが出たり、取得結果が多すぎて混乱したりと、何度も壁にぶち当たりました。

手作業で修正することでやっとページの取得方法を理解し、プログラムを修正することに成功しました。

次は、日本映画ベスト・テンの情報取得に挑戦する予定です。

初心者でもわかりやすい、Wikipedia APIの情報やプログラムの修正方法、バッファの処理方法など、様々なことを学びました。Pythonに興味を持っている方は、ぜひ参考にしてみてください。

今回のも壁にぶち当たりました。しかし、その分、自分自身で問題を解決する喜びも味わうことができました。皆さんも、挑戦することで自分自身の成長に繋がることを実感してほしいです。