За последнее десятилетие JavaScript стал повсеместным в сети. Все большее число сайтов теперь полагаются на JavaScript для динамического отображения контента на стороне клиента, а не на стороне сервера. Это создает проблему для веб-скраперов. Традиционные инструменты, такие как Beautiful Soup и Scrapy, могут очищать только статический HTML-код, передаваемый с сервера. Чтобы парсить динамические сайты JavaScript, нам нужен безголовый браузер. Вот здесь-то и появляется Всплеск…
Распространение веб-приложений на JavaScript
Использование JavaScript с годами резко возросло:
- 97% веб-сайтов сейчас используют JavaScript на стороне клиента (W3Techs)
- 94% из 10,000 XNUMX лучших сайтов используют такие платформы JavaScript, как React, Angular и Vue (реагировать)
Этот сдвиг обусловлен популярностью веб-фреймворков, таких как React, Angular и Vue, во внешнем интерфейсе. Эти платформы динамически отображают контент в браузере с помощью JavaScript, а не полагаются исключительно на серверные шаблоны.
Для очистки этих современных веб-приложений JavaScript требуется браузер для выполнения JavaScript. Такие инструменты, как запросы и Beautiful Soup, здесь не справляются. Вместо этого нам нужен инструмент автоматизации браузера, такой как Selenium, Playwright или Splash.
Почему Splash меняет игру
Splash — это служба рендеринга JavaScript с HTTP API для управления браузером. Splash, разработанный Scrapinghub, прекрасно интегрируется со Scrapy, предоставляя возможности автоматизации браузера, необходимые для очистки динамических сайтов JavaScript.
Вот некоторые ключевые преимущества Splash:
Безголовый браузер – Splash использует веб-кит из браузера Chromium, но работает без видимого пользовательского интерфейса. Это делает его идеальным для парсинга на стороне сервера.
Быстрый и легкий – Splash потребляет гораздо меньше ресурсов процессора и памяти, чем Selenium или Puppeteer. Он создан для обеспечения высокой производительности в любом масштабе.
Сценарий – Скрипты Lua можно использовать для эмуляции сложных действий пользователя, таких как прокрутка, нажатие, отправка форм и т. д.
Scrapy-интеграция – Промежуточное программное обеспечение Splash упрощает использование Scrapy. Это похоже на использование обычных запросов Scrapy.
Распределенное сканирование – Splash прекрасно сочетается с кластеризацией Scrapy для распределенного сканирования. Горизонтальное масштабирование легко.
В целом, Splash предоставляет возможности динамического рендеринга, необходимые для сайтов JavaScript, в быстром, легком и поддерживающем сценарии пакете, который легко интегрируется со Scrapy для крупномасштабного распределенного парсинга.
Установка Всплеска
Splash доступен в виде образа Docker, что упрощает настройку. Его также можно установить на Linux и macOS без Docker.
Сначала убедитесь, что Docker установлен в вашей системе.
Затем извлеките образ Splash Docker из Docker Hub:
docker pull scrapinghub/splash
Загрузив образ, запустите экземпляр Splash на порту 8050:
docker run -p 8050:8050 scrapinghub/splash
Теперь Splash будет запущен в контейнере и готов к очистке! Splash REPL доступен для тестирования через порт 8050.
Интеграция Splash со Scrapy
Чтобы вызвать Splash из пауков Scrapy, мы воспользуемся командой scrapy-splash
библиотека, которая хорошо справляется с интеграцией.
Сначала установите Scrapy и Scrapy-splash:
pip install scrapy scrapy-splash
Затем включите промежуточное программное обеспечение Splash и фильтр dupefilter в settings.py
:
SPLASH_URL = ‘http://localhost:8050‘
DOWNLOADER_MIDDLEWARES = {
‘scrapy_splash.SplashCookiesMiddleware‘: 723,
‘scrapy_splash.SplashMiddleware‘: 725,
}
DUPEFILTER_CLASS = ‘scrapy_splash.SplashAwareDupeFilter‘
Это настраивает Scrapy для отправки запросов через локальный экземпляр Splash.
Парсинг с помощью SplashRequest
Чтобы отправлять запросы в Splash, а не сканировать напрямую, Scrapy предоставляет SplashRequest
класса.
from scrapy_splash import SplashRequest
def start_requests(self):
yield SplashRequest(
url="http://quotes.toscrape.com",
callback=self.parse,
args={‘wait‘: 0.5},
)
def parse(self, response):
# Extract quotes from response.body
Ассоциация args
параметр позволяет отправлять конфигурацию в Splash, например wait
времени.
Splash визуализирует JavaScript и возвращает результат HTML нашему parse
обратный вызов, где мы можем извлечь данные, как обычно, с помощью Scrapy Selectors.
Обработка пагинации на основе JavaScript
Одним из преимуществ использования Splash является то, что он может нажимать ссылки и кнопки, которые обрабатывает JavaScript. Например, давайте очистим сайт, используя бесконечную прокрутку страниц, как в Twitter.
Без Splash мы не смогли бы запустить загрузку дополнительных страниц. Но с помощью сценариев Splash Lua мы можем автоматизировать прокрутку для загрузки большего количества контента:
script = """
function main(splash)
splash:go(splash.args.url)
while not splash:select(‘.loading‘) do
splash:runjs(‘window.scrollBy(0, 1000)‘)
splash:wait(1)
end
return splash:html()
end
"""
def start_requests(self):
yield SplashRequest(
url="https://twitter.com/elonmusk",
callback=self.parse,
args={
‘lua_source‘: script
}
)
def parse(self, response):
# Extract Tweets
Этот скрипт постепенно прокручивается вниз до тех пор, пока не исчезнет индикатор загрузки, расширяя фрагмент страницы, доступный Scrapy.
Обработка reCAPTCHA с помощью Splash
Сайты с большим количеством JavaScript часто используют reCAPTCHA и другие меры защиты от ботов. Splash позволяет обойти эту защиту, автоматизируя браузер для решения проблем.
Например, мы можем создать скрипт для автоматического нажатия reCAPTCHA:
script = """
function main(splash)
-- Go to target url
splash:go(splash.args.url)
-- Wait for reCAPTCHA to load
splash:wait(5)
-- Click on reCAPTCHA checkbox
splash:runjs(‘document.getElementById("recaptcha-anchor").click()‘)
-- Wait for validation
splash:wait(10)
return splash:html()
end
"""
Таким образом, Splash может пройти первоначальный шлюз reCAPTCHA, который мешает другим парсерам. Конечно, защита от ботов постоянно развивается, поэтому парсеры необходимо постоянно поддерживать и обновлять, чтобы адаптироваться.
Советы по отладке
Вот несколько советов по отладке пауков Scrapy с помощью Splash:
- Проверьте информацию о запросе/ответе Splash в журналах Scrapy.
- Проверьте необработанный HTML-ответ на
http://localhost:8050/render.html
- Включить подробный вход в систему Splash
settings.py
- Используйте инструменты разработчика браузера для устранения проблем JS
- Замедлить темп с
args={‘wait‘: 3}
изолировать проблемы - Установить аргумент Splash
images=0
отключить загрузку ресурсов - Используйте
splash:go()
в сценариях Lua для перезапуска рендеринга - Перехват и обработка распространенных исключений SplashScriptError.
Тщательный мониторинг журналов при возникновении проблем помогает сузить круг проблем.
Лучшие практики для производства
Вот несколько советов по парсингу сайтов JavaScript в больших масштабах:
- Используйте надежную службу ротации прокси, например Oxylabs, чтобы избежать блокировки IP-адресов.
- Внедрите случайные задержки от 2 до 10 секунд для ваших пауков.
- Распределите рабочие файлы Scrapyd по множеству серверов.
- Оптимизируйте настройку Docker с помощью docker-compose для кластеров Splash.
- Включите кэширование и сохранение Scrapy для меньшего количества запросов Splash.
- Отслеживайте узкие места производительности и соответствующим образом масштабируйте ресурсы.
Избегайте взрывов как можно быстрее, чтобы оставаться вне поля зрения. Медленное, равномерное и распределенное сканирование снижает риск.
Парсинг сложных сайтов – пример GitHub
Давайте рассмотрим пример очистки профилей GitHub, который в значительной степени использует JavaScript для навигации и объединения данных из вызовов API.
Сначала несколько примеров страниц профиля:
Код паука:
import json
from scrapy_splash import SplashRequest
class GithubSpider(scrapy.Spider):
# Other spider code...
def start_requests(self):
profiles = [
‘https://github.com/scrapy/‘,
‘https://github.com/tensorflow‘
]
for url in profiles:
yield SplashRequest(url, self.parse, endpoint=‘render.html‘)
def parse(self, response):
# Extract profile info from HTML
yield {
‘name‘: response.css(‘name‘).get(),
‘bio‘: response.css(‘bio‘).get(),
# etc...
}
# Extract additional JSON data
json_data = json.loads(response.css(‘json-data‘).get())
yield {
‘public_repos‘: json_data[‘public_repos‘],
‘followers‘: json_data[‘followers‘]
}
Ключевые моменты:
- Используйте
render.html
а не по умолчаниюexecute
конечная точка для возврата HTML - GitHub встраивает данные JSON, которые мы можем анализировать напрямую.
- Сценарии Lua также могут помочь очистить дополнительные страницы.
Это демонстрирует, как Splash предоставляет возможности рендеринга для обработки даже сложных сайтов с большим количеством JavaScript.
Подводя итог
Веб-приложения с большим количеством JavaScript становятся нормой. Скребки, созданные на основе BeautifulSoup и Requests, больше не справляются с этой задачей. Splash устраняет этот пробел, предоставляя простой HTTP API для управления безголовым рендерингом браузера.
Splash, интегрированный со Scrapy, позволяет динамически очищать даже сложные веб-приложения JavaScript в любом масштабе. Такие функции, как создание сценариев, обеспечивают контроль, необходимый для эмуляции действий пользователя для разбивки на страницы и защиты от ботов.
Чтобы работать с веб-приложениями следующего поколения, каждому инструменту парсера необходим мощный инструмент рендеринга JavaScript, такой как Splash.