美しいスープとPython 3でWebページをスクレイピングする方法

前書き

多くのデータ分析、ビッグデータ、機械学習プロジェクトでは、使用するデータを収集するためにWebサイトをスクレイピングする必要があります。 Pythonプログラミング言語はデータサイエンスコミュニティで広く使用されているため、独自のプロジェクトで使用できるモジュールとツールのエコシステムを備えています。 このチュートリアルでは、Beautiful Soupモジュールに焦点を当てます。

ルイスキャロルのAlice’s Adventures in Wonderlandの第10章にあるMock Turtle’sの曲のほのめかしであるBeautiful Soupは、Webスクレイピングプロジェクトの迅速なターンアラウンドを可能にするPythonライブラリです。 現在BeautifulSoup 4として利用可能で、Python2.7とPython3の両方と互換性があり、Beautiful Soupは、解析されたHTMLおよびXMLドキュメント(閉じられていないタグまたはtag soupおよびその他の不正な形式のマークアップを含むドキュメントを含む)から解析ツリーを作成します。

このチュートリアルでは、テキストデータを取得し、収集した情報をCSVファイルに書き込むために、Webページを収集して解析します。

前提条件

このチュートリアルに取り組む前に、localまたはserver-basedのPythonプログラミング環境をマシンにセットアップしておく必要があります。

RequestsモジュールとBeautifulSoupモジュールinstalledが必要です。これは、チュートリアル「https://www.digitalocean.com/community/tutorials/how-to-work-with-web-data-」に従って実行できます。 using-requests-and-beautiful-soup-with-python-3 [リクエストを使用してWebデータを操作する方法とPython3で美しいスープを使用する方法]。これらのモジュールについて実用的な知識があると便利です。

さらに、Webからスクレイピングされたデータを扱うため、HTMLの構造とタグ付けに慣れている必要があります。

データを理解する

このチュートリアルでは、米国のNational Gallery of Artの公式ウェブサイトのデータを使用します。 ナショナルギャラリーは、ワシントンD.C.のナショナルモールにある美術館です。 ルネサンスから現在まで、13,000人以上のアーティストが制作した120,000を超える作品を所蔵しています。

アーティストのインデックスを検索したいと思います。このチュートリアルの更新時に、次のURLのInternet ArchiveWayback Machineから入手できます。

[。注意]##

Note:上記の長いURLは、このWebサイトがインターネットアーカイブによってアーカイブされているためです。

インターネットアーカイブは、インターネットサイトやその他のデジタルメディアへの無料アクセスを提供する非営利のデジタルライブラリです。 この組織は、ウェブサイトのスナップショットを取り、サイトの履歴を保存します。現在、このチュートリアルが最初に作成されたときに利用できたナショナルギャラリーのサイトの古いバージョンにアクセスできます。 インターネットアーカイブは、同じサイトと利用可能なデータの反復間での比較を含む、あらゆる種類の履歴データスクレイピングを行う際に留意すべき優れたツールです。

インターネットアーカイブのヘッダーの下に、次のようなページが表示されます。

Index of Artists Landing Page

Beautiful Soupを使用してWebスクレイピングについて学習するためにこのプロジェクトを行うため、サイトから大量のデータを取得する必要はありません。したがって、スクレイピングするアーティストデータの範囲を制限しましょう。 したがって、1つの文字を選択しましょう(この例では、文字Zを選択します)。次のようなページが表示されます。

Artist names beginning with Z list

上のページでは、執筆時点で最初にリストされているアーティストがZabaglia, Niccolaであることがわかります。これは、データのプルを開始するときに注意することをお勧めします。 この最初のページで、文字Zの次のURLを使用して作業を開始します。

リストすることを選択しているレターの合計ページ数を後で確認することが重要です。これは、アーティストの最後のページまでクリックして見つけることができます。 この場合、合計4ページあり、執筆時点でリストされている最後のアーティストはZykmund, Václavです。 Zアーティストの最後のページには、次のURLがあります。

Howeverの場合、最初のページと同じインターネットアーカイブの数値文字列を使用して上記のページにアクセスすることもできます。

このチュートリアルの後半でこれらのページを反復処理するため、これは重要です。

