・macOS Big Sur
・Python3.8
・Google Chrome
Webスクレイピング
Webサイトから目的の情報を収集するための技術を「Webスクレイピング」といいます。
ここでは、Python WebサイトにあるmacOS用インストーラのバージョン一覧を抽出する手順をコードサンプルにして説明します。
Webスクレイピングの手順
- Webサーバ(Webサイトを提供しているコンピュータ)へWebサイトの情報を取得するためのリクエストを送る。(リクエスト送信)
- Webサーバからレスポンスを受け取る。(レスポンス取得)
- 受け取ったレスポンスから目的の情報を見つけ出す。(HTML解析)
リクエスト送信とレスポンス取得
Webサーバにリクエストを送信し、レスポンスを取得するには、「requestsライブラリ」を使用します。
requestsライブラリを使用することで、Webサーバから「HTML(Hyper Text Markup Languageの略。Webサイトを作るために使用される言語。)」を取得することができます。なお、WebサーバーとHTML等のデータを通信する方式を「HTTP(Hyper Text Transfer Protocolの略。)通信」といいます。
requestsライブラリ
% pip3 install requests
※ターミナル上で実行します。
import requests
requestsライブラリの「get()関数」を使用することで、指定したWebサイトにリクエストを送信し、レスポンスを取得することができます。
requests.get('URL')
この結果、指定したURLのWeb情報を含んだオブジェクトが返されます。このオブジェクトには様々な属性(情報)が格納されています。各属性は「オブジェクト名.属性名」で参照することができます。
主な属性 | 内容 |
url | アクセスしたURL。 |
status_code | ステータスコード(リクエスト結果を3桁の数字で表したもの)。 |
headers | レスポンスヘッダ。 |
encoding | エンコーディング。 |
text | HTMLをテキストで表示します。 |
content | HTMLをバイナリデータで表示します。 |
ステータスコードの主な種類。(詳細はWikipediaのページ「HTTPステータスコード」を参照。)
- 2xx:リクエストが正常に処理された状態。
- 3xx:リクエストの完了に追加の処理が必要な状態。(情報が別の場所に移動されているため、転送を促されている等。)
- 4xx:リクエストを送信した側で誤りがある状態。
- 5xx:リクエストを受信した側でエラーがある状態。
import requests # requestsライブラリをインポートします。
url = 'https://www.python.org/downloads/macos/'
response = requests.get(url) # Webサーバにリクエストを送信し、レスポンスを受け取ります。
print(response.status_code) # ステータスコードを表示します。
# 実行結果
200
# 続きです。
print(response.text) # HTMLを表示します。
# 実行結果
<!doctype html>
<!--[if lt IE 7]> <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 lt-ie8 lt-ie9"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--><html class="no-js" lang="en" dir="ltr"> <!--<![endif]-->
<head>
...(省略)...
HTML解析
Webサーバーから受け取ったHTMLを、「BeautifulSoupライブラリ」を使って解析することで、目的の情報を見つけ出します。
BeautifulSoupライブラリ
% pip3 install BeautifulSoup4
※ターミナル上で実行します。
※pip3 install bs4でも可です。
from bs4 import BeautifulSoup
BeautifulSoup('HTML', 'parser')
※第1引数には解析対象のHTMLを指定し、第2引数には解析の種類を指定します。
※解析の種類は複数ありますが、基本的には標準ライブラリで提供されている「html.parser」を指定すれば良いです。
# 続きです。
from bs4 import BeautifulSoup # BautifulSoupをインポートします。
soup = BeautifulSoup(response.text, 'html.parser')
情報の絞り込み
この結果、HTML解析用のオブジェクトが返されます。このオブジェクトには様々なメソッドが格納されています。
主なメソッド名 | 機能 |
find('タグ') | 引数で指定したタグを検索し、一致した最初の1つを返します。 |
find_all('タグ') | 引数で指定したタグを検索し、一致したもののリストを返します。 |
get_text() | タグを取り除いたテキストのみの文字列を返します。 |
find()メソッドやfind_all()メソッドでは、第2引数にHTMLの「class(タグをグループ管理するための名前)」や「id(タグを管理するための一意な識別子)」を指定することで、さらに情報を絞り込むこともできます。
指定する際は、「class_='〜'」や「id='〜'」とします。(※既にPythonのキーワードにはclassが存在するため、「class_」とします。)
必要な情報が記載されているタグを見つけるには、Webブラウザに付属している「開発ツール」の機能を使用すると良いです。Google Chromeの場合は、Webページ上の任意の場所で右クリックし、表示されたコンテキストメニューから「検証」を選ぶと、画面の右側にHTMLの情報が表示されます。
HTMLは、Webページの構造を指定する言語です。タイトル、見出し、本文、画像等を<タグ名>と</タグ名>で囲って指定します。
<div class="one">
<!--これはコメントです。<div>はまとまりを作ります。-->
<!--classで管理名を付けることができます。-->
<h1>見出し1</h1>
<p>段落内の文章</p>
</div>
<div class="two">
<h1>見出し2</h1>
<p>段落内の文章</p>
</div>
例えば、find_all('h1')で[<h1>見出し1</h1>, <h1>見出し2</h1>]となるリストを作成することができます。
Webサイトでは同種の情報は同じ構造で整理されることが多いです。タグの種類を揃えて使用する、classでグループ分けするといった方法で整理されているので、タグを適切に指定することで複数の情報を効率的に取得することができます。
実際にGoogle Chromeの開発ツールを使って、目的の箇所にあたりをつけていきます。
目的の箇所のHTMLをもう少し詳細に確認してみます。
<div class="column">
<h2>Stable Releases</h2>
<!--<ul>タグは順序の無い箇条書きを表します。各項目は<li>タグで囲います。-->
<ul>
<li>
<!--目的のバージョン情報は、この階層の<a>タグ内にあることが分かります。-->
<a href="/downloads/release/python-31011/">Python 3.10.11 - April 5, 2023</a>
<ul>
<li>Download
<!--インストーラーの項目は不要なので、この階層の内容は抽出の対象外とします。-->
<a href="https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg">macOS 64-bit universal2 installer</a>
</li>
</ul>
</li>
<li>
<a href="/downloads/release/python-3113/">Python 3.11.3 - April 5, 2023</a>
<ul>
<li>Download
<a href="https://www.python.org/ftp/python/3.11.3/python-3.11.3-macos11.pkg">macOS 64-bit universal2 installer</a>
</li>
</ul>
</li>
...(省略)...
classが'column'の<div>タグ→<h2>タグ→<ul>タグ→<li>タグ(この階層内でバージョン毎の情報が記載されていることが分かります。)→<a>タグに必要な情報が入っています。なお、下の階層にも<a>タグがありますが、こちらはインストーラーのため、不要の情報です。
また、上記の必要な情報が入っている<a>タグと、同じレベルの階層の<a>タグに、他のバージョン名が入っていることも分かります。
# 続きです。
v1 = soup.find('div',class_='column') # classが'column'の<div>タグで囲われた部分を抽出します。
print(v1)
# 実行結果
<div class="column">
<h2>Stable Releases</h2>
<ul>
<li>
<a href="/downloads/release/python-31011/">Python 3.10.11 - April 5, 2023</a>
<ul>
<li>Download <a href="https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg">macOS 64-bit universal2 installer</a></li>
</ul>
</li>
<li>
<a href="/downloads/release/python-3113/">Python 3.11.3 - April 5, 2023</a>
<ul>
<li>Download <a href="https://www.python.org/ftp/python/3.11.3/python-3.11.3-macos11.pkg">macOS 64-bit universal2 installer</a></li>
</ul>
</li>
...(省略)...
BeautifulSoupでは、絞り込んだオブジェクトの要素に「.タグ名」を付けることで、当該タグの最初の該当箇所だけを抽出することもできます。
また、find_all()メソッドは、対象のタグに対して子孫要素に当たる階層まで検索対象としますが、「recursive引数」に「False」を設定することで、直下の階層までを検索対象とすることができます。なお、recursive引数は、デフォルトでは「True」となっています。
例えば、<li>タグが入れ子になっている場合、外側の<li>タグ部分と内側の<li>タグ部分をそれぞれリストの要素にしてしまいます。「recursive=False」と指定することで、外側の<li>タグ部分のみを要素とするリストを作成することができます。
これらを利用し、以下のようにバージョン情報名が記載された<li>タグのまとまりを抽出し、該当の<a>タグのみを抽出します。
最後にget_text()メソッドを使用し、タグを取り除いたテキストのみの文字列を抜き出すように調整します。
# 続きです。
v2 = v1.ul.find_all('li', recursive=False) # <ul>タグの階層に移動し、1つ目の<li>タグで囲われた部分を抽出し、リスト化します。
for i in range(len(v2)):
# 各要素の最初の<a>タグのみ抽出します。(2番目の<a>タグに記載されているインストーラー名は除きます。)
# さらに、get_text()メソッドを使用し、タグを取り除いたテキストのみの文字列を抜き出すように調整します。
print(v2[i].a.get_text())
# 実行結果
Python 3.10.12 - June 6, 2023
Python 3.11.4 - June 6, 2023
Python 3.7.17 - June 6, 2023
Python 3.8.17 - June 6, 2023
Python 3.9.17 - June 6, 2023
Python 3.10.11 - April 5, 2023
Python 3.11.3 - April 5, 2023
Python 3.10.10 - Feb. 8, 2023
Python 3.11.2 - Feb. 8, 2023
Python 3.11.1 - Dec. 6, 2022
Python 3.10.9 - Dec. 6, 2022
...(省略)...