Pythonによるクラウドスクレイパー ステップバイステップガイド

PythonでCloudscraperを使用し、Cloudflareの保護を回避する方法、エラー処理、およびボット対策システムの代替ソリューションを探る方法を学びます。
4 分読
Cloudscraper in Python blog image

このチュートリアルでは、Cloudflareのボット検知を回避し、一般的なエラーを処理し、最も堅牢なボット対策に対する代替スクレイピングソリューションを探るために、Pythonライブラリ「cloudscraper」の使用方法を学びます。

Pythonでcloudscraperを使用する方法

このチュートリアルでは、Cloudflareで保護されたウェブサイトから、cloudscraperを使用する場合と使用しない場合の両方でデータをスクレイピングします。そのためには、BeautifulSoupとRequestsパッケージを使用します。これらのパッケージに慣れていない場合は、このPythonウェブスクレイピングガイドを参照して詳細を確認してください。

まず、以下のpipコマンドを実行して必要なパッケージをインストールします:

pip install tqdm==4.66.5 requests==2.32.3 beautifulsoup4==4.12.3

このチュートリアルをわかりやすくするため、ChannelsTVウェブサイトで特定の日に公開されたニュース記事からメタデータをウェブスクレイピングする以下のスクレイパーを作成しました:

import requests
from bs4 import BeautifulSoup
from datetime import datetime
from tqdm.auto import tqdm

def extract_article_data(article_source, headers):
    response = requests.get(article_source, headers=headers)
    if response.status_code != 200:
        return None

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

    title = soup.find(class_="post-title display-3").text.strip()

    date = soup.find(class_="post-meta_time").text.strip()
    date_object = datetime.strptime(date, 'Updated %B %d, %Y').date()

    categories = [category.text.strip() for category in soup.find('nav', {"aria-label": "breadcrumb"}).find_all('li')]

    tags = [tag.text.strip() for tag in soup.find("div", class_="tags").find_all("a")]

    article_data = {
        'date': date_object,
        'title': title,
        'link': article_source,
        'tags': tags,
        'categories': categories
    }

    return article_data

def process_page(articles, headers):
    page_data = []
    for article in tqdm(articles):
        url = article.find('a', href=True).get('href')
        if "https://" not in url:
            continue
        article_data = extract_article_data(url, headers)
        if article_data:
            page_data.append(article_data)
        return page_data

def scrape_articles_per_day(base_url, headers):
    day_data = []
    page = 1

    while True:
        for page in day_data:
            page = page_data
            return page_data

def process_page(articles, headers):
    page_data = []
    for article in tqdm(articles):
        url = article.find('a', href=True).get('href')
        if "https://" not in url:
            continue
        article_data = extract_article_data(url, headers)
        if article_data:
            page_data.append(article(article_data)
    return page_data

def scrape_articles_per_day(base_url, headers):
    day_data = []
    page = 1

    while True:
        page_url = f"{base_url}/page/{page}"
        response = requests.get(page_url, headers=headers)

        if not response or response.status_code != 200:
            break

        soup = BeautifulSoup(response.content, 'html.parser')
        articles = soup.find_all('article')

        if not articles:
            break
        page_data = process_page(articles, headers)
        day_data.extend(page_data)

        page += 1

    return day_data

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
}

URL = "https://www.channelstv.com/2024/08/01/"

scraped_articles = scrape_articles_per_day(URL, headers)
print(f"{len(scraped_articles)} 記事をスクレイピングしました。")
print("サンプル:")
print(scraped_articles[:2])

このコードでは、スクレイピング処理を容易にするために3つの関数が定義されています。最初の関数`extract_article_data`は、個々の記事からデータをロードし、公開日、タイトル、タグ、カテゴリなどのメタデータをPython辞書に抽出します。この辞書が返されます。データのロードと抽出は、RequestsとBeautiful SoupというPythonライブラリを使用して実装されています。