このWebページがどのように設定されているかを理解するために、そのDOMを確認すると、HTMLがどのように構造化されているかを理解するのに役立ちます。 DOMを検査するために、ブラウザのDeveloper Toolsを開くことができます。

ライブラリのインポート

コーディングプロジェクトを開始するために、Python 3プログラミング環境をアクティブにしましょう。 環境が存在するディレクトリにいることを確認し、次のコマンドを実行します。

. my_env/bin/activate

プログラミング環境をアクティブにして、たとえばnanoなどの新しいファイルを作成します。 ファイルには任意の名前を付けることができます。このチュートリアルでは、ファイルをnga_z_artists.pyと呼びます。

nano nga_z_artists.py

このファイル内で、使用するライブラリ(RequestsとBeautiful Soup)のインポートを開始できます。

Requestsライブラリを使用すると、Pythonプログラム内で人間が読める方法でHTTPを使用できます。BeautifulSoupモジュールは、Webスクレイピングをすばやく実行できるように設計されています。

リクエストと美しいスープの両方をimport statementでインポートします。 Beautiful Soupの場合、Beautiful Soup 4が含まれているパッケージであるbs4からインポートします。

nga_z_artists.py

# Import libraries
import requests
from bs4 import BeautifulSoup

RequestsモジュールとBeautiful Soupモジュールの両方をインポートしたら、最初にページを収集してから解析する作業に進むことができます。

Webページの収集と解析

次のステップは、リクエストを含む最初のWebページのURLを収集することです。 method requests.get()を使用して、最初のページのURLをvariablepageに割り当てます。

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


# Collect first page of artists’ list
page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')

Note:URLが長いため、このチュートリアルの上記および全体のコードは、79文字より長い行にフラグを立てるPEP 8 E501を渡しません。 最終バージョンでコードを読みやすくするために、URLを変数に割り当てることができます。 このチュートリアルのコードはデモンストレーション用であり、独自のプロジェクトの一部として短いURLを交換することができます。

次に、BeautifulSoupオブジェクトまたは解析ツリーを作成します。 このオブジェクトは、その引数としてRequestsからのpage.textドキュメント(サーバーの応答のコンテンツ)を取り、Pythonの組み込みhtml.parserからそれを解析します。

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')

# Create a BeautifulSoup object
soup = BeautifulSoup(page.text, 'html.parser')

ページが収集され、解析され、BeautifulSoupオブジェクトとして設定されたら、必要なデータの収集に進むことができます。

Webページからテキストを取得する

このプロジェクトでは、アーティストの名前とウェブサイトで利用可能な関連リンクを収集します。 アーティストの国籍や日付など、さまざまなデータを収集できます。 収集するデータが何であれ、それがWebページのDOMによってどのように記述されているかを調べる必要があります。

これを行うには、Webブラウザーで、最初のアーティストの名前Zabaglia, Niccolaを右クリック(またはCTRL + macOSをクリック)します。 ポップアップ表示されるコンテキストメニュー内に、Inspect Element(Firefox)またはInspect(Chrome)のようなメニュー項目が表示されます。

Context Menu — Inspect Element

関連するInspectメニュー項目をクリックすると、Web開発者向けのツールがブラウザ内に表示されます。 このリストでアーティストの名前に関連付けられているクラスとタグを探します。

Web Page Inspector

最初に、名前のテーブルが<div>タグ内にあり、class="BodyText"であることがわかります。 これは、Webページのこのセクション内のテキストのみを検索するように注意することが重要です。 また、名前がアーティストを説明するWebページを参照しているため、名前Zabaglia, Niccolaがリンクタグに含まれていることにも注意してください。 したがって、リンクの<a>タグを参照する必要があります。 各アーティストの名前はリンクへの参照です。

これを行うには、Beautiful Soupのfind()メソッドとfind_all()メソッドを使用して、BodyText<div>からアーティストの名前のテキストを取得します。

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


# Collect and parse first page
page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')
soup = BeautifulSoup(page.text, 'html.parser')

# Pull all text from the BodyText div
artist_name_list = soup.find(class_='BodyText')

# Pull text from all instances of  tag within BodyText div
artist_name_list_items = artist_name_list.find_all('a')

