このブログではPythonを使ったスクレイピングを、初心者向けにサンプルコード付きで解説しています。以下に紹介する記事では、①から⑨のステップでスクレイピングの方法を学び、実践に役立てられるよう体系的にまとめています。
【①〜⑨まとめ】PythonでWebスクレイピングを実践する方法【サンプルコード付き】
このブログではPythonスクレイピングを初心者向けに解説していきます。順番に各記事で解説しており入門者向けです。サンプルコード付きで解説するので実践的…
最初の記事ではBeautifulSoup(Requests)を使ったスクレイピングの基本について解説しました。
また、こちらの記事では各要素を取得する方法について解説しましたが、いずれも一つのページをスクレイピングする方法です。
実際のところ複数のページをまとめてスクレイピングしたい場合が多く、その方法について解説していきます。
目次
単一のページをスクレイピングする場合のおさらい
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import requests from bs4 import BeautifulSoup # 1. ウェブページをリクエストして、指定されたURLからデータを取得 url = 'https://scraping-for-beginner.herokuapp.com/ranking/' webpage_response = requests.get(url) # 2. コンテンツ属性を使用して、ウェブページのコンテンツを取得 webpage_content = webpage_response.content # 3. BeautifulSoupを使用して、取得したウェブページのコンテンツを解析可能な形式に変換 webpage_soup = BeautifulSoup(webpage_content, "html.parser") # 4. 特定の要素を取得(例:タイトル) post_titles = webpage_soup.select("h2") for post_title in post_titles: print(f'記事タイトル: {post_title.text}') # 結果 # 記事タイトル: 1観光地 1 # 記事タイトル: 2観光地 2 # 記事タイトル: 3観光地 3 # 記事タイトル: 4観光地 4 # 記事タイトル: 5観光地 5 # 記事タイトル: 6観光地 6 # 記事タイトル: 7観光地 7 # 記事タイトル: 8観光地 8 # 記事タイトル: 9観光地 9 # 記事タイトル: 10観光地 10 |
ここでは簡単に記事のタイトルを取得しています。例では10記事のタイトルが取得できました。しかし1ページ目しか取得できていませんね。
また、ページ内に複数のリンクがある場合、そのリンク先内容も取得したいところ。
ここからは複数のページをまとめて処理する方法を説明していきます。
以下、2つのパターンで説明していきます。
パターン1:URLでページ数を指定してスクレイピング
ニュースサイトやブログなどで1ページ目、2ページ目、3ページ目・・・のようにページネーションがあり、それらのページをまとめて取得したい場合です。
トップページ:example.com
2ページ目:example.com&page=2
3ページ目:example.com&page=3
このようにページ数がURLのパラメータである場合、簡単に取得できます。
指定したページURLのデータを取得する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import requests from bs4 import BeautifulSoup #指定ページから記事タイトル一覧を取得 def fetch_article_titles(page_number): #引数が1とそれ以外で条件分岐 if page_number == 1: #Webサイトの1ページ目を取得 url = 'https://scraping-for-beginner.herokuapp.com/ranking/' else: #2ページ目以降の場合、page=2のようにページ数を指定 url = f"https://scraping-for-beginner.herokuapp.com/ranking/?page={page_number}" webpage_response = requests.get(url) webpage_content = webpage_response.content webpage_soup = BeautifulSoup(webpage_content, "html.parser") post_titles = webpage_soup.select("h2") for post_title in post_titles: print(f'記事タイトル: {post_title.text}') fetch_article_titles(2) # 結果 # 記事タイトル: 11観光地 11 # 記事タイトル: 12観光地 12 # 記事タイトル: 13観光地 13 # 記事タイトル: 14観光地 14 # 記事タイトル: 15観光地 15 # 記事タイトル: 16観光地 16 # 記事タイトル: 17観光地 17 # 記事タイトル: 18観光地 18 # 記事タイトル: 19観光地 19 # 記事タイトル: 20観光地 20 |
ここでは単一のページをスクレイピングする場合と同じように、そのページ内の記事タイトルを取得しています。そして追加したコードは以下の点です。
1 2 3 4 5 6 7 |
#引数が1とそれ以外で条件分岐 if page_number == 1: #Webサイトの1ページ目を取得 url = 'https://scraping-for-beginner.herokuapp.com/ranking/' else: #2ページ目以降の場合、page=2のようにページ数を指定 url = f"https://scraping-for-beginner.herokuapp.com/ranking/?page={page_number}" |
get_links_from_page()という関数を作成していますが、引数が1であればトップページ、2以降であれば{page_number}、つまり指定したページが表示されるということです。
fetch_article_titles(2)による出力結果は上記のように、2ページ目の内容が得られます。
これによりまずは指定したページの内容が取得できました。ただし現状では1ページづつの指定しかできないので、まとめて複数ページを処理できるようにします。
複数URLのデータをまとめて取得する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
import requests from bs4 import BeautifulSoup #指定ページから記事タイトル一覧を取得 def fetch_article_titles(page_number): #引数が1とそれ以外で条件分岐 if page_number == 1: #Webサイトの1ページ目を取得 url = 'https://scraping-for-beginner.herokuapp.com/ranking/' else: #2ページ目以降の場合、page=2のようにページ数を指定 url = f"https://scraping-for-beginner.herokuapp.com/ranking/?page={page_number}" webpage_response = requests.get(url) webpage_content = webpage_response.content webpage_soup = BeautifulSoup(webpage_content, "html.parser") post_titles = webpage_soup.select("h2") for post_title in post_titles: print(f'記事タイトル: {post_title.text}') # 取得したいページ数の設定 page_range = 3 for page_number in range(1, page_range +1): # ページの範囲内でループ fetch_article_titles(page_number) # 指定されたページから記事タイトル一覧を取得 # 結果 # 記事タイトル: 1観光地 1 # 記事タイトル: 2観光地 2 # 記事タイトル: 3観光地 3 # 記事タイトル: 4観光地 4 # 記事タイトル: 5観光地 5 # 記事タイトル: 6観光地 6 # 記事タイトル: 7観光地 7 # 記事タイトル: 8観光地 8 # 記事タイトル: 9観光地 9 # 記事タイトル: 10観光地 10 # 記事タイトル: 11観光地 11 # 記事タイトル: 12観光地 12 # 記事タイトル: 13観光地 13 # 記事タイトル: 14観光地 14 # 記事タイトル: 15観光地 15 # 記事タイトル: 16観光地 16 # 記事タイトル: 17観光地 17 # 記事タイトル: 18観光地 18 # 記事タイトル: 19観光地 19 # 記事タイトル: 20観光地 20 # 記事タイトル: 21観光地 21 # 記事タイトル: 22観光地 22 # 記事タイトル: 23観光地 23 # 記事タイトル: 24観光地 24 # 記事タイトル: 25観光地 25 # 記事タイトル: 26観光地 26 # 記事タイトル: 27観光地 27 # 記事タイトル: 28観光地 28 # 記事タイトル: 29観光地 29 # 記事タイトル: 30観光地 30 |
1 2 |
# 取得したいページ数の設定 page_range = 3 |
page_range という変数に、取得したいページ数を設定しています。この例では、3ページ分のリンクを取得することを指定しています。
1 |
for page_number in range(1, page_range +1): # ページの範囲内でループ |
for ループは、指定された範囲内の数値を順番に取り出して処理を行う構文です。
range(1, page_range + 1) は、1から page_range(この場合は3)までの数値のシーケンスを生成します。page_range + 1 としているのは、range() 関数が終了値を含まないため、3ページまでのループを実現するためです。
つまり、page_number には 1, 2, 3 の値が順に代入されます。
1 |
fetch_article_titles(page_number) # 指定されたページから記事タイトル一覧を取得 |
各ページ番号に対して fetch_article_titles(page_number) 関数を呼び出します。
先ほどのコードに関数をループさせる処理を加えています。これによりpage_rangeで指定したページ数だけをまとめて処理することができるようになりました。
出力結果は上記のように、1ページ目から3ページ目までの内容が得られます。とはいえ結果が出力(print)されただけなので、実際のところはCSVなどに保存したいところです。それについては以下の記事で解説します。
⑨PythonでスクレイピングしたデータをCSVファイルに書き出す方法を解説
このブログではPythonを使ったスクレイピングを、初心者向けにサンプルコード付きで解説しています。以下に紹介する記事では、①から⑨のステップでスクレイピ…
パターン2:ページの中から各リンクを見つけクリック先のコンテンツ内容を取得
ページの中に複数の不規則な遷移先URLがある場合です。例えば商品サイトの検索結果画面でアイテム1、アイテム2・・・とあって、各商品の内容を取得したい時ですね。
この場合、各アイテムのURLは不規則である場合が多いです。なのでまずは各アイテムのURLを取得し、その後で各URLの中身をスクレイピングすることになります。
スクレイピングするサイトを見てみると、ページ内に20のリンクがあります。
このページに表示されているタイトルや画像であればそのままスクレイピングできますが、リンクをクリックした先の情報が欲しいため、工夫が必要です。
指定ページから各リンク先を見つける
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import requests from bs4 import BeautifulSoup from urllib.parse import urljoin #指定ページから各リンクを見つける def find_links(): url = 'https://scraping-training.vercel.app/site?postCount=20&title=%E3%81%93%E3%82%8C%E3%81%AF{no}%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%A7%E3%81%99&dateFormat=YYYY-MM-DD&isTime=true&timeFormat=&isImage=true&interval=360&isAgo=true&countPerPage=0&page=1&robots=true&' webpage_response = requests.get(url) webpage_content = webpage_response.content webpage_soup = BeautifulSoup(webpage_content, "html.parser") base_url = 'https://scraping-training.vercel.app' links = [] count = 0 a_tags = webpage_soup.select(".post-link") if a_tags: for a_tag in a_tags: href = a_tag.get('href') href = urljoin(base_url, href) title_div = a_tag.select_one(".post-title").text.strip() # 辞書に格納 data_dict = {"id": count, "記事タイトル": title_div, "url": href} links.append(data_dict) count += 1 return links |
以下、順番に説明していきます。
1 |
from urllib.parse import urljoin |
urljoin: ベースURLと相対URLを組み合わせて完全なURLを生成するための関数です。
1 2 3 |
base_url = 'https://scraping-training.vercel.app' links = [] count = 0 |
base_url: 相対URLを完全なURLに変換するために使用するベースURLです。
links: 記事の情報を格納するための空のリストです。
count: 記事のIDを管理するためのカウンタです。
1 2 3 4 5 6 |
a_tags = webpage_soup.select(".post-link") if a_tags: for a_tag in a_tags: href = a_tag.get('href') href = urljoin(base_url, href) title_div = a_tag.select_one(".post-title").text.strip() |
webpage_soup.select(“.post-link”): post-linkクラスを持つすべての<a>タグを取得します。
if a_tags:: a_tagsが空でないか確認します。
for a_tag in a_tags:: 各a_tagをループ処理します。
href = a_tag.get(‘href’): href属性を取得します(相対URL)。
href = urljoin(base_url, href): ベースURLと組み合わせて完全なURLを作成します。
title_div = a_tag.select_one(“.post-title”).text.strip(): 記事タイトルを取得し、前後の空白を削除します。
1 2 3 |
data_dict = {"id": count, "記事タイトル": title_div, "url": href} links.append(data_dict) count += 1 |
記事タイトル、URL、IDを含む辞書を作成し、linksリストに追加します。
countをインクリメントして次のIDに備えます。
この関数ではpost-linkクラスを持つすべての<a>タグを取得します。取得したリンクはIDと記事タイトルを付けて、辞書形式でリストに保存しています。今回のようにリンクが相対URLの場合、urljoinを使って完全なURLを作成しましょう。
この状態だとリンク一覧を取得しているだけなので、この後のコードで各リンクの内容を取得していきます。
各リンク先の内容をまとめて取得する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import requests from bs4 import BeautifulSoup from urllib.parse import urljoin #指定ページから各リンクを見つける def find_links(): url = 'https://scraping-training.vercel.app/site?postCount=20&title=%E3%81%93%E3%82%8C%E3%81%AF{no}%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%A7%E3%81%99&dateFormat=YYYY-MM-DD&isTime=true&timeFormat=&isImage=true&interval=360&isAgo=true&countPerPage=0&page=1&robots=true&' webpage_response = requests.get(url) webpage_content = webpage_response.content webpage_soup = BeautifulSoup(webpage_content, "html.parser") base_url = 'https://scraping-training.vercel.app' links = [] count = 0 a_tags = webpage_soup.select(".post-link") if a_tags: for a_tag in a_tags: href = a_tag.get('href') href = urljoin(base_url, href) title_div = a_tag.select_one(".post-title").text.strip() # 辞書に格納 data_dict = {"id": count, "記事タイトル": title_div, "url": href} links.append(data_dict) count += 1 return links # 関数を実行し、戻り値を受け取る links = find_links() # urlだけを抽出 url_list = [link['url'] for link in links] def fetch_item_datas(): h1_texts = [] for url in url_list: webpage_response = requests.get(url) webpage_content = webpage_response.content webpage_soup = BeautifulSoup(webpage_content, "html.parser") # アイテム内容を取得し、テキストをリストに追加 h1_tags = webpage_soup.select('h1') for h1 in h1_tags: h1_texts.append(h1.text.strip()) return h1_texts print(fetch_item_datas()) # 結果 ['1番目の記事', '2番目の記事', '3番目の記事', '4番目の記事', '5番目の記事', '6番目の記事', '7番目の記事', '8番目の記事', '9番目の記事', '10番目の記事', '11番目の記事', '12番目の記事', '13番目の記事', '14番目の記事', '15番目の記事', '16番目の記事', '17番目の記事', '18番目の記事', '19番目の記事', '20番目の記事'] |
まず先ほど取得した各リンクをループさせ、後は単一のページで各要素を取得するのと同様の処理を実施します。
ここではリンク先に記事タイトル(h1タグ)しかありませんが、例えば商品ページであれば商品名、金額などを取得するように処理しましょう。
1 2 |
links = find_links() url_list = [link['url'] for link in links] |
find_links関数を実行し、その戻り値をlinksに格納します。そしてlinksから各リンクのURLだけを抽出します。
1 |
for url in url_list: |
url_list内の各URLに対してループを開始します。
1 2 3 |
h1_tags = webpage_soup.select('h1') for h1 in h1_tags: h1_texts.append(h1.text.strip()) |
アイテム内容を取得し、リストに格納します。
1 |
print(fetch_item_datas()) |
fetch_item_datas関数を実行し、その結果を表示します。これにより、各ページから取得したアイテム内容が出力されます。
このように処理することで、ページ内にある各リンクを取得し、その後リンク先の内容をまとめて取得することができます。これであればURLが不規則な場合でも問題なく処理できます。
まとめ
この記事では、複数のページをまとめてスクレイピングする方法を解説しました。
ニュースサイトの記事をまとめて取得したり、ショッピングサイトの各商品データを一括で取得する際などに利用できます。