テキストスクレイピング: ステップバイステップチュートリアル

このガイドでは、設定からデータの保存まで、Python でのテキストスクレイピングについて解説し、プロキシを使用して IP ブロックを回避するためのヒントもご紹介します。
4 min read
Text scraping tutorial blog image

ウェブスクレイピング は、ウェブページからデータを抽出するプロセスです。データにはさまざまな形式があるため、具体的にテキストデータを収集する場合にテキストスクレイピングという用語が用いられます。

あらゆるビジネス上の意思決定を成功させるには、大量の関連データが不可欠です。競合他社のウェブサイトから情報をスクレイピングすることで、それぞれのビジネスロジックに関する深い情報が得られ、競争力を高めるうえで役立ちます。このチュートリアルでは、Python でテキストスクレイパーを実装し、ウェブデータを簡単に抽出して使用できるようにする方法を解説します。

前提条件

このチュートリアルを始める前に、次の前提条件が満たされているかを確認してください:

  • Python と pip の最新バージョンがシステムにインストールされていること。
  • Python 仮想環境。ウェブページの HTML コンテンツを取得するための requests、HTML から目的のテキストやデータを解析して抽出するための Beautiful Soup、抽出したデータを CSV ファイルなどの構造化された形式で整理して保存するための pandas など、必要なパッケージをすべて仮想環境にインストールしてください。

Python でのウェブスクレイピングを始めるための詳細情報をお探しの場合は、こちらの記事をご覧ください。

ウェブサイトの構造の把握

スクレイピングを始める前に、ターゲットとなるウェブサイトの構造を分析する必要があります。ウェブサイトは HTML を使用して構築されています。HTML は、コンテンツの編成と表示方法を定義するマークアップ言語です。

見出し、段落、リンクなど、各コンテンツは HTML タグで囲まれています。これらのタグは、スクレイピングしたいデータがどこにあるかを特定するのに役立ちます。たとえば、この例では、模擬ウェブサイトの Quotes to Scrape から引用をスクレイピングします。このウェブサイトの構造を確認するには、ブラウザでウェブサイトを開き、ページを右クリックしてデベロッパーツールにアクセスし、[検証] または [要素の検証] を選択します。これにより、ページの HTML コードが表示されます:

ウェブブラウザでの要素の検証

じっくりと構造を検証し、<div><span><p><a> などのタグを探します。これらのタグには、通常抽出したいテキストやリンクが含まれています。また、タグには通常 class 属性が含まれています。その目的は、HTML 要素に特定のクラスを定義して、CSS でスタイルを設定したり、JavaScript で選択したりできるようにすることです。

注: class 属性は、ページ上の同じスタイルや構造の特定の要素をターゲットとし、必要なデータを正確かつ簡単に抽出できるため、テキストスクレイピングで特に役立ちます。

この例では、引用が quote クラスの div 要素に含まれています。各引用のテキストと著者に関心がある場合、テキストは text クラスの div 内に含まれ、著者は author クラスの small 要素に含まれています:

引用の HTML 構造

HTML の仕組みに慣れていない方は、こちらの HTML ウェブスクレイピングに関する記事で詳細をご覧ください。

ウェブサイトのテキストスクレイピング

ウェブサイトの構造を把握したところで、次のステップは Quotes to Scrape サイトのスクレイピングに使用するコードを書くことです。

Python は使いやすく、requests や BeautifulSoup などの強力なライブラリがあるため、このタスクによく使用されています。requests ライブラリを使用して、ページの HTML コンテンツを取得します。これは、分析や抽出を行う前に生データを取得する必要があるため、欠かせないステップです。HTML コンテンツを取得したら、BeautifulSoup を使用してより扱いやすい構造に整理できます。

まず、text-scraper.py というテキストスクレイピングスクリプト用の Python ファイルを作成します。次に、BeautifulSoup と requests をインポートします:

import requests
from bs4 import BeautifulSoup

スクレイピングするウェブサイトの URL を指定して、GET 要求を送信します:

