Веб-скрапинг с помощью NODRIVER в 2025 году

Освойте веб-скраппинг с помощью NODRIVER. В этом руководстве описаны настройки, ключевые функции и методы оптимизации рабочих процессов извлечения данных.
5 мин. чтения
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).
  • Обход антибота
  • Постоянные файлы cookie для сеансов
  • Свежий экземпляр браузера при каждом использовании

Чем отличается NODRIVER?

NODRIVER использует архитектуру, радикально отличающуюся от Undetected Chromedriver и даже других безголовых браузеров. Традиционно эти браузеры зависят от Selenium или протокола Chrome DevTools Protocol (CDP).

NODRIVER использует свою собственную реализацию протокола DevTools. В документации он фактически называется “хромированной (-иш) библиотекой автоматизации”. При использовании NODRIVER вы не зависите от Selenium и не зависите напрямую от CDP. NODRIVER использует собственную реализацию CDP. Чтобы использовать NODRIVER, вам нужен только pip и браузер на базе Chrome.

Скраппинг с помощью NODRIVER

1. Начало работы

Прежде чем приступить к работе, необходимо убедиться, что у вас установлены Python и браузер. Если вы читаете эту статью, я предполагаю, что они у вас уже есть. Вы можете установить NODRIVER напрямую с помощью pip.

pip install nodriver

2. Базовая структура

Наша базовая структура очень похожа на ту, что вы можете получить в Playwright или Puppeteer. Если вы заинтересованы в использовании Playwright в Python, вы можете посмотреть полное руководство по скраппингу объявлений 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. Динамический контент

Для работы с динамическим содержимым у вас есть два варианта. Вы можете использовать метод .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']")

ПРИМЕЧАНИЕ: В приведенном выше фрагменте в качестве имени переменной я использовал tab, а не page. Они взаимозаменяемы. Они оба являются объектами вкладок. Подробнее о вкладках в NODRIVER вы можете узнать здесь.

5. Поиск элементов

NODRIVER предоставляет нам множество методов для поиска элементов на странице. Похоже, они занимаются обработкой некоторых старых методов.

Существует четыре различных текстовых метода поиска элементов. Два из них, скорее всего, исчезнут в будущем.

#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")

Как и вышеперечисленные методы, существуют также четыре метода поиска элементов на основе селектора. Два из них, скорее всего, исчезнут. Если разработчики 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 из объекта ссылки. Это уродливо, но работает. Я ожидаю, что метод атрибутов скоро будет заменен на что-то более функциональное.

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. В текущей форме текст является просто атрибутом, а не методом – при использовании атрибутов 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 to 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 выдает ошибку всякий раз, когда мы запускаем его в режиме headless. Мы не уверены, является ли это намеренным (в качестве обхода антибота) или законной проблемой.

В окне терминала отображается отслеживание ошибки Python, указывающее на превышение максимальной глубины рекурсии в скрипте Nodriver, с указанием путей к файлам и номеров строк. Ошибка указывает на проблемы с подготовкой безголовой сессии и отправкой одноразового запроса.

Взаимодействие страниц

Хотя в документации NODRIVER перечислено множество взаимодействий со страницами, большинство из них либо работают частично, либо не работают вообще. Как вы можете видеть, это задокументировано на скриншоте ниже как для click_mouse(), так и для mouse_click().

Фрагмент кода, показывающий параметры и использование асинхронных функций mouse_click и click_mouse, включая подробности о выборе кнопки, клавишах-модификаторах и внутреннем событии для ожидания.

Извлечение атрибутов

Самая большая больная точка NODRIVER – это извлечение атрибутов. Как уже говорилось, он выводит массив, и это крайне архаично, как вы видели в нашем обходном пути href. Вот дословный вывод из атрибута. Для скраппинга на уровне производства это необходимо решить.

Фрагмент кода, показывающий атрибут гиперссылки со значением '/page/2/' в формате массива.

Использование прокси с помощью 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 больших успехов в будущем. Если вы ищете что-то более мощное, обратите внимание на браузеры, представленные ниже.

  • Селен: Работает с 2004 года. Selenium зависит от Chromedriver, но он проверен в бою и готов к производству. Узнайте больше о веб-скрептинге Selenium.
  • Playwright: Playwright похож на отполированную, готовую к работе версию того, что вы видели в этом руководстве по NODRIVER. Узнайте, как использовать Playwright для веб-скреппинга.

Заключение

NODRIVER – это новый интересный инструмент для автоматизации работы браузера, но быстрое развитие означает, что некоторые функции еще не созрели. Для крупномасштабного и надежного веб-скрапинга лучше использовать такие надежные решения, как:

  • Жилые прокси: Подключение реальных устройств для обхода гео-блоков.
  • Web Unlocker: Управляемые прокси со встроенным решателем CAPTCHA.
  • Браузер для скрапинга: Удаленная автоматизация браузера с поддержкой прокси и CAPTCHA. Идеальное решение для многоэтапных проектов по скрапбукингу.
  • Пользовательский скрепер: Выполняйте пользовательские задания по скраппингу без необходимости написания кода и извлечения данных с помощью экспертов.

Запишитесь на бесплатную пробную версию и начните работу уже сегодня!