Beautiful Soup解析ツリーを適切にフォーマットされたUnicode文字列に変換するために、これらの名前をprettify()メソッドで出力します。

nga_z_artists.py

...
artist_name_list = soup.find(class_='BodyText')
artist_name_list_items = artist_name_list.find_all('a')

# Create for loop to print out all artists' names
for artist_name in artist_name_list_items:
    print(artist_name.prettify())

これまでのプログラムを実行してみましょう。

python nga_z_artists.py

そうすると、次の出力が表示されます。

この時点で出力に表示されるのは、最初のページの<div class="BodyText">タグにある<a>タグ内のすべてのアーティストの名前に関連する全文とタグ、およびいくつかの追加のタグです。下部のリンクテキスト。 この追加情報は必要ないので、次のセクションでこれを削除してみましょう。

余分なデータを削除する

これまでのところ、Webページの1つの<div>セクション内のすべてのリンクテキストデータを収集することができました。 ただし、アーティストの名前を参照しない下部のリンクは必要ないので、その部分を削除してみましょう。

ページの下部のリンクを削除するには、もう一度右クリックしてDOMをInspectします。 <div class="BodyText">セクションの下部にあるリンクがHTMLテーブルに含まれていることがわかります。<table class="AlphaNav">

Links in AlphaNav HTML Table

したがって、Beautiful Soupを使用してAlphaNavクラスを検索し、decompose()メソッドを使用して解析ツリーからタグを削除し、その内容とともにタグを破棄できます。

変数last_linksを使用して、これらの下部リンクを参照し、プログラムファイルに追加します。

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')

soup = BeautifulSoup(page.text, 'html.parser')

# Remove bottom links
last_links = soup.find(class_='AlphaNav')
last_links.decompose()

artist_name_list = soup.find(class_='BodyText')
artist_name_list_items = artist_name_list.find_all('a')

for artist_name in artist_name_list_items:
    print(artist_name.prettify())

ここで、python nga_z_artist.pyコマンドを使用してプログラムを実行すると、次の出力が表示されます。

この時点で、出力にはウェブページの下部にリンクが含まれなくなり、アーティスト名に関連付けられたリンクのみが表示されるようになりました。

これまで、アーティスト名のリンクを具体的にターゲットにしてきましたが、あまり望まない余分なタグデータがあります。 次のセクションでそれを削除しましょう。

タグからコンテンツを引き出す

実際のアーティストの名前のみにアクセスするには、リンクタグ全体を印刷するのではなく、<a>タグのコンテンツをターゲットにする必要があります。

これは、Beautiful Soupの.contentsを使用して行うことができます。これにより、タグの子がPythonlist data typeとして返されます。

リンク全体とそのタグを印刷する代わりに、子のリストを印刷するように、forループを修正しましょう(つまり、 アーティストのフルネーム):

nga_z_artists.py

import requests
from bs4 import BeautifulSoup


page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')

soup = BeautifulSoup(page.text, 'html.parser')

last_links = soup.find(class_='AlphaNav')
last_links.decompose()

artist_name_list = soup.find(class_='BodyText')
artist_name_list_items = artist_name_list.find_all('a')

# Use .contents to pull out the  tag’s children
for artist_name in artist_name_list_items:
    names = artist_name.contents[0]
    print(names)

各アイテムのindex numberを呼び出すことにより、上記のリストを反復処理していることに注意してください。

pythonコマンドを使用してプログラムを実行すると、次の出力が表示されます。

OutputZabaglia, Niccola
Zaccone, Fabian
Zadkine, Ossip
...
Zanini-Viola, Giuseppe
Zanotti, Giampietro
Zao Wou-Ki

手紙Zの最初のページにあるすべてのアーティストの名前のリストを受け取りました。

ただし、それらのアーティストに関連付けられているURLもキャプチャする場合はどうでしょうか。 Beautiful Soupのget('href')メソッドを使用して、ページの<a>タグ内で見つかったURLを抽出できます。

