Python+Seleniumを使ったスクレイピングその2(2023年10月時点)
この前はSeleniumを使ってスクレイピングを行い、ページソースを取得する部分までをメモとして残しました。今回は、さらにそのページソースから必要な部分を取得するやり方について、メモとして残しておきます。なお、今回もWindowsで行う前提です。
今回も例としてPythonの公式サイトにアクセスしてみます。公式サイトのトップページから、Latest Newsの各ページにアクセスし、本文を取得してデータフレームに格納することに挑戦してみます。
Webサイトへのアクセス
前回のおさらいですが、目的とするwebサイトにアクセスします。
# ライブラリをインポート from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # アクセスしたいURLを指定 url = "http://www.python.org" # ブラウザを操作するためのwebdriverを設定 options = Options() options.add_argument("--headless") driver = webdriver.Chrome(options=options) wait = WebDriverWait(driver=driver, timeout=30) # 最大待ち時間の設定 # ブラウザを起動し、URLにアクセス driver.get(url) wait.until(EC.presence_of_all_elements_located) # HTMLの要素が全てレンダリングされるまで待つ
ここで以下のように処理すると、あとはBeautifulSoup
でHTMLを処理するだけになります。ただし、今回は勉強も兼ねてSelenium
だけでHTMLを処理し、必要な要素を取得してみようと思います。
from bs4 import BeautifulSoup def get_soup(driver): html = driver.page_source soup = BeautifulSoup(html, "html.parser") return soup soup = get_soup(driver)
HTMLを確認
実際にブラウザでWebサイトにアクセスし、処理したいHTMLの構造を確認しておきます。Chromeであれば、Ctrl
+ Shift
+i
でDevToolsを開き、WebサイトのHTMLを確認することが出来ます。右側のパネル上で、実際に取得したい要素の種類や、つけられているクラス名などを知ることが出来ます。
要素を取得
それでは、実際に要素を取得していきます。まずはLatest NewsのタイトルとURLの一覧を取得しておきます。
要素を取得する場合、次の2つのメソッドと、要素の指定方法、要素の中身(文章等)の取得方法を一つだけ覚えておけばなんとかなります。
要素の取得方法
取得する際のメソッドは次の通りです。
find_element()
|要素を一つだけ取得します。同じ条件の要素が複数ある場合は最初の要素のみ取得します。find_elements()
|条件に当てはまる要素を全て、リスト形式で取得します。
また、指定方法は次の通りです。
By.CSS_SELECTOR
|要素の種類(div
等)とclass名、あるいはid名で指定することが出来ます。ID名の場合は基本的に一要素にしかつけないので、find_element()
としか組み合わせません。
class名で指定する場合、.
を前につけて指定します。id名の場合は#
です。なお、ある要素のさらに子要素を取得する場合は、直接の子要素であれば>
、孫以降の要素も含むのであれば" "(※スペース)をつければOKです。
例えば、次のようなHTMLのページがあり、driver
でアクセスしていたものとします。
<div id="id_name"> <p class="a_class">a_hoge</p> <p class="a_class">a_fuga</p> <p class="b_class">b_fuga</p> </div>
次のように指定することで、要素の取得が出来ます。
# id名で指定する場合 id_element = driver.find_element(By.CSS_SELECTOR, "div#id_name") # class名で指定する場合 a_class_element_list = driver.find_elements(By.CSS_SELECTOR, "p.a_class") # id名で指定した後、その下の子要素をまとめて指定する場合 p_element_list = driver.find_elements(By.CSS_SELECTOR, "div#id_name p")
要素の中身を取得する際には、get_attribute()
を使います。aタグに対して行う場合、textContent
を指定すれば文章を、href
を指定すればURLを取得できます。
# 要素を取得 a_element = driver.find_element(By.CSS_SELECTOR, "a#sample_id") # 要素内の中身を取得 txt = a_alement.get_attribute("textContent") href = a_element.get_attribute("href")
タイトルとURLを取得
それでは、実際にLatest NewsのタイトルとURLを取得していきます。
# Latest NewsのタイトルとURLを取得する latest_news_elements_list = driver.find_elements(By.CSS_SELECTOR, "div.blog-widget li") latest_news_url_dict = {} for elem in latest_news_elements_list: # aタグを取得 a_tag = elem.find_element(By.CSS_SELECTOR, "a") # aタグから、タイトルとURLを取得 news_title = a_tag.get_attribute("textContent") news_url = a_tag.get_attribute("href") # dictに保存 latest_news_url_dict[news_title] = news_url print(latest_news_url_dict)
{'Announcing our new Community Communications Manager!': 'https://pyfound.blogspot.com/2023/10/announcing-community-communications-mgr.html', 'September & October Board Votes': 'https://pyfound.blogspot.com/2023/10/september-october-board-votes.html', 'Security Developer-in-Residence 2023 Q3 Report': 'https://pyfound.blogspot.com/2023/10/security-developer-in-residence-2023-q3-report.html', 'Python 3.13.0 alpha 1 is now available': 'https://pythoninsider.blogspot.com/2023/10/python-3130-alpha-1-is-now-available.html', 'Python 3.11.6 is now available': 'https://pythoninsider.blogspot.com/2023/10/python-3116-is-now-available.html'}
Latest Newsの本文を取得
それでは、実際に各URLにアクセスし、本文を取得していきます。要領は先程と同じです。各URLにアクセスする際、ちゃんと本文が表示されてから本文の取得を開始するように、WebDriverWait()
を使っています。
import polars as pl # 各Latest NewsのURLにアクセスし、本文(pタグ)を取得する news_description_list = [] for title, url in latest_news_url_dict.items(): # URLにアクセス driver.get(url) WebDriverWait(driver, 30).until( EC.presence_of_element_located((By.CSS_SELECTOR, "div.post")) ) # 本文を取得(画像等が間に本文中に存在する影響で、複数のpタグに分かれていることに注意) description_elements = driver.find_elements(By.CSS_SELECTOR, "div.post p") description = "" for elem in description_elements: description += elem.get_attribute("textContent") # リストに保存 news_description_list.append(description) # データフレームに格納 latest_news_dict = { "title": latest_news_url_dict.keys(), "url": latest_news_url_dict.values(), "description": news_description_list } df = pl.from_dict(latest_news_dict) df.head()
title | url | description |
---|---|---|
str | str | str |
"Announcing our… | "https://pyfoun… | "We announced o… |
"September & Oc… | "https://pyfoun… | "We’re writing … |
"Security Devel… | "https://pyfoun… | "It’s been thre… |
"Python 3.13.0 … | "https://python… | " It’s not a ve… |
"Python 3.11.6 … | "https://python… | " Python 3.11.… |
しっかりと本文を取得し、データフレームに格納することが出来ました!