2つ目の関数process_pageは、特定ページ上の全記事リンクを取得し、extract_article_data関数を呼び出して各記事データを抽出します。抽出されたメタデータ辞書はリストに格納され、最終的に返されます。最後の関数scrape_articles_per_dayは whileループでページ番号をインクリメントし、存在しないページに到達するまで各ページの記事データをスクレイピングします。

次に、スクレイピング対象のURLを定義し、フィルタリング対象の日付を2024年8月1日と指定します。また、サンプルのユーザーエージェントを含むヘッダー変数を定義します。scrape_articles_per_day関数を呼び出し、URLと ヘッダー変数を渡します。その後、スクレイピングされた記事の数と最初の2件の結果を出力します。

理想的にはこのスクレイパーは機能するはずですが、ChannelsTVウェブサイトがCloudflareを利用しているため動作しません。これにより、extract_article_dataおよびscrape_articles_per_day関数で実装された直接リクエスト経由でのウェブページコンテンツへのアクセスが妨げられています。

このスクリプトを実行すると、出力は次のようになります:

0 articles were scraped.
Samples:
[]

cloudscraperの導入

以前、特定の記事メタデータをスクレイピングしようとした際、Cloudflareの保護により何も返されませんでした。このセクションでは、この問題を回避するためにcloudscraperをインストールして使用します。

まず、以下のpip コマンドを実行して cloudscraper ライブラリをインストールします:

pip install cloudscraper==1.2.71

次に、パッケージをインポートし、fetch_html_content関数を以下のように定義します:

import cloudscraper

def fetch_html_content(url, headers):
    try:
        scraper = cloudscraper.create_scraper()
        response = scraper.get(url, headers=headers)

        if response.status_code == 200:
            return response
        else:
            print(f"URL {url} の取得に失敗しました。ステータスコード: {response.status_code}")
            return None
    except Exception as e:
        print(f"URL {url} の取得中にエラーが発生しました。エラー: {str(e)}")
        return None

この関数は、スクレイピング対象のURLとリクエストヘッダーを入力パラメータとして受け取り、レスポンスオブジェクトまたはNoneを返します。関数内部ではtry-exceptブロックを定義します。tryブロック内で、cloudscraper.create_scraperメソッドを使用してスクレイパーを作成します。 次に、スクレイパー.getメソッドを呼び出し、urlとheaders変数を渡します。レスポンスのステータスコードが200の場合、レスポンスを返します。そうでない場合はエラーメッセージを出力し、Noneを返します。同様に、tryブロック内でエラーが発生した場合、exceptブロックがトリガーされ、適切なメッセージが出力されNoneが返されます。

その後、スクリプト内のすべてのrequests.get 呼び出しをこのfetch_html_content 関数に置き換えます。最初にextract_article_data 関数で次のように置換します:

def extract_article_data(article_source, headers):
    response = fetch_html_content(article_source, headers)

次に、scrape_articles_per_day 関数内のrequests.get呼び出しを以下のように置換します:

def scrape_articles_per_day(base_url, headers):
    day_data = []
    page = 1

    while True:
        page_url = f"{base_url}/page/{page}" 
        response = fetch_html_content(page_url, headers)

この関数を定義することで、cloudscraperライブラリがCloudflareの制限回避を支援します。

コードを実行すると、出力は次のようになります:

URL https://www.channelstv.com/2024/08/01//page/5 の取得に失敗しました。ステータスコード: 404
55 件の記事をスクレイピングしました。
サンプル:
[{'date': datetime.date(2024, 8, 1),
  'title': '#EndBadGovernance抗議活動が続く中、抵抗、催涙ガス、略奪、夜間外出禁止令が発生',
  'link': 'https://www.channelstv.com/2024/08/01/tear-gas-resilience-looting-curfew-as-endbadgovernance-protests-hold/',
  'tags': ['イーグル・スクエア', '飢餓', '略奪', 'MKOアビオラ公園', '暴力'],
  'categories': ['Headlines']},
 {'date': datetime.date(2024, 8, 1),
  'title': '囚人交換で解放されたロシア人芸術家の母、娘との『抱擁』を待つ',
  'link': 'https://www.channelstv.com/2024/08/01/mother-of-russian-artist-freed-in-prisoner-swap-waiting-to-hug-her/',
  'tags': ['Prisoner Swap', 'Russia'],
  'categories': ['World News']}]