上記のリンクの出力から、URL全体がキャプチャされていないことがわかっているため、リンク文字列をURL文字列の前にconcatenateします(この場合はhttps://web.archive.org/)。

これらの行もforループに追加します。

nga_z_artists.py

...
for artist_name in artist_name_list_items:
    names = artist_name.contents[0]
    links = 'https://web.archive.org' + artist_name.get('href')
    print(names)
    print(links)

上記のプログラムを実行すると、bothのアーティストの名前と、アーティストについて詳しく説明しているリンクへのURLが表示されます。

OutputZabaglia, Niccola
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630
Zaccone, Fabian
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=34202
...
Zanotti, Giampietro
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11631
Zao Wou-Ki
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=3427

現在、Webサイトから情報を取得していますが、現在はターミナルウィンドウに印刷しています。 代わりに、このデータをキャプチャして、ファイルに書き込むことで他の場所で使用できるようにします。

データをCSVファイルに書き込む

ターミナルウィンドウにのみ存在するデータを収集することは、あまり役に立ちません。 コンマ区切り値(CSV)ファイルを使用すると、表形式のデータをプレーンテキストで保存でき、スプレッドシートやデータベースの一般的な形式です。 このセクションを開始する前に、how to handle plain text files in Pythonについて理解しておく必要があります。

まず、Pythonの組み込みcsvモジュールを、Pythonプログラミングファイルの先頭にある他のモジュールと一緒にインポートする必要があります。

import csv

次に、'w'モードを使用して、z-artist-names.csvというファイルを作成してwrite toに開きます(ここではファイルに変数fを使用します)。 また、一番上の行の見出し:NameLinkを記述し、これらをリストとしてwriterow()メソッドに渡します。

f = csv.writer(open('z-artist-names.csv', 'w'))
f.writerow(['Name', 'Link'])

最後に、forループ内で、アーティストのnamesとそれに関連するlinksを使用して各行を記述します。

f.writerow([names, links])

以下のファイルでこれらの各タスクの行を確認できます。

nga_z_artists.py

import requests
import csv
from bs4 import BeautifulSoup


page = requests.get('https://web.archive.org/web/20121007172955/http://www.nga.gov/collection/anZ1.htm')

soup = BeautifulSoup(page.text, 'html.parser')

last_links = soup.find(class_='AlphaNav')
last_links.decompose()

# Create a file to write to, add headers row
f = csv.writer(open('z-artist-names.csv', 'w'))
f.writerow(['Name', 'Link'])

artist_name_list = soup.find(class_='BodyText')
artist_name_list_items = artist_name_list.find_all('a')

for artist_name in artist_name_list_items:
    names = artist_name.contents[0]
    links = 'https://web.archive.org' + artist_name.get('href')


    # Add each artist’s name and associated link to a row
    f.writerow([names, links])

pythonコマンドを使用してプログラムを実行すると、ターミナルウィンドウに出力は返されません。 代わりに、作業しているディレクトリにz-artist-names.csvというファイルが作成されます。

開くために使用するものに応じて、次のようになります。

z-artist-names.csv

Name,Link
"Zabaglia, Niccola",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=11630
"Zaccone, Fabian",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=34202
"Zadkine, Ossip",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=3475w
...

または、スプレッドシートのように見える場合があります。

CSV Spreadsheet

どちらの場合でも、収集した情報がコンピュータのメモリに保存されるようになったため、このファイルを使用して、より意味のある方法でデータを操作できます。

家系の名前が文字Zで始まるアーティストのリストの最初のページからデータを取得するプログラムを作成しました。 ただし、これらのアーティストの合計4ページがWebサイトで利用可能です。

これらすべてのページを収集するために、forループを使用してさらに反復を実行できます。 これにより、これまでに記述したコードのほとんどが修正されますが、同様の概念が採用されます。

まず、リストを初期化してページを保持します。

pages = []

この初期化されたリストに次のforループを設定します。

for i in range(1, 5):
    url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ' + str(i) + '.htm'
    pages.append(url)

Earlier in this tutorial、文字Z(または使用している文字)で始まるアーティストの名前を含むページの総数に注意する必要があることに注意しました。 文字Zには4ページあるため、上記のforループを1から5の範囲で作成し、4ページのそれぞれを反復処理するようにしました。 。

この特定のWebサイトの場合、URLは文字列https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZで始まり、その後にページの番号が続きます(これは、forループからの整数iになります。 t3)s)で終わり、.htmで終わります。 これらの文字列を連結して、結果をpagesリストに追加します。

