Pythonでwebスクレイピングしてみた

今回は、pythonを使って、どうやってwebスクレイピングすればいいのか、実際のサイトを使ってやってみようと思います。
## はじめに
pythonの勉強をしているとき、ちょくちょくwebスクレイピングって聞いたことがあったけど、実際にやったことなかった。

最近フリーランスとして活動するために副業からやろうとpythonの案件を見ていると意外とwebスクレイピングの案件があり、せっかくだしやってみるかということで今回やってみました。

## webスクレイピングとは
webスクレイピングとは、ウェブサイトから任意の情報を取得する技術のことです。
競合他社のサイトから価格情報を取得し、比較するといった市場調査とかに用いられているイメージです。

しかし、全てのサイトでこのスクレイピングを行ってよいというわけではありません。規約としてwebスクレイピングを禁止しているサイトもあるので以下の点を守るのが、よいでしょう。
1. 利用規約の遵守：ウェブサイトの利用規約を確認し、スクレイピングが許可されているかどうかを確認する
2. robots.txtの確認：ウェブサイトのルートディレクトリにある```robots.txt```ファイルを確認し、スクレイピングの許可や制限事項が記述されているかを確認
3. アクセス頻度の制御：過度なリクエストの送信はサーバーに負荷をかける可能性があるので、適切なアクセス頻度を設定する
4. データ利用範囲の尊重：利用規約や著作権に違反しないよう取得したデータの利用範囲を守る
5. プライバシーの保護：不適切なデータ収集や個人情報の公開はさけ、プライバシー保護に十分配慮する

## Webスクレイピングの方法
```Python```のwebスクレイピングの代表的なライブラリには```Requests```、```BeautifulSoup```、```Selenium```の3つがあります。それぞれ簡単に説明すると、
* ```Requests```：HTTPライブラリでwebページを取得するためのもの
* ```Selenium```：ブラウザを操作する(スクロールなど)ためのもの
* ```BeautifulSoup```：取得したHTMLから情報を抽出するもの

まずは、それぞれの使い方の基本をやっていきます。

```python:Requests
import requests

r = requests.get('https://news.yahoo.co.jp') #ページ情報の取得

print(r.headers) #header情報の表示
print("-------")
print(r.content) #Body以下のコンテンツ
```
今回は、Yahoo!ページを例に行いました。```Requests```はwebページを取得するライブラリです。```get()```を使うだけで、かなりシンプルにページ情報が取得できますね。出力結果は長くなってしまうので、割愛してます。


```python:BeautifulSoup
from bs4 import BeautifulSoup

html = "<h1>sayhello</h1>,<h1>saysay</h1>,<h2>say</h2>"

soup = BeautifulSoup(html,"html.parser")

print(soup.select("h1"))
```
```BeautifulSoup```は取得したHTMLから情報を取得するライブラリです。この例では、自分で、```html = "<h1>sayhello</h1>,<h1>saysay</h1>,<h2>say</h2>"```というようなHTMLを作成し、行っている。```html.parse```はHTMLのパーサーで、「HTMLを基に解析するよ」ってことを言っています。
最後に```select()```でどの要素を取得するかを指定しています。今回は、```h1```を指定しているので、出力結果は、
```
[<h1>sayhello</h1>,<h1>saysay</h1>]
```
となります。
```Requests```と```BeautifulSoup```を組み合わせると、webページを取得して、取得したwebページから情報抽出を行うという、一連手順が行えます。
```python:Requests+BeautifulSoup
import requests
from bs4 import BeautifulSoup

r = requests.get('https://news.yahoo.co.jp')

soup = BeautifulSoup(r.content,"html.parser")

print(soup.find("ul").text)
```
出力結果が、
```
マイページ購入履歴
```
この例では、かなりざっくりとやっているので、抜き出す情報の量が少ないですけど、やり方のイメージはついたと思います。