# URL of the quotes website
url = 'https://quotes.toscrape.com/'

# Send a GET request to the URL
response = requests.get(url)

GET 要求を送信すると、ページ全体の HTML が返されます。それを解析して、必要なデータ (この場合は各引用のテキストと著者) のみを抽出する必要があります。そのためには、まず BeautifulSoup オブジェクトを作成して HTML を解析する必要があります:

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

引用を含む (つまり quote クラスの) div 要素をすべて検索します:

quotes = soup.find_all('div', class_='quote')

引用を保存するリストを作成します:

data = []

次に、各引用からテキストと著者を抽出し、data リストに保存します:

for quote in quotes:
    text = quote.find('span', class_='text').text.strip()
    author = quote.find('small', class_='author').text.strip()

    data.append({
        'Text': text,
        'Author': author
    })

スクリプトは次のようになります:

import requests
from bs4 import BeautifulSoup

# URL of the quotes website
url = 'http://quotes.toscrape.com/'

# Send a GET request to the URL
response = requests.get(url)

# Create a BeautifulSoup object to parse the HTML
soup = BeautifulSoup(response.text, 'html.parser')

# Find all quote containers
quotes = soup.find_all('div', class_='quote')

# Extract data from each quote
data = []
for quote in quotes:
    text = quote.find('span', class_='text').text.strip()
    author = quote.find('small', class_='author').text.strip()

    data.append({
        'Text': text,
        'Author': author
    })

print(data)

それでは、ターミナルからスクリプトを実行します:

# For Linux and macOS
python3 text-scraper.py

# For Windows
python text-scraper.py

抽出された引用のリストが出力されます:

[{'Author': 'Albert Einstein',
  'Text': '"The world as we have created it is a process of our thinking. It '
        'cannot be changed without changing our thinking."'},
 {'Author': 'J.K. Rowling',
  'Text': '"It is our choices, Harry, that show what we truly are, far more '
        'than our abilities."'},
 {'Author': 'Albert Einstein',
  'Text': '"There are only two ways to live your life. One is as though '
        'nothing is a miracle. The other is as though everything is a '
        'miracle."'},
 {'Author': 'Jane Austen',
  'Text': '"The person, be it gentleman or lady, who has not pleasure in a '
        'good novel, must be intolerably stupid."'},
 {'Author': 'Marilyn Monroe',
  'Text': ""Imperfection is beauty, madness is genius and it's better to be "
        'absolutely ridiculous than absolutely boring."'},
 {'Author': 'Albert Einstein',
  'Text': '"Try not to become a man of success. Rather become a man of '
        'value."'},
 {'Author': 'André Gide',
  'Text': '"It is better to be hated for what you are than to be loved for '
        'what you are not."'},
 {'Author': 'Thomas A. Edison',
  'Text': ""I have not failed. I've just found 10,000 ways that won't work.""},
 {'Author': 'Eleanor Roosevelt',
  'Text': '"A woman is like a tea bag; you never know how strong it is until '
        "it's in hot water.""},
 {'Author': 'Steve Martin',
  'Text': '"A day without sunshine is like, you know, night."'}]

このテキストスクレイピングはわりと簡単に見えますが、ウェブスクレイピングの際には恐らく、ウェブサイトが過剰な要求を検出した場合の IP ブロックや、自動アクセスを防ぐための CAPTCHA などの問題が発生します。これらの課題を克服するには、プロキシを使用できます。

プロキシによる匿名でのスクレイピング

プロキシは、IP アドレスをローテーションして要求がさまざまな場所から来ているように見せることで、IP ブロックや CAPTCHA を回避します。プロキシを使用するには、request.get() メソッドを構成して、すべての要求をプロキシサーバー経由でルーティングする必要があります。

このシナリオでは、Bright Data ローテーションプロキシを使用します。これにより、195 か国以上に広がる 7,200 万を超える IP アドレスを使用できます。まず、右上の [無料トライアルを開始] を選択し、登録フォームに入力して、[アカウントを作成] をクリックし、無料の Bright Data アカウントを作成します。

Bright Data の登録フォーム

基本的な住宅用プロキシの作成

Bright Data アカウントを作成したら、ログインして [プロキシとスクレイピング] セクションに移動します。[プロキシネットワーク] セクションで [住宅用プロキシ] を見つけ、[開始] をクリックします。

Bright Data ダッシュボード: **プロキシとスクレイピング** セクション

住宅用プロキシの新しいゾーンを追加するように求められます。すべてデフォルトのままにし、ゾーンに名前を付けて、[追加] をクリックします:

新しい住宅用プロキシゾーンの作成

これだけで新しい住宅用プロキシゾーンが作成されます!

プロキシを使用するには、認証情報 (ユーザー名、パスワード、ホスト) が必要です。これらの認証情報は、再度 [プロキシとスクレイピング] セクションにアクセスし、作成したプロキシゾーンを選択して確認します:

作成されたプロキシゾーンのリスト

プロキシゾーンをクリックすると、ゾーンコントロールパネルが表示されます。[認証] セクションに、認証情報が表示されます:

Bright Data のプロキシゾーン認証情報

スクレイピングスクリプトの更新

プロキシ認証情報を入手したところで、プロキシを構成します。まず、認証情報を変数として保存します:

host = 'brd.superproxy.io'
port = 22225

username = 'brd-customer-<customer_id>-zone-<zone_name>'
password = '<zone_password>'

次に、保存されている認証情報からプロキシ URL を作成します:

proxy_url = f'http://{username}:{password}@{host}:{port}'

HTTP と HTTPS の両方に対してプロキシ構成を作成します:

proxies = {
    'http': proxy_url,
    'https': proxy_url
}

そして、既存の requests.get() 呼び出しにプロキシ構成を追加します:

response = requests.get(url, proxies=proxies)

この時点で、スクリプトは次のようになります:

import requests
from bs4 import BeautifulSoup

# BrightData credentials
host = 'brd.superproxy.io'
port = 22225

username = 'brd-customer-<customer_id>-zone-<zone_name>'
password = '<zone_password>'

# Compose a proxy URL
proxy_url = f'http://{username}:{password}@{host}:{port}'

# Create a proxy configuration
proxies = {
    'http': proxy_url,
    'https': proxy_url
}

# URL of the quotes website
url = 'http://quotes.toscrape.com/'

# Send a GET request to the URL via the specified proxy
response = requests.get(url, proxies=proxies)

# Create a BeautifulSoup object to parse the HTML
soup = BeautifulSoup(response.text, 'html.parser')

# Find all quote containers
quotes = soup.find_all('div', class_='quote')

# Extract data from each quote
data = []
for quote in quotes:
    text = quote.find('span', class_='text').text.strip()
    author = quote.find('small', class_='author').text.strip()

    data.append({
        'Text': text,
        'Author': author
    })

print(data)

スクリプトの実行とテスト

このスクリプトを実行すると、プロキシを構成していないスクリプトと同じ結果が得られます。違いは、現在スクレイピングしているウェブサイトは、要求がどこか別の場所から送信されていると認識しているため、実際の場所が特定されない点です。この仕組みを説明するために、新しい簡単なスクリプトを書いてみましょう。

必要なライブラリをインポートし、スクリプトの url を "http://lumtest.com/myip.json" に設定します:

import requests
from bs4 import BeautifulSoup

url = "http://lumtest.com/myip.json"

プロキシ構成なしで url に GET 要求を送信し、応答用の BeautifulSoup オブジェクトを作成します:

# Send a GET request to the URL
response = requests.get(url)

# Create a BeautifulSoup object to parse the HTML
soup = BeautifulSoup(response.text, 'html.parser')

最後に、soup オブジェクトを出力します:

print(soup)

このスクリプトを実行します。すると、自分の IP アドレスと場所に関する情報が応答として返されます。

比較のために、Bright Data プロキシを使用するように GET 要求を構成し、それ以外はすべて同じままにします:

# BrightData credentials
host = 'brd.superproxy.io'
port = 22225

username = 'brd-customer-hl_459f8bd4-zone-test_residential_proxy'
password = '8sdgouh1dq5h'

proxy_url = f'http://{username}:{password}@{host}:{port}'

proxies = {
    'http': proxy_url,
    'https': proxy_url
}

# Send a GET request to the URL
response = requests.get(url, proxies=proxies)

新しいスクリプトを実行すると、応答として別の IP アドレスが返されます。これは実際の IP アドレスではなく、設定したプロキシの IP アドレスです。本質的に、IP アドレスをプロキシサーバーの背後に隠していることになります。

データの保存

ウェブサイトからデータをスクレイピングしたら、次のステップは、それを簡単にアクセスして分析できる構造化された形式で保存することです。CSV はデータ分析ツールやプログラミング言語で広くサポートされているため、この目的によく使用されます。

スクレイピングしたデータを CSV ファイルに保存するには、まず pandas ライブラリをインポートします (スクレイピングスクリプトの最上部)。このライブラリにはデータを CSV 形式に変換するメソッドが含まれています:

import pandas as pd

次に、スクレイピングで収集したデータから pandas DataFrame オブジェクトを作成します:

df = pd.DataFrame(data)

最後に、DataFrame を CSV ファイルに変換して名前を付けます (例: quotes.csv):

df.to_csv('quotes.csv', index=False)

これらの変更後、スクリプトを実行します。すると、スクレイピングされたデータが CSV ファイルで保存されます。

この簡単な例では、引用を使ってできることはあまりありません。しかし、スクレイピングするデータによっては、さまざまな方法で分析して情報を引き出せます。

たとえば、pandas describe() 関数を使用して記述統計を調べることかできます。この関数は、平均、中央値、標準偏差などの数値データの概要をすばやく提供します。Matplotlib や seaborn を使用してデータを視覚化し、ヒストグラムや散布図、棒グラフを作成して、パターンや傾向を視覚的に認識することもできます。テキストデータの場合、単語の頻度分析や感情分析などの自然言語処理を使用して、レビューやコメントの共通のテーマや全体的な感情を把握できます。

より深い分析情報を得るには、データセット内のさまざまな変数間の相関関係を調べます。たとえば、本の評価とレビューの長さの関係を調べたり、ジャンルや著者によって評価がどのように異なるかを分析したりできます。pandas groupby() 関数を使ってデータを集計すれば、カテゴリー間のメトリクスを比較できます。

データのコンテキストと、答えを求めようとしている質問を考慮することを忘れないでください。たとえば、本の評価を分析する場合、高評価に最も寄与している要因を調べたり、人気のジャンルの傾向を時系列で特定したりします。調査結果は常に批判的なアプローチで評価し、データ収集プロセスにおける潜在的なバイアスを考慮します。

まとめ

このチュートリアルでは、Python でテキストスクレイプを行う方法、プロキシを使用するメリット、Bright Data のローテーションプロキシが IP ブロックを回避して匿名性を維持するのにどのように役立つかについて解説しました。

独自のスクレイピングソリューションの開発はやりがいがありますが、多くの場合、コードの管理、CAPTCHA の処理、ウェブサイトのポリシーの遵守などの課題が伴います。そこで、Bright Data のスクレイピング API が役立ちます。自動 CAPTCHA 解決、IP ローテーション、強力なデータ解析などの機能により、Bright Data はスクレイピングプロセスを簡素化し、インフラストラクチャの管理ではなくデータ分析に集中できるようにします。

Bright Data の無料トライアルに登録して、Bright Data がどのようにウェブスクレイピングプロジェクトを強化し、ビジネスニーズを満たす信頼性と拡張性に優れ効率的なデータ収集ソリューションを提供するか、ご覧ください。

クレジットカードは必要ありません