【入門】Pythonで複数ページをスクレイピング(再帰処理)

【入門】Pythonで複数ページをスクレイピング(再帰処理)
スポンサーリンク
スポンサーリンク

PythonでWebスクレイピングを行う方法を解説します。
Webサイト内の複数ページを、リンクを辿りながらクロール(巡回)して情報を取得して、Excelに出力します。またWebサイトをスクレイピングする際の注意点も説明していきます。

スポンサーリンク

使用ライブラリ

PythonでWebスクレイピングを行うためのライブラリでよく利用されているものは Requests, Beautiful Soup, Selenium, Scrapy があります。
ここでは、 Requests, Beautiful Soupを用いた方法を解説します。

ライブラリの基本的な使い方は以下の記事を参照してください。
・関連記事:【入門】PythonによるWebスクレイピング

ライブラリ:Requests(公式ドキュメント
インストール:必要
ライブラリ:Beautiful Soup(公式ドキュメント
インストール:必要

スクレイピングする際の注意点

Webサイトをスクレイピングする際には、以下の点を確認してからルールや制限を守って行います。注意すべき点としては「著作権法」「動産不法侵入」「偽計業務妨害罪」「利用規約」などが挙げられます。

具体的には以下のような内容に注意してください。

  • Webページに記載している内容をデータ化しても良いか
  • そのサイトにWebブラウザーではなくプログラムからアクセスしても良いか
  • サイトの利用規約でスクレイピングを禁止していないか
  • ページのアクセスに対する制限・指示がないか
  • サーバに過剰な負荷をかけないか

ページのアクセスに対する制限や指示を確認するには、robots.txt を確認します。主な記載内容は以下になります。

  • User-Agent
  • Disallow
  • Allow
  • Crawl-delay
  • Sitemap

User-Agentは、どのクローラーの動きを制御するかを指定します。Googlebotと指定した場合、GoogleのWebクロールを制御することになります。

Disallowは、クローラーのアクセスを制御するファイルを指定するものです。Disallowで指定されたファイルやディレクトリはアクセスを禁止しています。

Allowは、Disallowで指定しているディレクトリ配下のサブディレクトリ・特定ページのクロールを許可するためのものです。

Crawl-delayは、アクセス間隔を秒数で指定しています。この指定が無い場合もサーバの負荷を考慮するために、最低1秒以上は間隔を空けるようにしましょう。
2010年には、岡崎市立図書館のシステムに障害を発生させた事で、クロールしていた開発者が逮捕されるという事件も発生していますので、リスクを十分に理解して行いましょう。(参考:岡崎市立中央図書館事件

Sitemapは、ページのサイトマップ(sitemap.xml)の場所をクローラーに伝えるものです。SitemapSpider でサイトマップを読み込ませることで簡単にWebページのスクレイピングを行うことができます。

robots.txt の確認

robots.txt を確認してみます。通常はトップページURL/robots.txtにアクセスする事で確認できます。技術評論社 のWebサイトでは https://gihyo.jp/robots.txt で表示する事ができます。
(以下は2022年3月5日時点のもの

robots.txt

User-agent: * の指示内容を確認して、スクレイピングする対象のURLがDisallowに含まれていないか確認します。今回は「https://gihyo.jp/book/list」 のページをクロールするので禁止されていないことが分かります。

対象のURLが許可されているか、Python標準ライブラリのurllib.robotparserを使って確認すること可能です。

複数ページをクロールして情報を取得する

ここでは例として技術評論社の新刊書籍一覧の全ページの情報を取得する方法を解説します。

赤枠内の情報をスクレイピング → 次ページのリンク取得 → 赤枠情報をスクレイピングを繰り返し、Excelに情報を出力します。

技術評論社HP

Excelに出力した結果です。

取得結果リスト

プログラム解説

プログラムのポイントを解説します。

プログラムは上から順に説明しますが、スクリプトを実行するとPRG1のライブラリ設定を読み込んだ後に、PRG7からmain()が実行されます。

PRG1:ライブラリ設定

import requests
from bs4 import BeautifulSoup

HTMLのダウンロードと解析を行うためにrequests BeautifulSoupをインポートします。
インストールや基本的な使い方は以下の記事を参照してください。
・関連記事:【入門】PythonによるWebスクレイピング

import time

クロールの間隔を空けるための待ち時間の設定に使用します。

import openpyxl as px
from openpyxl.styles import Alignment

Excelに取得したデータを出力するためにopenpyxlライブラリを使用します。

PRG2:クロール処理開始

1行ずつ解説します。

def main():

関数main()を定義します。

top_url = ‘https://gihyo.jp/book/list’

クロールを開始するURLを指定します。

booklist = [[‘タイトル’, ‘著者’, ‘価格’, ‘URL’]]

取得した書籍一覧データを格納するリストをbooklistとして、Excelに出力する際のヘッダーを設定します。

booklist = get_list(top_url, booklist)

PRG4のget_list関数を呼び出します。引数はクロール対象URLtop_urlとデータ格納用リストbooklistを指定します。

PRG3:Excel出力

Excelの操作方法についてはこちらの記事を参考にしてください。
・参考記事:【Python入門】openpyxlによるExcel操作まとめ

wb = px.Workbook()
ws = wb.active

新しいExcelファイルを作成してブックをwbwbのアクティブなシートをwsと設定します。

ws.column_dimensions[‘A’].width = 70
ws.column_dimensions[‘B’].width = 35
ws.column_dimensions[‘C’].width = 30
ws.column_dimensions[‘D’].width = 45

出力した結果を見やすくするために、シートのA列~D列の幅を設定します。

for y, row in enumerate(booklist):

booklistから1要素ずつ取り出して処理します。enumerate関数でインデックス番号をyとして取得し、Excelに出力する際の行番号に使用します。

booklistは以下のような多次元配列になるため[‘タイトル’, ‘著者’, ‘価格’, ‘URL’]のリスト を順次取り出してrowに格納します。

[[‘タイトル’, ‘著者’, ‘価格’, ‘URL’],
 [‘・・・’, ‘・・・’, ‘・・・’, ‘・・・’],
 [‘・・・’, ‘・・・’, ‘・・・’, ‘・・・’],
 [‘・・・’, ‘・・・’, ‘・・・’, ‘・・・’],
 ・・・

for x, cell in enumerate(row):

取り出したリストrowの要素を一つずつ処理していきます。
Excelの列指定用にenumerateでリスト要素のインデックス番号を取得してxに格納しています。
要素の値はcellに格納します。
・関連記事:繰り返し処理(for文)

ws.cell(row= y+1, column= x+1, value=cell)
ws.cell(row= y+1, column= x+1).alignment = Alignment(wrapText=True)

シートのセルに要素を書き出します。
x, y は インデックス番号で0から始まるため、1を足してセルのアドレスとして使用します。
Alignment(wrapText=True)で文字列の折返しを設定します。

wb.save(‘./新刊書籍一覧.xlsx’)
print(‘end’)

ワークブックを、スクリプトと同じディレクトリに名前を付けて保存します。
最後に処理の終了としてendを表示しています。

PRG4:対象URLのhtmlから書籍リストの取得

指定したURLからデータを取得して、リストに格納する関数です。
1行ずつ解説します。

def get_list(url, booklist):

第一引数url、第二引数booklist として get_list()関数を定義します。

response = requests.get(url)

Requestsライブラリのget()メソッドでHTMLデータをダウンロードして、responseに格納します

soup = BeautifulSoup(response.text, ‘html.parser’)

responseのHTMLをBeautifulSoupのhtml parserで解析して、結果をsoupに格納します。
これでタグ名などで簡単に要素を取得することが可能になります。

for item in soup.find_all(“li”, class_=”clearfix”):

書籍一覧の要素は、タグ「li」クラス「clearfix」となっているため、find_allで要素を取得します。
取得した複数の要素をfor文で一つずつitemとして取り出して処理を行います。
・関連記事:繰り返し処理(for文)

要素取得
list = []

項目毎の書籍情報を一時的に格納するためのリストを準備します。

ここから書く情報を取得するため、タイトル、著者、価格、リンクの要素を開発者ツールで確認し、タグ名やクラス名を確認します。

開発者ツールの使用方法はこちらを確認ください
【入門】PythonによるWebスクレイピング-5.1.2 クラスによる要素の検索

要素確認
title = item.find(“h3”).text

書籍タイトルは、h3タグのテキスト情報から取得します。

author = item.find(“p”, class_=”author”).text

著者は、タグ「p」クラス「author」のテキスト情報から取得します。

author = author.replace(‘\u3000’, ‘ ‘)

著者のテキスト情報の全角空白が\u3000と表示されてしまうため、半角スペースに置換しています。

・関連記事:文字列処理-文字列の置換(replace)

price = item.find(“p”, class_=”price”).text

価格は、タグ「p」クラス「price」のテキスト情報から取得します。

link = ‘https://gihyo.jp’ + item.find(“a”).get(“href”)

書籍詳細のリンクURLは、タグ「a」要素からget("href")で取得できますが、ヘッダURLが不足しているため追加しています。

list.extend([title, author, price, link])

一時格納用のリストlistextend()メソッドで複数要素をまとめて追加します。

・関連記事:リスト(配列)-複数の要素をリストに追加する

booklist.append(list)

書籍一覧リストbooklistに一時格納リストを追加します。

PRG5:次ページがある場合はPRG4の書籍リスト取得を再帰処理

next_url = get_next_page(soup)

次のページのリンクを取得するために、PRG6のget_nex_page()を呼び出して、結果をnext_urlに格納します。

if next_url is None:
    return booklist

次のページのリンクが無い場合に、next_urlの結果がNoneとなるため、その場合はbooklistを返して処理を終了します。

else:
    time.sleep(5)
    booklist = get_list(next_url, booklist)
    return booklist

next_urlがNone以外の場合=次のページのリンクが見つかった場合は、5秒間停止した後に、PRG4の書籍情報の取得関数get_listを再帰的に呼び出します。
結果のbooklistreturnで返します。

今回の再帰処理は以下のような流れになります。

再帰処理

PRG6:次ページのURLを取得する関数

def get_next_page(soup):

次ページのURLを取得する関数をget_next_page()と定義します。引数はbeautifulsoupオブジェクトです。

next_link = soup.find(“p”, class_=”next”)

次のページへのリンク要素、タグ「p」クラス「next」を検索して、結果をnext_linkに格納します。

リンク要素
if next_link.find(‘a’) is None:
    return

next_link要素からタグ「a」を検索した結果がNoneの場合は、何もせずに処理を終了します。戻り値はNoneになります。

else:
    next_url = next_link.find(“a”).get(“href”)
    return ‘https://gihyo.jp/’ + next_url

タグ「a」が見つかった場合は、get(“href”)でリンク情報を取得し、ヘッダURL'https://gihyo.jp/'を追加した値を返して処理を終了します。

PRG7:スクリプトとして実行された場合に実行

if __name__ == ‘__main__’:
    main()

Pythonには __name__ という特殊な変数があり、プログラムの実行状況により自動的に変数の内容が決まります。Pythonファイルをスクリプトとして直接実行した場合は、__name__ 変数は "__main__" という文字列になります。
ここでは、スクリプトを直接実行した場合に、main()関数を実行するように設定しています。

PythonでWebサイト内のページをクロール(巡回)して情報を取得する方法を解説しました。

・関連記事:【入門】PythonによるWebスクレイピング