最後に```Selenium```についてです。```Selenium```は```JavaScript```が使われた動的なサイトのスクレイピングで用いられます。ヘッドレスブラウザの操作やページのスクロール、データの読み込み、ページの解析などを行います。
```python:Selenium
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from bs4 import BeautifulSoup
import time

#Chromeドライバオプションを設定
chrome_options = Options()
chrome_options.add_argument('--headless') #ヘッドレスモードで起動

# Chromeドライバを自動インストールして起動
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)

# 指定したURLのページを開く
url = 'https://news.yahoo.co.jp'
driver.get(url)

# ページをスクロールしてデータを読み込む
SCROLL_PAUSE_TIME = 2  # スクロールの待機時間
scroll_count = 3  # スクロール回数

# ページの高さを取得
last_height = driver.execute_script('return document.body.scrollHeight')

# スクロールを繰り返す
for _ in range(scroll_count):
    # ページをスクロール
    driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')
    # ページが読み込まれるまで待機
    time.sleep(SCROLL_PAUSE_TIME)
    # 新しい高さを取得
    new_height = driver.execute_script('return document.body.scrollHeight')
    # 高さが変わっていなければ終了
    if new_height == last_height:
        break
    last_height = new_height

# スクロール後のページのソースコードを取得
html = driver.page_source

# スクレイピングしたHTMLをBeautiful Soupで解析
soup = BeautifulSoup(html, 'html.parser')

# 特定の要素を抽出
titles = soup.select('title')
for title in titles:
    print(title.text)
    

# ドライバを終了
driver.quit()
```
このコードは、Yahoo!ニュースのウェブページにアクセスし、ページを3回スクロールしてから、HTMLを取得・解析し、```<title>```タグの内容を取得して表示するものです。

```Selenium```単体では、ブラウザ操作はできないので、```Selenium```の命令に対してブラウザ操作を行うためにChromeドライバのインストールが必要です。

インストールしたらヘッドレスモードで起動します。ヘッドレスモードは、ブラウザ画面を表示させずにバックグラウンドで動かすモードです。練習として行っているので、特に画面表示させる必要はないでしょう。

後は、プログラムに逐一コメントを書いているので、そちらを見ていただけたら理解できるでしょう。

## 実際のサイトでWebスクレイピングをしてみよう
今回は、プログラミングスクールの価格を取得してみたいと思います。今回は1社だけしか行いませんが、同様な方法で何社かやれば価格比較ができると思います。

まずは、対象となるサイトですが、テックアカデミーさんのサイトを使わせていただこうと思います。しっかり利用規約を読んで、禁止事項にスクレイピングに関して書かれていないかを確認しました。

次にデベロッパーツールを使用して、価格が書かれている個所を確認し、スクレイピングを行っていきます。
```python:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
import time

options = webdriver.ChromeOptions()
options.add_argument("--headless")  # 実行時にブラウザを表示しない
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)

url = "https://techacademy.jp/course/first-sidejob"
driver.get(url)
time.sleep(3)  # 読み込み待機

parents = driver.find_elements(By.XPATH, '//*[contains(text(),"実質負担額")]/ancestor::*[1]')
td_elements = driver.find_elements(By.XPATH, '//td[.//span[contains(text(), "リスキリング")]]')

prices = []


for td in td_elements:
    prices.append(td.text)

for elem in parents:
    prices.append(elem.text)
    
driver.quit()

print("抽出した価格情報：")
for price in prices:
    print(price)
```
上記のプログラムで、```parents = driver.find_elements(By.XPATH, '//*[contains(text(),"実質負担額")]/ancestor::*[1]')```

```td_elements = driver.find_elements(By.XPATH, '//td[.//span[contains(text(), "リスキリング")]]')```
この2行部分が、特定の情報を抜き取っている部分です。
```parents```のほうが、実質負担額という文字を含む要素の親または祖先要素を取得していて、```td_elements```が```td```タグの中の```span```タグでリスキリングという文字が含まれているものを取得しています。

このコードを実行すると、
```出力結果
抽出した価格情報：
482,900円 （税込）
分割払い例20,121円/月 ※1
リスキリング補助金適応
最大 70 ％還元で
175,600円
537,900円 （税込）
分割払い例22,413円/月 ※1
リスキリング補助金適応
最大 70 ％還元で
195,600円
実質負担額125,600円
実質負担額145,600円
```
このようにしっかり価格情報が取得できました。