クラウドスクレイパーの追加機能

ご覧の通り、CloudscraperはCloudflareのIUAM保護を回避するのに役立ちますが、Cloudscraperには他にも注目すべき機能があります。

プロキシの使用

プロキシは、お使いのコンピューターとターゲットサイト間の仲介サーバーとして機能し、インターネットを匿名で閲覧することを可能にします。リクエストはプロキシを経由して送信されるため、Cloudflareで保護されたサイトなどのターゲットウェブサイトは、トラフィックの送信元としてお使いのデバイスではなくプロキシサーバーを認識します。

Cloudscraperでは、プロキシを定義し、既に作成済みのCloudscraperオブジェクトに次のように渡すことができます:

スクレイパー = cloudscraper.create_scraper()

プロキシ = {
    'http': 'http://your-proxy-ip:port',
    'https': 'https://your-proxy-ip:port'
}

response = scraper.get(URL, proxies=proxy)

ここでは、デフォルト値を持つスクレイパーオブジェクトを定義します。次に、httphttps プロキシを含むプロキシ辞書を定義します。最後に、通常のrequest.get メソッドと同様に、このプロキシ辞書オブジェクトをscraper.get メソッドに渡します。

ユーザーエージェントとJavaScriptインタープリターの変更

前のスクリプトではユーザーエージェントを直接指定しましたが、cloudscraperライブラリはユーザーエージェントを自動生成することもできます。これにより、スクリプト作成時の手動設定が削減され、異なるブラウザIDを持つ実際のユーザーを模倣することが可能になります。 これはランダムに行われますが、cloudscraper.create_scraperメソッドにbrowserパラメータを渡すことで、サンプリング対象のユーザーエージェントの種類を選択することも可能です。このbrowserパラメータは、ブラウザとプラットフォームの文字列値、およびデスクトップとモバイルのブール値を格納する辞書を含みます。

cloudscraperではスクレイパーで使用するJavaScriptインタプリタとエンジンの指定も可能です。デフォルトはcloudscraperチームが作成したネイティブソルバーです。その他の利用可能なオプションはNode.js、Js2Py、ChakraCore、v8evalです。

以下は、インタプリタとブラウザの仕様を示すサンプルスニペットです:

スクレイパー = cloudscraper.create_scraper(
    interpreter="nodejs",
    browser={
        "browser": "chrome",
        "platform": "ios",
        "desktop": False,
    }
)

ここでは、インタープリタを「nodejs」に設定し、ブラウザパラメータに辞書を渡しています。この辞書内で、ブラウザはChromeに、プラットフォームは「ios」に設定されています。デスクトップパラメータはFalseに設定されており、モバイルとデスクトップの値がデフォルトでTrueに設定されているため、ブラウザがモバイル上で実行されることを意味します。この場合、CloudflareはChromeブラウザ上で動作するモバイルiOSユーザーエージェントを選択します。

CAPTCHAの処理

CAPTCHAは人間とボットを区別するために設計されており、ウェブスクレイピング時にターゲットウェブページの読み込みを妨げる場合があります。Cloudscraperの利点の一つは、reCAPTCHAや hCaptchaなどに対応したサードパーティ製CAPTCHAソルバーをサポートしていることです。 他のサードパーティ製CAPTCHAソルバーに関心がある場合は、GitHubサポートチケットを通じてCloudscraperチームに提案できます。

以下のスニペットは、CAPTCHA処理に対応するようスクレイパーを修正する方法を示しています:

スクレイパー = cloudscraper.create_scraper(
  captcha={
    'provider': 'capsolver',
    'api_key': 'your_capsolver_api_key'
  }
)

このコードでは、CAPTCHAプロバイダーとしてCapsolverを指定し、CapsolverのAPIキーを設定します。両方の値は辞書に格納され、cloudscraper.create_scraperメソッドのCAPTCHAパラメータに渡されます。

