2025年のNODRIVERによるウェブスクレイピング

NODRIVERを使ってウェブスクレイピングをマスターしましょう。このガイドでは、セットアップ、主要機能、データ抽出ワークフローを合理化する方法について説明します。
4 分読
web scraping with NODRIVER blog image

何年もの間、Undetected Chromedriverは安全なブラウジングとアンチボットバイパスの定番であった。Undetected Chromedriverの開発者は、その後NODRIVERを開発しました。NODRIVERを使えば、SeleniumやWebdriverに依存する必要がなくなる。簡単なpipインストールで、全てが準備できるはずです。

このガイドでは、次のことを学ぶ:

  • NODRIVERとは?
  • 他のヘッドレス・ブラウザとの違いは?
  • NODRIVERの使い方
  • NODRIVERの限界とは?
  • NODRIVERをプロキシで使うには?
  • NODRIVERの堅実な代替品

NODRIVERとは何か?

NODRIVERとは何か?

NODRIVERはUndetected Chromedriverの完全な非同期後継である。すべてのkwargsのデフォルトとして「ベスト・プラクティス」を使用し、わずかなコード量ですぐに動作するように設計されている。

NODRIVERは次のような特徴を誇っている:

  • パフォーマンス
  • 外部依存なし(Chromedriverすらない)
  • アンチボット・バイパス
  • 永続的セッションクッキー
  • 使用するたびに新鮮なブラウザ・インスタンス

NODRIVERは何が違うのか?

NODRIVERは、Undetected Chromedriverや他のヘッドレスブラウザとも根本的に異なるアーキテクチャを使用している。従来、これらの他のブラウザはSeleniumやChrome DevTools Protocol(CDP)に依存していた。

NODRIVERはDevToolsプロトコルの独自の実装を使用している。ドキュメントでは、これは実際には「chrome(っぽい)自動化ライブラリ」と呼ばれています。NODRIVERでは、Seleniumに依存することもなく、CDPに直接依存することもありません。NODRIVERはCDPのカスタム実装を使用します。NODRIVERを使うために必要なのは、pipとクロームベースのブラウザだけです。

NODRIVERによるスクレイピング

1.はじめに

始める前に、Pythonとブラウザがインストールされていることを確認する必要がある。この記事を読んでいるということは、すでにこれらがインストールされているのだろう。NODRIVERはpipで直接インストールできます。

pip install nodriver

2.基本構造

私たちの基本的な構造は、PlaywrightやPuppeteerで得られるものと本当によく似ています。PythonでPlaywrightを使うことに興味があれば、Amazonのリストをスクレイピングするための完全なガイドをここで見ることができます。NODRIVERはPlaywrightとよく似ていますが、まだ開発中です。

これが基本的な構成だ。

import nodriver

async def main():
    #start the browser
    browser = await nodriver.start()

    base_url = "https://quotes.toscrape.com"

    #navigate to a page
    page = await browser.get(base_url)

    ###logic goes here###

    #close the browser
    await page.close()

if __name__ == '__main__':

    #in their docs, they advise directly against asyncio.run()
    nodriver.loop().run_until_complete(main())

3.ページの取得

上記の基本スケルトンでお気づきのように、browser.get()は ページ・オブジェクトを返す。複数のページを同時に開くこともできます。創意工夫を凝らせば、高度な並行操作も可能です。

以下のスニペットはあくまで理論的なものである。

#navigate to a page
page_1 = await browser.get(base_url)
page_2 = await browser.get(a_different_url)

####do stuff with the different pages#####

4.ダイナミックコンテンツ

動的コンテンツを処理するには、2つの選択肢があります。.sleep()メソッドで任意の時間を待つか、.wait_for()でページ上の特定のセレクタを待つかです。

#wait an arbitrary amount of time
await tab.sleep(1)

#wait for a specific element
await tab.wait_for("div[data-testid='some-value']")

注:上のスニペットでは、変数名としてpageの代わりにtabを使っている。これらは互換性がある。どちらもタブ・オブジェクトです。NODRIVERのタブについては、こちらで詳しく説明しています。

5.要素を見つける