このループに加えて、上記の各ページを通過する2番目のループがあります。 このforループのコードは、これまでに作成したコードと同じように見えます。これは、合計4ページのそれぞれについてZアーティストの手紙の最初のページで完了したタスクを実行しているためです。 。 元のプログラムを2番目のforループに入れたので、元のループがnested for loopとして含まれていることに注意してください。

2つのforループは次のようになります。

pages = []

for i in range(1, 5):
    url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ' + str(i) + '.htm'
    pages.append(url)

for item in pages:
    page = requests.get(item)
    soup = BeautifulSoup(page.text, 'html.parser')

    last_links = soup.find(class_='AlphaNav')
    last_links.decompose()

    artist_name_list = soup.find(class_='BodyText')
    artist_name_list_items = artist_name_list.find_all('a')

    for artist_name in artist_name_list_items:
        names = artist_name.contents[0]
        links = 'https://web.archive.org' + artist_name.get('href')

        f.writerow([names, links])

上記のコードでは、最初のforループがページを反復処理し、2番目のforループがそれらの各ページからデータを取得してから、アーティストの名前とリンク行を追加していることがわかります。各ページの各行を1行ずつ通過します。

これらの2つのforループは、importステートメント、CSVファイルの作成とライター(ファイルのヘッダーを書き込むための行を含む)、およびpages変数の初期化(割り当て済み)の下にあります。リストへ)。

プログラミングファイルのより大きなコンテキスト内では、完全なコードは次のようになります。

nga_z_artists.py

import requests
import csv
from bs4 import BeautifulSoup


f = csv.writer(open('z-artist-names.csv', 'w'))
f.writerow(['Name', 'Link'])

pages = []

for i in range(1, 5):
    url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ' + str(i) + '.htm'
    pages.append(url)


for item in pages:
    page = requests.get(item)
    soup = BeautifulSoup(page.text, 'html.parser')

    last_links = soup.find(class_='AlphaNav')
    last_links.decompose()

    artist_name_list = soup.find(class_='BodyText')
    artist_name_list_items = artist_name_list.find_all('a')

    for artist_name in artist_name_list_items:
        names = artist_name.contents[0]
        links = 'https://web.archive.org' + artist_name.get('href')

        f.writerow([names, links])

このプログラムは少し作業をしているため、CSVファイルを作成するには少し時間がかかります。 完了すると、出力が完了し、アーティストの名前とそれに関連するZabaglia, NiccolaからZykmund, Václavへのリンクが表示されます。

思いやりがある

Webページをスクレイピングするときは、情報を取得するサーバーを慎重に検討することが重要です。

サイトにWebスクレイピングに関連する利用規約または利用規約があるかどうかを確認します。 また、サイトに自分でデータを取得する前にデータを取得できるAPIがあるかどうかを確認します。

サーバーに継続的にアクセスしてデータを収集しないでください。 サイトから必要なものを収集したら、他の人のサーバーに負担をかけるのではなく、データをローカルでスキャンするスクリプトを実行します。

さらに、ウェブサイトがあなたを特定し、質問がある場合はフォローアップできるように、あなたの名前とメールを含むヘッダーを削っておくことをお勧めします。 Python Requestsライブラリで使用できるヘッダーの例は次のとおりです。

import requests

headers = {
    'User-Agent': 'Your Name, example.com',
    'From': '[email protected]'
}

url = 'https://example.com'

page = requests.get(url, headers = headers)

識別可能な情報でヘッダーを使用すると、サーバーのログを調べた人があなたに連絡できるようになります。

結論

このチュートリアルでは、PythonとBeautiful Soupを使用してWebサイトからデータを取得しました。 収集したテキストをCSVファイルに保存しました。

より多くのデータを収集し、CSVファイルをより堅牢にすることで、このプロジェクトの作業を続けることができます。 たとえば、各アーティストの国籍と年を含めることができます。 学んだことを使用して、他のWebサイトからデータをスクレイピングすることもできます。

ウェブから情報を引き出すことについて学び続けるには、チュートリアル「https://www.digitalocean.com/community/tutorials/how-to-crawl-a-web-page-with-scrapy-and-python-3 [ ScrapyとPython 3を使用してWebページをクロールする方法]。