序章

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

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

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

前提条件

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

RequestsとBeautifulSoupモジュールがインストールされている必要があります。これは、チュートリアル「RequestsとBeautifulSoupwith Python3を使用してWebデータを操作する方法」に従って実行できます。 これらのモジュールに実際に精通していることも役立ちます。

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

データを理解する

このチュートリアルでは、米国の National Gallery ofArtの公式ウェブサイトのデータを使用します。 ナショナルギャラリーは、ワシントンDCのナショナルモールにある美術館です。 13,000人以上の芸術家によって行われたルネッサンスから現在までの12万点以上の作品を保持しています。

このチュートリアルの更新時に、インターネットアーカイブウェイバックマシンから次のURLで入手できるアーティストのインデックスを検索したいと思います。

https://web.archive.org/web/20170131230332/https://www.nga.gov/collection/an.shtm

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

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

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

Index of Artists Landing Page

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

Artist names beginning with Z list

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

https://web.archive.org/web/20121007172955/http://www.nga.gov/collection/anZ1.htm

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

https://web.archive.org/web/20121010201041/http://www.nga.gov/collection/anZ4.htm

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

https://web.archive.org/web/ 20121007172955 /http://www.nga.gov/collection/anZ4.htm

このチュートリアルの後半でこれらのページを繰り返し処理するため、これは注意することが重要です。

このWebページがどのように設定されているかを理解するために、HTMLがどのように構造化されているかを理解するのに役立つDOMを見ることができます。 DOMを検査するために、ブラウザの開発者ツールを開くことができます。

ライブラリのインポート

コーディングプロジェクトを開始するには、Python3プログラミング環境をアクティブ化しましょう。 環境が配置されているディレクトリにいることを確認し、次のコマンドを実行します。

  1. . my_env/bin/activate

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

  1. nano nga_z_artists.py

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

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

importステートメントを使用してリクエストとBeautifulSoupの両方をインポートします。 Beautiful Soupは、BeautifulSoup4が入っているパッケージbs4からインポートします。

nga_z_artists.py
# Import libraries
import requests
from bs4 import BeautifulSoup

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

Webページの収集と解析

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

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')

<$>[注] ノート :URLが長いため、このチュートリアル全体の上記のコードは通過しません PEP 8 E501 79文字より長い行にフラグを立てます。 最終バージョンでコードを読みやすくするために、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ページのこのセクション内のテキストのみを検索するように注意することが重要です。 また、 Zabaglia、Niccola という名前は、アーティストを説明するWebページを参照しているため、リンクタグに含まれていることにも注意してください。 したがって、リンクについては<a>タグを参照する必要があります。 各アーティストの名前は、リンクへの参照です。

これを行うには、BeautifulSoupの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 <a> tag within BodyText div
artist_name_list_items = artist_name_list.find_all('a')

次に、プログラムファイルの下部に、artist_name_list_items変数に入れたすべてのアーティスト名を反復処理するために、forループを作成します。

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())

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

  1. python nga_z_artists.py

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

Output
<a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630"> Zabaglia, Niccola </a> ... <a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=3427"> Zao Wou-Ki </a> <a href="/web/20121007172955/https://www.nga.gov/collection/anZ2.htm"> Zas-Zie </a> <a href="/web/20121007172955/https://www.nga.gov/collection/anZ3.htm"> Zie-Zor </a> <a href="/web/20121007172955/https://www.nga.gov/collection/anZ4.htm"> <strong> next <br/> page </strong> </a>

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

不要なデータの削除

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

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

Links in AlphaNav HTML Table

したがって、BeautifulSoupを使用して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コマンドを使用してプログラムを実行すると、次の出力が返されます。

Output
<a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630"> Zabaglia, Niccola </a> <a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=34202"> Zaccone, Fabian </a> ... <a href="/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=11631"> Zanotti, Giampietro </a> <a href="/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=3427"> Zao Wou-Ki </a>

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

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

タグからコンテンツをプルする

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

これは、BeautifulSoupの.contentsを使用して行うことができます。これにより、タグの子がPythonリストデータ型として返されます。

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 <a> tag’s children
for artist_name in artist_name_list_items:
    names = artist_name.contents[0]
    print(names)

各アイテムのインデックス番号を呼び出して、上記のリストを繰り返し処理していることに注意してください。

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

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

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

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

上記のリンクの出力から、URL全体がキャプチャされていないことがわかっているため、リンク文字列をURL文字列の先頭に連結します(この場合は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)

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

Output
Zabaglia, 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)ファイルを使用すると、表形式のデータをプレーンテキストで保存でき、スプレッドシートやデータベースの一般的な形式です。 このセクションを始める前に、Pythonでプレーンテキストファイルを処理する方法をよく理解しておく必要があります。

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

import csv

次に、'w'モード。 また、一番上の行の見出し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ページがあります。

これらすべてのページを収集するために、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)

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

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

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

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ページをスクレイピングするときは、情報を取得するサーバーに注意を払うことが重要です。

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

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

さらに、Webサイトがあなたを識別し、質問がある場合にフォローアップできるように、名前と電子メールが記載されたヘッダーをスクレイプすることをお勧めします。 PythonRequestsライブラリで使用できるヘッダーの例は次のとおりです。

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サイトからデータを取得することもできます。

Webから情報を取得する方法について引き続き学習するには、チュートリアル「ScrapyとPython3を使用してWebページをクロールする方法」をお読みください。