NODRIVERはページ上の要素を見つけるための様々なメソッドを提供してくれる。いくつかのレガシーなメソッドを扱っている最中のようだ。

要素を見つけるためのテキストベースの方法は4種類ある。そのうちの2つは将来的になくなる可能性が高い。

#find an element using its text
my_element = page.find("some text here")

#find a list of elements by their text
my_elements = page.find_all("some text here")

#find an element using its text
my_element = page.find_element_by_text("some text here")

#find a list of elements using their text
my_elements = page.find_element_by_text("some text here")

上記のメソッドと同様に、要素を見つけるためのセレクタ・ベースのメソッドも4つある。そのうちの2つはおそらく消えるでしょう。NODRIVERの開発者がCDPと明確に連携したいのであれば、query_selectorメソッドはおそらく存続するでしょう。

#find a single element using its css selector
my_element = page.select("div[class='your-classname']")

#find a list of elements using a css selector
my_elements = page.select_all("div[class='your-classname']") 

#find a single element using its css selector
my_element = page.query_selector("div[class='your-classname']")

#find a list of elements using a css selector
my_elements = page.query_selector_all("div[class='your-classname']") 

上で見たように、ページ上の要素をどのように見つけたいとしても、その方法は複数ある可能性が高い。いずれ、NODRIVERの開発者たちはこの点を改善するかもしれない。とはいえ、現時点では、彼らの解析方法はスイス軍のチェーンソーのようなものだ。

6.データの抽出

NODRIVERはデータを抽出するためにいくつかの方法を提供しています。.attributes特質を使って属性を直接抽出することができますが、これはあまり使い勝手が良くありません – JSONオブジェクトではなく配列を返します。

リンク・オブジェクトからhrefを抽出するために私が作ったハッキーな回避策を紹介しよう。不格好だが、うまくいく。attributesメソッドは近いうちに、もう少し機能的なものに置き換えられると期待している。

next_button = await page.select("li[class='next'] > a")

#this returns an array
attributes = next_button.attributes

#use array indexing to find the href object and its value
for i in range(len(attributes)):
    if attributes[i] == "href":
        next_url = attributes[i+1]

注意: 他のほとんどのヘッドレス・ブラウザはget_attribute()メソッドを含んでいます。しかし、このメソッドはNODRIVERではまだ動作していません。

テキスト・データの抽出方法を説明しよう。お気づきかもしれないが、ここではawaitを使っていない。これは将来、他のCDPスタイルのブラウザに合わせるために変更されると思う。現在の形では、textは単なる属性であり、メソッドではありません。awaitを属性で使用すると、実際にはエラーが投げられます。これはPuppeteerとPlaywrightの両方に反するように感じますが、これがNODRIVERの現状です。

#find the quote element
quote_element = await quote.query_selector("span[class='text']")
#extract its text
quote_text = quote_element.text

7.データの保存

データを小さなJSONファイルに格納します。引用を抽出する場合、各引用にはタグのリストがあり、リストはCSV形式ではあまりうまくいきません。

import json

with open("quotes.json", "w", encoding="utf-8") as f:
    json.dump(scraped_data, f, ensure_ascii=False, indent=4)

8.すべてをまとめる

では、これらのコンセプトをすべてまとめて、実際に動くスクリプトにしてみましょう。以下の例では、QutoesからScrape(チュートリアルをスクレイピングするためだけに作られたサイト)にデータを抽出するために、上記のコンセプトを使用しています。以下のコードをコピー&ペーストして、NODRIVERが実際にどのように動作するかを感じてください。

import nodriver
import json