よくある Cloudscraper のエラー

Cloudflareの制限を回避する簡単な方法であるcloudscraperですが、使用開始時にいくつかのエラーが発生する可能性があります。以下に、遭遇する可能性のある最も一般的なエラー(および解決策)をいくつか示します。

モジュールが見つかりません

モジュールが見つからないエラーは、Python環境内に存在しないライブラリをインポートまたは使用しようとした際に発生するPythonの一般的なエラーです。

Pythonでは環境内で作業を行い、そのアクティブな環境にインストールされたライブラリのみがスクリプトやノートブックからアクセス可能です。モジュールが見つからないエラーは、関連する(仮想)環境をアクティブ化していないか、環境内にパッケージがインストールされていないことを示しています。

Windows で仮想環境をアクティブ化するには、次のコマンドを実行します:

<venv-name>Scriptsactivate.bat

LinuxまたはmacOSを使用している場合は、次のコマンドを使用できます:

source <venv-name>/bin/activate

パッケージが全くインストールされていない場合は、以下のコマンドを実行してインストールしてください:

pip install cloudscraper

cloudscraperが最新のCloudflareバージョンをバイパスできない

このエラーは、古いバージョンのCloudflareをバイパスするように設計されたcloudscraperバージョンを使用しようとした際に発生します。これは、Pythonライブラリが更新されるまで、新しいバージョンのCloudflareが古いバージョンのcloudscraperを制限する変更を加えている可能性があるため問題となります。

古いバージョンのcloudscraperを使用している場合は、次のコマンドでパッケージをアップグレードすることをお勧めします:

pip install -U cloudscraper

既に最新版のcloudscraperを使用している場合、更新を待つか、代替となる解決策を見つける必要があるかもしれません。

cloudscraperの代替手段

ここで学んだ内容を実装してもCloudflareの保護を回避できない場合は、Bright Dataの利用を検討してください。

Bright Dataは、データセンターISP プロキシモバイル プロキシレジデンシャルプロキシを含む最大規模のプロキシネットワークの一つを有しています。これらのプロキシを仲介として使用することで、IPブロックの回避、パフォーマンスの向上、地理的制限の回避、プライバシー保護が可能になります。

Bright DataでCloudflare保護を回避するには、アカウント作成・設定後、API認証情報を取得するだけです。その後、以下の例のようにターゲットURLのデータにアクセスできます:

import requests

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

username = 'brd-customer-<CUSTOMER_ID>-zone-<ZONE_NAME>'
password = '<ZONE_PASSWORD>'

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

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

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

ここでは、PythonRequestsライブラリを使用してGETリクエストを発行し、proxiesパラメータ経由でプロキシを指定します。作成されるプロキシは、Bright Dataのユーザー名、パスワード、ホスト、ポート番号を使用します。特にユーザー名は、Bright DataのカスタマーIDとゾーン名に基づいて定義されます。これらはすべてアカウントから取得可能です。

まとめ

このチュートリアルでは、Pythonのcloudscraperライブラリを使用してCloudflareで保護されたウェブサイトをスクレイピングする方法を学びました。 また、発生する可能性のある一般的なエラーとその回避方法についても学びました。cloudscraperはCloudflareのIUAMを回避する優れた解決策となり得ますが、他の無料技術と同様に制限があります。そのため、Cloudflare保護サイトにアクセスするための強力なBright DataプロキシネットワークとWeb Unlockerの使用方法も学習しました。

Bright Dataは、インターネット上のデータに制限なくアクセスできる自動化ツールを提供します。自動化が目的でない場合でも、大規模なプロキシネットワークを活用してリクエスト失敗数を削減できます。

ウェブスクレイピングを次のレベルへ引き上げる準備はできていますか?当社のプレミアムプロキシと専門的なウェブデータ収集サービスが、最も強固なボット対策さえも容易に回避する方法を発見してください。今すぐ無料トライアルを開始しましょう!