Веб-скрапинг в Python: ошибки, проблемы и методы решения
Процесс веб-скрапинга может столкнуться с различными трудностями, такими как блокировки, ошибки извлечения данных и снижение производительности. Чтобы преодолеть эти проблемы, разумно рассмотреть возможность использования языка программирования Python. В прошлом Python предоставлял инструменты для извлечения данных, среди которых библиотеки Requests, lxml и Beautiful Soup.
В этой статье мы проанализируем возможности веб-скрапинга на Python в 2024 году, обсудим возникающие трудности и ошибки, а также предложим современные решения для их устранения.
Ранее мы рассмотрели библиотеки для веб-скрапинга и сравнили их между собой. Вы можете изучить статью в нашем блоге.
Проблема 1: Ответ 200 от сервера
Такое сообщение встречается часто и клиенты / разработчики сталкиваются с такой ситуацией на протяжении нескольких лет. Рассмотрим на примере:
import httpx
url = 'https://www.wayfair.com/'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Connection": "keep-alive",
}
response = httpx.get(url, headers=headers)
print(response.content[:10])
Код работает для для requests, httpx, и aiohttp с чистой установкой и без расширений. В результате мы увидим:
b'\x83\x0c\x00\x00\xc4\r\x8e4\x82\x8a'
Возникает ощущение, что произошла ошибка, но это не так, сервер дал правильный ответ.
Разберемся по какой причине ответ выглядит закодированным. Ответ кроется в заголовке Accept-Encoding
.В приведенном выше примере мы просто скопировали его из своего браузера, поэтому он перечисляет все методы сжатия, поддерживаемые нашим браузером: "gzip, deflate, br, zstd". Бэкенд Wayfair поддерживает сжатие с помощью "br", то есть Brotli, и использует его как наиболее эффективный метод.
Это может произойти произойти, если ни одна из перечисленных выше библиотек не имеет зависимости Brotli
среди своих стандартных зависимостей. Однако все они поддерживают распаковку из этого формата, если у вас уже Brotl установлен Brotl
. Поэтому необходимо просто установить соответствующую библиотеку:
pip install Brotli
Вы получите следующий результат печати:
b'<!DOCTYPE '
Тот же результат можно получить для aiohttp
и, httpx
, выполнив установку с расширениями:
pip install aiohttp[speedups]
pip install httpx[brotli]
Вы также могли заметить, что zstd
некоторое время назад появился новый поддерживаемый формат сжатия данных. Ранее не встречалось никаких бэкендов, которые его используют, но но httpx
будут поддерживать распаковку в версиях выше 0.28.0. Для разработчиков может быть отличным решением использования для сжатия дампов ответов сервера в своих проектах, тк показывает эффективность а асинхронных решениях с aiofiles
.
Наиболее распространенное решение этой ситуации, заключается в том, чтобы разработчики просто перестали использовать заголовок Accept-Encoding
, получая таким образом несжатый ответ от сервера. Вот несколько ситуаций, с которыми вы столкнитесь, при отсутствии заголовка Accept-Encoding
:
- Увеличение нагрузки на пропускную способность интернет-канала;
- Увеличение стоимости канала при использовании прокси с графиком;
- Увеличение нагрузки на интернет-пропускную способность сервера;
Это лишь список некоторых проблем. Многие разработчики не понимают роль заголовков, которые используют в своих работах.
Проблема 2: Заголовок в браузере как инкогнито, но выходит ответ 403
В 2023 году мы получили большие языковые модели, как ChatGPT, и улучшенную защиту Cloudflare. Те, кто уже занимался скрапингом веб-данных, имели дело с DataDome, PerimeterX, InCapsula и им подобными.
Cloudflare – один из крупнейших провайдеров CDN в мире, обслуживающий огромное количество сайтов. Это внесло изменения и многие услуги доступны многим сайтам с низким порогом входа, что отличает его от упомянутых ранее технологий, направленные на защиту сайтов от парсинга.
Cloudflare – одна из причин, по которой не сможете выполнить задачу по веб-скрапинг с помощью requests
и beautifulsoup
, тк они просто не будут работать.
Давайте рассмотрим простой пример кода:
from httpx import Client
client = Client(http2=True)
url = 'https://www.g2.com/'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Connection": "keep-alive",
}
response = client.get(url, headers=headers)
print(response)
В итоге мы увидим ответ 403! И вот что произойдет, если мы используем curl
?
curl -XGET -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' -H 'Connection: keep-alive' 'https://www.g2.com/' -s -o /dev/null -w "%{http_code}\n"
И снова ответ 403! Попробуем разобраться в проблеме.
Поскольку Cloudflare использует отпечатки TLS многих HTTP-клиентов, популярных среди разработчиков, администраторы сайтов также могут настраивать агрессивность блокировки клиентов Cloudflare на основе этих отпечатков.
Для curl
, мы можем решить это так:
curl -XGET -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' -H 'Connection: keep-alive' 'https://www.g2.com/' --tlsv1.3 -s -o /dev/null -w "%{http_code}\n"
Можно предположить, что такое же решение можно применить и к httpx, но это не так. Ранее можно было изменить основные параметры httpcore
, которые он передает в h2
, отвечающее за взаимодействие HTTP2. На данный момент такое решение не работает.
Подходов существует множество и попробуем обойти, манипулируя TLS. Недостатком приема является использование библиотеки ssl
для работы с TLS в Python, что не дает возможности тонко манипулировать. Но сообщество Python реализует решения, существующие в других языках программирования.
Первый способ решения — использовать tls-client
Решать возникшую проблему будем с использованием tls-client
. Эта оболочка Python для библиотеки Golang предоставляет API, аналогичный requests
.
pip install tls-client
from tls_client import Session
client = Session(client_identifier="firefox_120")
url = 'https://www.g2.com/'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Connection": "keep-alive",
}
response = client.get(url, headers=headers)
print(response)
tls_client
поддерживает предварительные настройки TLS для популярных браузеров, актуальность которых поддерживается разработчиками. Чтобы использовать это, нужно передать необходимый client_identifier
. Однако библиотека также позволяет выполнять тонкие манипуляции с TLS вручную.
Второй способ решения — использовать curl_cffi
Эта оболочка вокруг библиотеки C исправляет curl
и предоставляет API, аналогичный requests
.
pip install curl_cffi
from curl_cffi import requests
url = 'https://www.g2.com/'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Connection": "keep-alive",
}
response = requests.get(url, headers=headers, impersonate="chrome124")
print(response)
curl_cffi
также предоставляет предварительные настройки TLS для некоторых браузеров, которые задаются с помощью параметра impersonate
. Он также предоставляет возможности для тонкой ручной настройки TLS.
Давайте проведем простое сравнение:
Особенность | tls_client | curl_cffi |
Предварительная настройка TLS | + | + |
Руководство по TLS | + | + |
Асинхронная поддержка | - | + |
Поддержка крупной компании | - | + |
Количество участников | - | + |
Очевидно curl_cff имеет большее преимущество и выигрывает. Но с учетом возникновения странных ошибок, можно сказать о недоработке этих двух подходов. Возможно, в будущем появятся библиотеки, способные решить эту проблему
Библиотека Scrapy тоже подходит для обхода TLS-отпечатков. Так что из коробки Scrapy
тоже будет заблокирован, но ничто не мешает вам использовать его curl_cffi
в вашем Scrapy Spider.
Проблема 3: headless-браузеры и Cloudflare Turnstile
В работе нам приходится сталкиваться с headless-браузерами и, скажем так, довольно часто. Чтобы быстро проверить, работает ли библиотека с браузером, следует начать с проверки обычного не-headless режима. Вам даже не нужно использовать автоматизацию; просто откройте сайт с помощью нужной библиотеки и действуйте вручную.
Какие библиотеки стоит посетить для этого?
Playwright + playwright-stealth
Он будет заблокирован и не позволит вам решить капчу.
Playwright — отличная библиотека для автоматизации браузера. Однако разработчики прямо заявляют, что не планируют разрабатывать ее как инструмент для веб-скрейпинга.
Ни один проект Python не решает эффективно эту проблему.
undetected_chromedriver
Он также будет заблокирован и не позволит вам решить капчу.
Это довольно распространенная библиотека для работы с headless-браузерами на Python, и в некоторых случаях она позволяет обойти Cloudflare Turnstile. Но на целевом сайте она блокируется. Также в своих проектах можно столкнулся как минимум с двумя другими случаями, когда Cloudflare блокировал undetected_chromedriver
.
В целом, undetected_chromedriver
— хорошая библиотека для ваших проектов, тем более, что она использует под капотом старый добрый Selenium.
botasaurus-driver
Позволяет обойти CAPTCHA после нажатия.
Главной особенностью является специальная разработка для веб-скрапинга. Он также имеет более высокоуровневую библиотеку для работы – botasaurus. С другой стороны, пока что он довольно сырой, у botasaurus-driver
нет документации и довольно сложный в работе API.
Подводя итог, скорее всего, вашей основной библиотекой для headless-браузинга будет undetected_chromedriver
. Но в некоторых особенно сложных случаях вам может потребоваться использовать botasaurus
.
Проблема 4: Фреймворки
Высокоуровневые фреймворки призваны ускорить и облегчить разработку, позволяя нам сосредоточиться на бизнес-логике, хотя за это мы часто платим ценой гибкости и контроля.
Итак, каковы же основы веб-скрапинга в 2024 году?
Scrapy
Невозможно говорить о фреймворках веб-скрапинга Python, не упомянув Scrapy. Scrapinghub впервые выпустил его в 2008 году. В течение 16 лет он разрабатывался как библиотека с открытым исходным кодом, на основе которой компании-разработчики создавали свои бизнес-решения.
Преимущества:
- Огромное количество руководств.
- Библиотеки промежуточного программного обеспечения написаны сообществом и расширяют их функциональность. Например, scrapy-playground.
Недостатки:
В последние годы Zyte уделяет больше внимания разработке собственной платформы. В Scrapy в основном используются только исправления..
- Недостаточно развиты системы, позволяющие обходить системы защиты от скрапинга, требуют самостоятельного внедрения.
- Изначально Scrapy разрабатывался с использованием асинхронного фреймворка Twisted. Частичная поддержка asyncio была добавлена только в версии 2.0. Просматривая исходный код, вы можете заметить некоторые обходные пути, которые были добавлены для этой цели.
Сравнив преимущества и недостатки Scrapy, приходишь к выводу – это хорошее и проверенное решение для сайтов, которые не защищены от веб-скрейпинга. Вам нужно будет разработать и добавить в фреймворк необходимые решения, чтобы обойти меры по предотвращению скрейпинга.
Botasaurus
Новый фреймворк для веб-скрейпинга с использованием автоматизации браузера, основанный на botasaurus-driver
. Первоначальный коммит был сделан 9 мая 2023 года.
Преимущества фреймворка:
- Позволяет обойти любую защиту Claudflare, а также многие другие, используя
botasaurus-driver
. - Хорошая документация для быстрого запуска
К недостаткам относятся:
- Только автоматизация браузера, не предназначена для HTTP-клиентов.
- Тесная связь с botasaurus-драйвером; вы не сможете легко заменить его на что-то лучшее, если оно появится в будущем.
- Никакой асинхронности, только многопоточность.
- На данный момент он довольно сырой и все еще требует исправлений для стабильной работы.
- На данный момент доступно очень мало обучающих материалов.
Это хороший фреймворк для быстрого создания веб-скрейпера на основе автоматизации браузера. Ему не хватает гибкости и поддержки HTTP-клиентов, что крайне важно для таких пользователей, как я.
crawlee for Python
Новый фреймворк для веб-скрейпинга в экосистеме Python. Первоначальный коммит был сделан 10 января 2024 года, а в СМИ он был опубликован 5 июля 2024 года.
Поскольку это совершенно новое решение на рынке, сейчас оно находится в стадии активного проектирования и разработки. Сообщество также активно участвует в его разработке. Итак, мы видим, что использование curl_cffi
уже обсуждается. Ранее обсуждалась возможность создания собственного клиента, основанного на доверии.
Хотелось бы ожидать, чтобы HTTP-клиент для Python разрабатывался и поддерживался крупной компанией. А Rust очень хорошо зарекомендовал себя как библиотечный язык для Python. Давайте вспомним хотя бы Ruff и Pedantic v2.
Преимущества:
- Фреймворк был разработан известной компанией на рынке веб-скрейпинга, которая обладает обширным опытом в этой области.
- Поддержка как автоматизации браузера, так и HTTP-клиентов.
- Полностью асинхронный, основанный на asyncio.
- Активная фаза разработки и медиа-активность. Поскольку разработчики прислушиваются к мнению сообщества, это очень важно на данном этапе.
Отдельно стоит отметить, что у него довольно хорошая модульная архитектура. Если разработчики введут возможность переключения между несколькими HTTP-клиентами, мы получим довольно гибкую структуру, которая позволяет нам легко изменять используемые технологии, при простой реализации от команды разработчиков.
Недоработки:
- Эта структура является новой. На данный момент доступно очень мало учебных материалов.
- На данный момент он довольно сырой и все еще требует исправлений для стабильной работы, а также удобных интерфейсов для настройки. Отсутствует реализация каких-либо средств обхода систем защиты от перехвата, кроме смены сессий и прокси-серверов.
В долгосрочной перспективе это может оказаться лучшим решением, чем Scrapy и Botasaurus. Оно уже предоставляет гибкие инструменты для работы с HTTP-клиентами, автоматизации браузеров из коробки и быстрого переключения между ними. Однако в нем отсутствуют инструменты для обхода защиты от скрейпинга, и их внедрение в будущем может стать решающим фактором при выборе фреймворка для вас.
Заключение
Индустрия меняется и ставит перед вами новые задачи, и если вы профессионально занимаетесь веб-очисткой, вам придется внимательно следить за ситуацией. В какой-нибудь другой области вы бы остались разработчиком, который создает продукты, используя устаревшие технологии. Но в современном веб-скрейпинге вы становитесь разработчиком, который создает веб-скрейперы, которые просто не работают.
Кроме того, не забывайте, что вы являетесь частью более широкого сообщества Python, и ваши знания могут пригодиться при разработке инструментов, которые будут полезны для всех нас. Как вы можете видеть, многие из необходимых вам инструментов создаются буквально прямо сейчас.