async def main():

    #list to hold scraped data
    scraped_data = []


    browser = await nodriver.start()

    next_url = "/"

    base_url = "https://quotes.toscrape.com"

    #while we still have urls to scrape
    while next_url:

        #go to the page
        page = await browser.get(f"{base_url}{next_url}")

        #find quote divs using a selector
        quotes = await page.select_all("div[class='quote']")

        #iterate through the quotes
        for quote in quotes:

            #find the quote element and extract its text
            quote_element = await quote.query_selector("span[class='text']")
            quote_text = quote_element.text

            #find the author and extract the text
            author_element = await quote.query_selector("small[class='author']")
            author = author_element.text

            #find the tag elements
            tag_elements = await quote.query_selector_all("a[class='tag']")
            tags = []

            #iterate through the tags and extract their text
            for tag_element in tag_elements:
                text = tag_element.text
                tags.append(text)

            #add our extracted data to the list of scraped data
            scraped_data.append({
                "quote": quote_text,
                "author": author,
                "tags": tags
            })

        #check the page for a "next" button
        next_button = await page.select("li[class='next'] > a")

        #if it doesn't exist, close the browser and break the loop
        if next_button == None:
            await page.close()
            next_url = None

        #if it does, follow this block instead
        else:
            attributes = next_button.attributes

            #loop through the attributes to find your desired attribute, its value is the next index
            for i in range(len(attributes)):
                if attributes[i] == "href":
                    next_url = attributes[i+1]

    #write the data to a json file
    with open("quotes.json", "w", encoding="utf-8") as f:
        json.dump(scraped_data, f, ensure_ascii=False, indent=4)


if __name__ == '__main__':

    nodriver.loop().run_until_complete(main())

上記のスクリプトを実行すると、以下のようなオブジェクトを含むJSONファイルが得られる。

[
    {
        "quote": "“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”",
        "author": "Albert Einstein",
        "tags": [
            "change",
            "deep-thoughts",
            "thinking",
            "world"
        ]
    },
    {
        "quote": "“It is our choices, Harry, that show what we truly are, far more than our abilities.”",
        "author": "J.K. Rowling",
        "tags": [
            "abilities",
            "choices"
        ]
    },

NODRIVERの現在の限界

現在、NODRIVERには特筆すべき重大な制限がある。それらについて説明しよう。

ヘッドレスモード

NODRIVERをヘッドレスモードで実行すると、必ずエラーがスローされる。これが意図的(アンチボットバイパスとして)なのか、それとも正当な問題なのかは分からない。

Pythonエラーのトレースバックがターミナルウィンドウに表示され、ファイルパスと行番号が参照されたNodriverスクリプトの最大再帰深度を超えたエラーが示されました。このエラーは、ヘッドレスセッションの準備とワンショットリクエストの送信に関する問題を示唆しています。

ページ相互作用

NODRIVERのドキュメントには数多くのページ・インタラクションが掲載されていますが、そのほとんどは部分的に動作するか、あるいは全く動作しません。ご覧のように、これは下のスクリーンショットのclick_mouse()mouse_click()の両方に記載されています。

非同期関数mouse_clickとclick_mouseのパラメータと使い方を示すコード・スニペット。ボタンの選択、修飾キー、待機用の内部イベントの詳細を含む。

属性抽出

NODRIVERの最大の問題点は属性抽出です。前述したように、これは配列を出力し、hrefの回避策で見たように非常に古臭いものです。これがattributeからのリテラル出力です。本番レベルのスクレイピングでは、これに対処する必要があります。

値'/page/2/'を持つハイパーリンク属性を配列形式で示すコードのスニペット。

NODRIVERによるプロキシの使用

現在のところ、NODRIVERのプロキシサポートはせいぜい限られています。NODRIVER はプロキシ接続のためのcreate_context()メソッドを提供しています。

以下のスニペットは、彼らの問題ページからのものだ。しかし、何時間たっても接続できない。

tab = await  browser.create_context("https://www.google.nl", proxy_server='socks5://myuser:mypass@somehost')

# or add  new_window=True if you would like a new window

彼らのドキュメントを見ると、プロキシに関するセクションがある[1]。公式なプロキシのセクションがあっても、実際のドキュメントはありません。これは近い将来修正されるものと思われる。

有効な代替案

現在のところ、本番用としてはまだ準備ができていないが、将来的にはNODRIVERの素晴らしい成果を期待している。もっとヘビーデューティーなものをお探しなら、以下のブラウザをご覧ください。

結論

NODRIVERはブラウザ自動化のためのエキサイティングな新ツールですが、急速に開発が進んでいるため、まだ成熟していない機能もあります。大規模で信頼性の高いウェブスクレイピングのためには、以下のような堅牢なソリューションの使用を検討してください:

無料トライアルに登録して、今すぐ始めましょう!