Python, Selenium: парсинг веб-страниц за восемь шагов.
На этот раз я объясню (с полными примерами кода), как создать веб-парсер за восемь шагов с использованием фреймворка Selenium Python.
Возьму рецепт на сайте https://www.simplyrecipes.com/. Тема этого поста может быть базовой частью любого проекта Data Science: сбор данных.
Поэтому я выбрал этот веб-сайт, потому что он просто содержит данные, которые мне нужны для моего прилагательного НЛП. Кроме того, в этом руководстве на шагах 3, 5 и 7 будут рассмотрены некоторые конкретные проблемы (исключения селена), которые могут возникнуть во время сканирования Интернета. Поэтому вам не нужно будет переходить через Stackoverflow после реализации этого кода проекта.
Определения.
Парсинг веб-страниц часто называют сканированием веб-страниц или поиском в Интернете, или программным просмотром коллекции веб-страниц и извлечением данных. Это очень полезная практика для любого специалиста по данным.
С помощью веб-скребка вы можете извлекать данные о наборе продуктов, получать большой корпус текста для задач НЛП, получать любые количественные данные для анализа электронной коммерции или собирать большой набор изображений для целей компьютерного зрения. Вы даже можете получать данные с сайта без официального API.
Selenium — переносимый фреймворк для тестирования веб-приложений. Он автоматизирует веб-браузеры, и вы можете использовать его для выполнения действий в среде браузера от вашего имени. Selenium также поставляется с парсером и может отправлять веб-запросы. Вы можете извлекать данные из HTML-документа, как вы это делаете с Javascript DOM API. Как обычно, люди используют Selenium, если им требуются данные, которые могут быть доступны только при загрузке файлов Javascript.
Шаг 1. Установка.
Документация Selenium предлагает устанавливать пакеты несколькими способами. В моем случае я применяю установку в виртуальных средах в Pycharm для ОС Ubuntu.
Используя pip, вы можете установить селен следующим образом:

После того, как вы установили Selenium, необходимо обязательно скачать драйвер.
Selenium требует наличия драйвера для взаимодействия с выбранным браузером. Firefox, например, требует geckodriver. Он должен быть в вашем PATH, e. g., поместите его в / usr / bin или / usr / local / bin.
В этом руководстве я использую драйвер Chrome версии 87 для ОС Ubuntu.
Теперь мы установили пакет и распаковали драйвер в каталог проекта. Мы можем перейти на веб-страницу, где находятся данные. Давайте изучим структуру HTML.
Шаг 2. Изучите HTML-структуру страницы.
Здесь 131 страница с 26 ссылками на статьи с рецептами на каждой странице. Мы можем собрать более трех тысяч URL. Мы должны помнить, что последняя страница не заполнена. Некоторые ссылки не ведут к информации о рецепте. Как справиться с этой ситуацией? Вы увидите это на шаге 6. «Сохраните все собранные URL-адреса в файл».
Итак, что у нас есть в HTML-коде этой страницы для сбора ссылок на рецепты?
Если с одним местом будет проблематично — есть еще попробовать. А как насчет навигации по страницам?
С помощью кнопки «Далее» я автоматически меняю страницу через Python.
Шаг 3. Установите соединение с веб-страницей.
Вот первый пример кода. Это функция для создания хромированного драйвера для подключения к веб-странице. Я сделал его функциональным представлением для более удобного использования.
Очень важно выставлять таймаут после каждого запроса на соединение. В противном случае вы будете заблокированы, и ваша программа не будет работать.
Это может произойти, если у вас плохое интернет-соединение, или на странице много всплывающих окон, или если страница очень загружена и тяжелая. В этих ситуациях вы можете поймать selenium.TimeoutException. Чтобы этого избежать, я использовал вторичную попытку подключения. Программа останавливается только в том случае, если вторая попытка не удалась.
Шаг 4. Соберите ссылки рецептов с одной страницы.
После того, как я создал хромодрайвер, пришло время получить ссылки со страницы. Есть несколько популярных способов сделать это с помощью Selenium:
- используя XPath
- с помощью селектора CSS
- по классу, идентификатору, тегу
- частичный поиск с помощью CSS или XPath
- и Т. Д.
Я сделал эту часть с помощью поиска XPath:
Эта функция получает в качестве параметра объект драйвера Chrome. Первый шаг — создать пустой список для сбора ссылок (строка 7). Второй — войти в диапазон подсчета ссылок (мы помним, что этот веб-сайт содержит 26 ссылок на страницу) (строка 9) и выполнить итерацию по объекту HTML, используя этот индекс в XPath (строки 11–12). В конце добавьте каждую ссылку в список (строка 15), который был создан в начале этой функции.
Есть три простых шага, как получить XPath элемента на веб-странице:
1. Перейдите на веб-страницу и нажмите F12 на клавиатуре.
2. Найдите нужный элемент и щелкните его правой кнопкой мыши в HTML.
3. Скопируйте XPath и вставьте его в свою функцию в качестве аргумента для поиска.
Это действительно несложно сделать в режиме разработчика (F12).
Шаг 5. Переходите от страницы к странице для сбора ссылок.
Теперь я могу создать функцию для глобального обхода постранично. Посмотрите на код ниже:
Опять же, сначала создайте пустой список (строка 11). Функция получает в качестве аргумента хромодрайвер и количество страниц, которые она должна пройти.
Затем мы переходим к циклу for в диапазоне номеров страниц (строка 12). Мы до сих пор помним, что на сайте 131 страница с URL рецептов.
В строках 15–16 вызывается предыдущая функция для получения ссылок с одной страницы и сохранения результата в списке.
В строках 19–22 найдите кнопку «Далее», которую нужно щелкнуть ». Если страниц больше нет, вы получите сообщение об этом (строки 24–26).
Функция возвращает список [список [str]]. Каждый внутренний список [str] представляет собой список URL-адресов каждой пройденной страницы (строка 27).
Шаг 6. Сохраните все собранные URL в файл.
На этом этапе у нас есть три константы:
- путь к файлу, в котором будут храниться URL-адреса с рецептами (строка 2)
- имя файла / путь к файлу, в котором будут храниться ссылки, полученные без рецептов (на каждой странице, которую мы проходим, есть ссылки со статьями и обзорами, а не с рецептами) (строка 3)
- шаблон строки (строка 4), чтобы различать эти две категории URL.
Функция получает имя файла в качестве параметра, в котором мы будем хранить правильные ссылки. На этот раз мы создаем хромированный драйвер (строка 14).
Вызов предыдущей функции, чтобы собрать URL-адрес и записать результат в переменную (строка 17).
В строках 20–29 мы перебираем элементы в результирующей переменной. Откройте два файла в режиме «записи» и проверьте по шаблону, в какой файл мы должны записать элемент. Записываем строку и закрываем оба файла.
Хорошо, это уже большая часть, которую мы сделали. Остается только собирать данные с веб-страниц.
Шаг 7. Соберите данные рецепта: ингредиенты и инструкции.
Найдите правильное место в HTML для ингредиентов рецепта и инструкций по рецепту. Используйте тот же способ, что и в шагах 2 и 4: применение поиска XPath плюс поиск по имени тега.
Теперь о функции ниже. В качестве параметра он получает ссылку на страницу рецепта. В строках 14–16 мы определяем словарь и два списка (для сбора наших данных).
В строке 23 вызываем поиск элемента HTML. Он должен вернуть нам объект селена. Используйте его, чтобы продолжить поиск по имени тега в строке 32.
Итак, что происходит между этими двумя шагами: в строках 25–30? ». Предсказывает выпадение программы из-за алертов (в нашем случае ReCaptcha).
Иногда предупреждения отображаются не сразу. Когда вы пытаетесь переключиться на него до того, как он станет видимым, вы получите NoAlertPresentException. Чтобы этого избежать, просто дождитесь появления предупреждения. Код в строках 25–30 обрабатывает этот сценарий.
Строка 34: Где-то в середине загрузки я внезапно понял, что не все ингредиенты собраны. Некоторые из них представлены в виде пустого списка. После проверки HTML я увидел еще один элемент div, в котором данные находятся на странице. Таким образом, строка 34 проверяет условие «пусто» для элемента div. Если search_by_tag_name возвращает пустой список, программа переходит к строкам 35–36. Он должен увидеть тот же идентификатор в предыдущем div и собрать данные. Затем переберите результат поиска и добавьте каждый элемент в список (строки 37–38).
Сбор инструкций отличается от сбора ингредиентов. В строках 41–42 мы ищем по XPath. В строке 43 я применяю поиск по имени тега. Здесь нет проверки на предупреждения (какое облегчение), как с ингредиентами. Однако в инструкции есть картинки с тем же именем тега, что и текстовые данные! Поэтому я проверяю «это текст абзаца, а не пустая строка» (строка 44) и только после добавления текста в список (строка 45). В противном случае программа переходит к следующему элементу абзаца (строки 48–49).
Когда все поисковые запросы и итерации завершены, chromedriver должен быть закрыт (строка 50). Теперь вы можете обновить наш словарь собранными данными (53–54) и отправить их в оператор возврата (строка 56).
Кроме того, я написал небольшую функцию печати, чтобы отслеживать, какие данные загружаются. Он дает вывод на консоль: количество ссылок на страницу в файле .txt, саму ссылку и собранные текстовые данные в формате JSON (об ингредиентах и инструкциях).
Шаг 8. Запускаем все в main.py
Последняя часть этого руководства — запустить все из одного места, сохранить собранные данные в файл CSV.
Я выбрал CSV, потому что Python позволяет нам построчно записывать в этот формат файла. Поэтому, если вы остановите скребок посередине и продолжите с того же места после перерыва, он сработает. Вы не потеряете данные.
Единственное, что вам нужно изменить, чтобы продолжить загрузку:
- Строки комментариев 17 и 18, потому что они собирают ссылки с веб-сайта. Они уже есть в файле .txt;
- И измените параметр запуска для np.arrange () в строке 23, чтобы перезапустить загрузку с того места, где вы остановились. Функция мониторинга предоставит вам информацию о последней ссылке, которую использовал ваш скребок.
main открывает файл .txt со всеми URL-адресами для чтения строк (строки 21–22). Затем собирает данные с выбранной страницы (строка 26), сохраняет собранные данные в списке и записывает их в виде строки в файл CSV.
Это все. Через несколько часов у вас будет отличный набор из 2565 строк на 2 столбца. Осталось только предварительно обработать данные: удалить дубликаты, разбить на абзацы, убрать знаки препинания, превратить в числовые последовательности и обучить модель.
Выводы.
Этого руководства должно быть достаточно, чтобы научиться создавать парсер с Selenium и собирать данные с любого веб-сайта. Чтобы применить этот код к другому веб-сайту, измените только константы для URL-шаблона и XPath во всех функциях сканирования.
Как через selenium пропарсить почту
Python, Selenium: парсинг веб-страниц за восемь шагов.
На этот раз я объясню (с полными примерами кода), как создать веб-парсер за восемь шагов с использованием фреймворка Selenium Python.
Возьму рецепт на сайте https://www.simplyrecipes.com/. Тема этого поста может быть базовой частью любого проекта Data Science: сбор данных.
Поэтому я выбрал этот веб-сайт, потому что он просто содержит данные, которые мне нужны для моего прилагательного НЛП. Кроме того, в этом руководстве на шагах 3, 5 и 7 будут рассмотрены некоторые конкретные проблемы (исключения селена), которые могут возникнуть во время сканирования Интернета. Поэтому вам не нужно будет переходить через Stackoverflow после реализации этого кода проекта.
Определения.
Парсинг веб-страниц часто называют сканированием веб-страниц или поиском в Интернете, или программным просмотром коллекции веб-страниц и извлечением данных. Это очень полезная практика для любого специалиста по данным.
С помощью веб-скребка вы можете извлекать данные о наборе продуктов, получать большой корпус текста для задач НЛП, получать любые количественные данные для анализа электронной коммерции или собирать большой набор изображений для целей компьютерного зрения. Вы даже можете получать данные с сайта без официального API.
Selenium — переносимый фреймворк для тестирования веб-приложений. Он автоматизирует веб-браузеры, и вы можете использовать его для выполнения действий в среде браузера от вашего имени. Selenium также поставляется с парсером и может отправлять веб-запросы. Вы можете извлекать данные из HTML-документа, как вы это делаете с Javascript DOM API. Как обычно, люди используют Selenium, если им требуются данные, которые могут быть доступны только при загрузке файлов Javascript.
Шаг 1. Установка.
Документация Selenium предлагает устанавливать пакеты несколькими способами. В моем случае я применяю установку в виртуальных средах в Pycharm для ОС Ubuntu.
Используя pip, вы можете установить селен следующим образом:
После того, как вы установили Selenium, необходимо обязательно скачать драйвер.
Selenium требует наличия драйвера для взаимодействия с выбранным браузером. Firefox, например, требует geckodriver. Он должен быть в вашем PATH, e. g., поместите его в / usr / bin или / usr / local / bin.
В этом руководстве я использую драйвер Chrome версии 87 для ОС Ubuntu.
Теперь мы установили пакет и распаковали драйвер в каталог проекта. Мы можем перейти на веб-страницу, где находятся данные. Давайте изучим структуру HTML.
Шаг 2. Изучите HTML-структуру страницы.
Здесь 131 страница с 26 ссылками на статьи с рецептами на каждой странице. Мы можем собрать более трех тысяч URL. Мы должны помнить, что последняя страница не заполнена. Некоторые ссылки не ведут к информации о рецепте. Как справиться с этой ситуацией? Вы увидите это на шаге 6. «Сохраните все собранные URL-адреса в файл».
Итак, что у нас есть в HTML-коде этой страницы для сбора ссылок на рецепты?
Если с одним местом будет проблематично — есть еще попробовать. А как насчет навигации по страницам?
С помощью кнопки «Далее» я автоматически меняю страницу через Python.
Шаг 3. Установите соединение с веб-страницей.
Вот первый пример кода. Это функция для создания хромированного драйвера для подключения к веб-странице. Я сделал его функциональным представлением для более удобного использования.
Очень важно выставлять таймаут после каждого запроса на соединение. В противном случае вы будете заблокированы, и ваша программа не будет работать.
Это может произойти, если у вас плохое интернет-соединение, или на странице много всплывающих окон, или если страница очень загружена и тяжелая. В этих ситуациях вы можете поймать selenium.TimeoutException. Чтобы этого избежать, я использовал вторичную попытку подключения. Программа останавливается только в том случае, если вторая попытка не удалась.
Шаг 4. Соберите ссылки рецептов с одной страницы.
После того, как я создал хромодрайвер, пришло время получить ссылки со страницы. Есть несколько популярных способов сделать это с помощью Selenium:
- используя XPath
- с помощью селектора CSS
- по классу, идентификатору, тегу
- частичный поиск с помощью CSS или XPath
- и Т. Д.
Я сделал эту часть с помощью поиска XPath:
Эта функция получает в качестве параметра объект драйвера Chrome. Первый шаг — создать пустой список для сбора ссылок (строка 7). Второй — войти в диапазон подсчета ссылок (мы помним, что этот веб-сайт содержит 26 ссылок на страницу) (строка 9) и выполнить итерацию по объекту HTML, используя этот индекс в XPath (строки 11–12). В конце добавьте каждую ссылку в список (строка 15), который был создан в начале этой функции.
Есть три простых шага, как получить XPath элемента на веб-странице:
1. Перейдите на веб-страницу и нажмите F12 на клавиатуре.
2. Найдите нужный элемент и щелкните его правой кнопкой мыши в HTML.
3. Скопируйте XPath и вставьте его в свою функцию в качестве аргумента для поиска.
Это действительно несложно сделать в режиме разработчика (F12).
Шаг 5. Переходите от страницы к странице для сбора ссылок.
Теперь я могу создать функцию для глобального обхода постранично. Посмотрите на код ниже:
Опять же, сначала создайте пустой список (строка 11). Функция получает в качестве аргумента хромодрайвер и количество страниц, которые она должна пройти.
Затем мы переходим к циклу for в диапазоне номеров страниц (строка 12). Мы до сих пор помним, что на сайте 131 страница с URL рецептов.
В строках 15–16 вызывается предыдущая функция для получения ссылок с одной страницы и сохранения результата в списке.
В строках 19–22 найдите кнопку «Далее», которую нужно щелкнуть ». Если страниц больше нет, вы получите сообщение об этом (строки 24–26).
Функция возвращает список [список [str]]. Каждый внутренний список [str] представляет собой список URL-адресов каждой пройденной страницы (строка 27).
Шаг 6. Сохраните все собранные URL в файл.
На этом этапе у нас есть три константы:
- путь к файлу, в котором будут храниться URL-адреса с рецептами (строка 2)
- имя файла / путь к файлу, в котором будут храниться ссылки, полученные без рецептов (на каждой странице, которую мы проходим, есть ссылки со статьями и обзорами, а не с рецептами) (строка 3)
- шаблон строки (строка 4), чтобы различать эти две категории URL.
Функция получает имя файла в качестве параметра, в котором мы будем хранить правильные ссылки. На этот раз мы создаем хромированный драйвер (строка 14).
Вызов предыдущей функции, чтобы собрать URL-адрес и записать результат в переменную (строка 17).
В строках 20–29 мы перебираем элементы в результирующей переменной. Откройте два файла в режиме «записи» и проверьте по шаблону, в какой файл мы должны записать элемент. Записываем строку и закрываем оба файла.
Хорошо, это уже большая часть, которую мы сделали. Остается только собирать данные с веб-страниц.
Шаг 7. Соберите данные рецепта: ингредиенты и инструкции.
Найдите правильное место в HTML для ингредиентов рецепта и инструкций по рецепту. Используйте тот же способ, что и в шагах 2 и 4: применение поиска XPath плюс поиск по имени тега.
Теперь о функции ниже. В качестве параметра он получает ссылку на страницу рецепта. В строках 14–16 мы определяем словарь и два списка (для сбора наших данных).
В строке 23 вызываем поиск элемента HTML. Он должен вернуть нам объект селена. Используйте его, чтобы продолжить поиск по имени тега в строке 32.
Итак, что происходит между этими двумя шагами: в строках 25–30? ». Предсказывает выпадение программы из-за алертов (в нашем случае ReCaptcha).
Иногда предупреждения отображаются не сразу. Когда вы пытаетесь переключиться на него до того, как он станет видимым, вы получите NoAlertPresentException. Чтобы этого избежать, просто дождитесь появления предупреждения. Код в строках 25–30 обрабатывает этот сценарий.
Строка 34: Где-то в середине загрузки я внезапно понял, что не все ингредиенты собраны. Некоторые из них представлены в виде пустого списка. После проверки HTML я увидел еще один элемент div, в котором данные находятся на странице. Таким образом, строка 34 проверяет условие «пусто» для элемента div. Если search_by_tag_name возвращает пустой список, программа переходит к строкам 35–36. Он должен увидеть тот же идентификатор в предыдущем div и собрать данные. Затем переберите результат поиска и добавьте каждый элемент в список (строки 37–38).
Сбор инструкций отличается от сбора ингредиентов. В строках 41–42 мы ищем по XPath. В строке 43 я применяю поиск по имени тега. Здесь нет проверки на предупреждения (какое облегчение), как с ингредиентами. Однако в инструкции есть картинки с тем же именем тега, что и текстовые данные! Поэтому я проверяю «это текст абзаца, а не пустая строка» (строка 44) и только после добавления текста в список (строка 45). В противном случае программа переходит к следующему элементу абзаца (строки 48–49).
Когда все поисковые запросы и итерации завершены, chromedriver должен быть закрыт (строка 50). Теперь вы можете обновить наш словарь собранными данными (53–54) и отправить их в оператор возврата (строка 56).
Кроме того, я написал небольшую функцию печати, чтобы отслеживать, какие данные загружаются. Он дает вывод на консоль: количество ссылок на страницу в файле .txt, саму ссылку и собранные текстовые данные в формате JSON (об ингредиентах и инструкциях).
Шаг 8. Запускаем все в main.py
Последняя часть этого руководства — запустить все из одного места, сохранить собранные данные в файл CSV.
Я выбрал CSV, потому что Python позволяет нам построчно записывать в этот формат файла. Поэтому, если вы остановите скребок посередине и продолжите с того же места после перерыва, он сработает. Вы не потеряете данные.
Единственное, что вам нужно изменить, чтобы продолжить загрузку:
- Строки комментариев 17 и 18, потому что они собирают ссылки с веб-сайта. Они уже есть в файле .txt;
- И измените параметр запуска для np.arrange () в строке 23, чтобы перезапустить загрузку с того места, где вы остановились. Функция мониторинга предоставит вам информацию о последней ссылке, которую использовал ваш скребок.
main открывает файл .txt со всеми URL-адресами для чтения строк (строки 21–22). Затем собирает данные с выбранной страницы (строка 26), сохраняет собранные данные в списке и записывает их в виде строки в файл CSV.
Это все. Через несколько часов у вас будет отличный набор из 2565 строк на 2 столбца. Осталось только предварительно обработать данные: удалить дубликаты, разбить на абзацы, убрать знаки препинания, превратить в числовые последовательности и обучить модель.
Выводы.
Этого руководства должно быть достаточно, чтобы научиться создавать парсер с Selenium и собирать данные с любого веб-сайта. Чтобы применить этот код к другому веб-сайту, измените только константы для URL-шаблона и XPath во всех функциях сканирования.
Парсим любой сайт за считанные секунды. Как достать нужную информацию с сайта используя Selenium, XPath и Proxy Sever
Дарова, Хабр! Около года назад я решил заработать на ставках на спорт используя свои знания математики и программирования и тогда я наткнулся на небольшую проблему — как же достать нужную мне информацию с сайта? Как парсить веб-страницы? В этой статье я расскажу простыми словами каким тонкостям я научился.
Парсинг
Что ж такое парсинг? Это собирание и систематизирование информации, которая размещена на веб-сайтах с помощью специальных программ, автоматизирующих процесс.
Парсинг обычно используется для анализа ценовой политики и получения контента.
Начало
Чтобы забрать деньги у букмекеров мне надо было оперативно получать информацию про коэффициенты на определённые события с нескольких сайтов. В математическую часть вдаваться не будем.
Поскольку я изучал С# у себя в шараге, я решил всё писать на нём. Ребята со Stack Overflow посоветовали использовать Selenium WebDriver. Это драйвер браузера(программная библиотека), который позволяет разрабатывать программы, управляющие поведением браузера. То что надо, подумал я.
Установил библиотеку и побежал смотреть гайды в интернете. Спустя некоторое время я написал программу, которая могла открыть браузер и переходить по некоторым ссылкам.
Ура! Хотя стоп, а как на кнопки нажимать, как доставать нужную информацию? Тут нам поможет XPath.
XPath
Если простыми словами, то это язык запросов к элементам XML и XHTML документа.
В этой статье я буду использовать Google Chrome. Однако в других современных браузерах должен быть если не такой же, то очень похожий интерфейс.
Чтобы посмотреть код страницы, на которой вы находитесь надо нажать F12.
Чтобы посмотреть, в каком месте кода находиться элемент на странице(текст, картинка, кнопка) надо нажать на стрелочку в левом верхнем углу и выбрать данный элемент на странице. Теперь перейдем к синтаксису.
Стандартный синтаксис для написания XPath:
//: Выбирает все узлы в html документе начиная от текущего узла
Tagname: Тег текущего узла.
@: Выбирает атрибуты
Attribute: Имя атрибута узла.
Value: Значение атрибута.
Может быть поначалу непонятно, но после примеров всё должно встать на свои места.
Рассмотри несколько простых примеров:
Рассмотрим более сложные примеры для заданного html’я:
XPath = //div[@class= ‘contentBlock’]//div
Для этого XPath’а будут выбраны следующие элементы:
XPath = //div[@class= ‘contentBlock’]/div
Обратите внимание на разницу между /(выбирает от корневого узла) и //(выбирает узлы от текущего узла независимо от их местонахождения). Если непонятно, то посмотрите на примеры выше ещё раз.
Этот запрос равносилен этим при таком html’е:
parent:: — возвращает предка на один уровень выше.
Есть ещё супер крутая фича, такая как following-sibling:: — возвращает множество элементов на том же уровне, следующих за текущим, аналогично preceding-sibling:: — возвращает множество элементов на том же уровне, предшествующих текущему.
Думаю, теперь стало понятнее. Для закрепления материала советую зайти на этот сайт и написать несколько запросов, чтобы найти некоторые элементы этого html’я.
Теперь зная, что такое XPath, вернемся к написанию кода. Поскольку модераторам хабра не нравятся букмекерские конторы, то будет парсить цены на кофе в Walmart’е
Thread.Sleep’ы были написаны, чтобы веб-страничка успевала загрузиться.
Программа откроет сайт магазина Walmart, нажмёт пару кнопок, откроет отдел с кофе и получит название и цены на товары.
Если веб-страничка довольно таки большая и поэтому XPath’ы долго работают или их сложно написать, то надо воспользоваться каким-то другим методом.
HTTP запросы
Для начала рассмотрим как на сайте появляется контент.
Если простыми словами, то браузер делает запрос серверу с просьбой дать нужную информацию, а сервер в свою очередь, эту информацию предоставляет. Всё это осуществляется с помощью HTTP запросов.
Чтобы посмотреть на запросы, которые отправляет ваш браузер на конкретном сайте, то просто откройте этот сайт, нажмите F12 и перейдите во вкладку Network, после этого перезагрузите страницу.
Теперь осталось найти нужный нам запрос.
Как это делать? – рассмотрите все запросы с типом fetch(третья колонка на картинке выше) и смотрите на вкладку Preview.
Если она не пустая, то она должна быть в формате XML или JSON, если нет – продолжайте поиски. Если да, то посмотрите, есть ли здесь нужная вам информация. Чтобы это проверить советую использовать какой – то JSON Viewer или XML Viewer(загуглите и откройте первую ссылку, скопируйте текст с вкладки Response и вставьте в Viewer). Когда вы найдёте нужный вам запрос, то сохраните у себя где то его название(левая колонка) или хост URL’а(вкладка Headers), чтоб потом не искать. Например если на сайте walmart’а открыть отдел кофе, то будет отправляться запрос, юрл которого начинается с walmart.com/cp/api/wpa. Там будет вся информация про кофе в продаже.
Полпути пройдено, теперь этот запрос можно «подделывать» и отправлять сразу через программу, получая нужную информацию за считанные секунды. Осталось распарсить JSON или XML, а это делается намного проще, чем писание XPath’ов. Но зачастую формирование таких запросов вещь довольно таки неприятная(смотри на URL на картинке выше) и если у вас даже всё получиться, то в некоторых случаях вы будете получать такой ответ.
Сейчас вы узнаете, как можно избежать проблем с подражанием запроса используя альтернативу — прокси сервера.
Прокси сервер
Прокси сервер — устройство, являющееся посредником между компьютером и интернетом.
Было б замечательно, если б наша программа была прокси сервером, тогда можно быстро и удобно обрабатывать нужные респонсы с сервера. Тогда была б такая цепочка Браузер – Программа – Интернет(сервер сайта, который парсим).
Благо для си шарпа есть замечательная библиотека для таких нужд – Titanium Web Proxy.
Создадим класс PServer
Теперь пройдемся по каждому методу отдельно.
proxyServer.BeforeRepsone += OnRespone – добавляем метод обработки ответа с сервера. Он будет вызываться автоматически, когда будет приходить респонс.
explicitEndPoint — Конфигурация прокси сервера,
ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl = true)
IPAddress и port, на котором работает прокси сервер.
decryptSsl – стоит ли расшифровывать SSL. Иначе говоря, если decrtyptSsl = true, то прокси сервер будет обрабатывать все запросы и ответы.
explicitEndPoint.BeforeTunnelConnectRequest += OnBeforeTunnelConnectRequest — добавляем метод обработки запроса перед его отправкой на сервер. Он также будет вызываться автоматически перед отправкой запроса.
proxyServer.Start() — «запуск» прокси-сервера, с этого момента он начинает обрабатывать запросы и ответы.
e.DecryptSsl = false — текущий запрос и ответ на него не будут обрабатываться.
Если нас не интересует запрос или ответ на него(например картинка или какой – то скрипт), то зачем его расшифровывать? На это тратиться довольно много ресурсов, и если будут расшифровываться все запросы и ответы, то программа будет долго работать. Поэтому если текущий запрос не содержит хост интересующего нас запроса, то расшифровывать его нет смысла.
await e.GetResponseBodyAsString() – возвращает респонс в виде строки.
Чтобы WebDriver подключился к прокси серверу, то надо написать следующие:
Создаем многопоточный веб-парсер с Python и Selenium
В этом посте мы разберем вопрос, как ускорить выполнение скрипта Python при парсинге информации с веб-страниц, используя модуль concurrent.futures для реализации многопоточности его выполнения. Также мы разберем сам сценарий и покажем, как проверить корректность его работы с помощью pytest .
После прочтения этой статьи вы сможете:
- Собирать информацию и перемещаться по страницам веб-сайтов с помощью Selenium и Beautiful Soup
- Настроить pytest для проверки функциональности процедур вашего кода.
- Запускать парсер для параллельного выполнения запросов, используя модуль concurrent.futures .
- Настроить драйвер ChromeDriver Selenium для работы в headless режиме работы.
Настройка проекта
Сначала клонируем репозиторий с кодом нашего проекта. Для этого из командной строки выполним следующие команды:
Приведенные выше команды могут отличаться в зависимости от вашей среды разработки.
Установите ChromeDriver глобально. (Мы же используем версию 85.0.4183.87).
Разбор работы скрипта
Скрипт перебирает и собирает информацию с первых 20 страниц сайта Hacker News о последних статьях, используя Selenium для автоматизации взаимодействия с сайтом и Beautiful Soup для анализа HTML.
Начнем с основного файла. Вначале определим функцию run_process в которой будет запущен основной цикл работы нашего скрипта. Затем проверяем будет ли ChromeDriver работать в headless режиме, после чего определим и инициализируем несколько переменных, а также инициализируем сам драйвер с помощью метода get_driver() , импортированного из файла scrapers/scraper.py . В цикле while перебираем страницы сайта с которых будем собирать нужные нам данные. Для этого вызывается функция run_process() , которая непосредственно управляет соединением WebDriver и вызовами, соответствующих функций для сбора информации со страниц. Для этого в run_process() передается экземпляр WebDriver, а также номер страницы, которые передаются в функцию connect_to_base() , которая импортируется из файла scraper.py , код которого приводится ниже.
Функция connect_to_base() пытается подключиться к сайту Hacker News, а затем использует функционал явного ожидания explicit wait Selenium, чтобы убедиться, что элемент с загружен на страницу, прежде чем продолжить и получить из него нужные нам данные.
Просмотрите документацию Selenium для получения дополнительной информации о порядке использования explicit wait.
Чтобы подражать человеку-пользователю, вызывается метод sleep(2) который вносит задержку на 2 секунды после того, как драйвер подключился к Hacker News. После загрузки страницы и выполнения sleep(2) , драйвер захватывает HTML код страницы, который затем передается в функцию parse_html() .
В свою очередь функция parse_html() использует возможности библиотеки Beautiful Soup для синтаксического разбора HTML кода страницы для сбора нужной информации со страниц, которая будет помещена в список dicts . Эта функция также передает URL адрес статьи в функцию get_load_time() .
Эта функция загружает страницу статьи по переданному в нее URL адресу article_url , а также запоминает время ее загрузки.
Полученная информация о статье добавляется в CSV файл после передачи списка с полученными данными о статьях в функцию write_to_file .
После запуска скрипта нам потребовалось ожидать около 355 секунд (почти 6 минут) до окончания его работы:
Имейте в виду, что контент может быть не на всех 20 страницах, поэтому прошедшее время может отличаться в вашем случае. Когда этот скрипт запускался, контент присутствовал на 18 страницах (около 530 записей).
Это достаточно большое время. Ну что ж, сначала добавим к нашему коду возможность базового тестирования.
Тестируем наш код
С целью проверить функциональность нашего парсера без запуска браузера и, таким образом, не повторять GET запросы к сайту Hacker News, вы можете загрузить HTML код страницы и сохранить его в папку test/test.html , а затем парсить его локальную копию. Это поможет избежать блокировки вашего IP-адреса из-за слишком быстрого выполнения большого количества запросов, в ходе отладки и тестирования функций парсинга данных, этот подход также сэкономит ваше время, поскольку вам не нужно запускать браузер при каждом запуске скрипта.
Ниже представлен код файла тестов, который находится в папке test/test_scraper.py:
Убедимся, что все работает как надо:
Код выполнялся всего 20 секунд. Попробуем имитировать работу функции get_load_time() , исключив отправку GET запроса.
Настраиваем многопоточность
А теперь самое интересное! Внеся всего лишь несколько изменений в код нашего сценария, мы можем ускорить процесс его выполнения:
В модуле concurrent.futures класс ThreadPoolExecutor используется для создания пула потоков и асинхронного выполнения вызовов функций run_process() . Метод submit принимает функцию вместе с аргументами, передаваемыми при ее вызове, и возвращает результат выполнения функции run_process . Метод wait используется для блокировки запущенного асинхронно кода до завершения всех выполняемых им задач.
Стоит отметить, что вы можете легко использовать в вашем коде многопроцессорность, используя класс ProcessPoolExecutor , поскольку и ProcessPoolExecutor , и ThreadPoolExecutor реализуют для один и тот же интерфейс:
Почему в нашем примере мы используем многопоточность вместо многопроцессорности?
Скарпинг веб-страниц в большей степени связан с выполнением операций ввода-вывода I/O, поскольку получение по сети HTML кода (I/O) происходит медленнее, чем непосредственно его парсинг (ЦП). Чтобы узнать об этом больше, а также о разнице между параллелизмом parallelism (многопроцессорностью) и параллелизмом concurrency (многопоточностью), ознакомьтесь со статьей Speeding Up Python with Concurrency, Parallelism, and asyncio.
Запустим наш усовершенствованный парсер:
С кодом нашего парсера, работающего в многопоточном режиме, можно ознакомиться по ссылке.
Чтобы еще более ускорить процесс, мы можем запустить Chrome в headless режиме, передав в качестве аргумента в командной строке значение headless :
Заключение
Немного модифицировав исходный код нашего веб-скарпера мы смогли распараллелить его работу, что позволило сократить время выполнения сценария от 385 секунд до чуть более 35 секунд. В нашем случае этот подход позволил увеличить быстродействие скрипта на 90%, что является достаточно эффективным решением.
Python Практический. Скрапинг/Парсинг сайтов с Selenium и BS4
В этом уроке мы спарсим данные с сайта с помощью Selenium. Selenium нужен, чтобы автоматизировать браузер.
Таблица, которую мы будем извлекать находится здесь. Ее не получится извлечь простым способом, потому что url таблицы не меняется при переходе на другие страницы.
Решение
Импортируем нужные модули, функции и укажем рабочую папку.
Запускаем драйвер и открываем url.
Получаем ссылки для переключения страниц.
Получаем текст этих ссылок и записываем его в список.
Создаем пустой датафрейм, извлекаем таблицу с каждой страницы с помощью Selenium и записываем в созданный датафрейм.
Web scraping на Python при помощи Selenium: пособие для начинающих
Ранее мы показывали, как парсить поисковые результаты Google, используя две библиотеки Python: requests и BeautifulSoup. А в этом руководстве мы применим несколько иной подход для веб-скрейпинга – с использованием библиотеки Selenium для Python. Затем результаты мы сохраним в файл CSV с помощью пакета pandas.
Код из этого примера доступен на GitHub.
Почему Selenium
Selenium – это фреймворк, разработанный для автоматизации тестирования веб-приложений. Мы можем написать на Python скрипт, автоматически контролирующий взаимодействия с браузером, например переходы по ссылкам и отправку форм. Но кроме того, Selenium будет очень полезен, если нам нужно собрать данные со страницы, содержание которой генерирует JavaScript. В этом случае данные отображаются после множества Ajax-запросов.
Впрочем, во многих случаях и BeautifulSoup, и scrapy отлично справляются с извлечением информации. Выбор библиотеки будет зависеть от того, как именно загружаются данные на конкретной странице, которую требуется парсить.
Чтобы определится с выбором и лучше понять работу Selenium, можно ознакомиться еще с двумя обзорами: здесь и здесь.
Подготовка
Мы будем использовать две библиотеки Python — Selenium и pandas. Чтобы установить их, просто запустите pip install selenium pandas .
Кроме того, потребуется драйвер браузера, чтобы симулировать браузерную сессию. В этом руководстве мы воспользуемся драйвером Chrome.
Загрузка драйверов
Начнем
В нашем примере извлекать данные будем из этого цитатника, специально созданного для упражнений в веб-скрейпинге. Достанем из него все цитаты с их авторами и сохраним их в CSV файл.
Этот код импортирует драйвер Chrome и библиотеку pandas. А затем создает объект браузера при помощи driver = Chrome(webdriver) . Обратите внимание, переменная webdriver указывает на исполняемый файл драйвера, который мы ранее загрузили для выбранного браузера. Если вы предпочитаете Firefox, импорт будет выглядеть вот так:
from selenium.webdriver import Firefox
Основной скрипт
Изучив внимательнее URL сайта, обнаружим, что адрес для пагинации формируется следующим образом:
Вооруженные этим знанием, мы создадим переменную pages с количеством страниц, которое мы собираемся обработать. В данном примере мы в цикле извлечем сведения всего из 10 страниц.
Команда driver.get(url) посылает HTTP-запрос к соответствующей странице. Дальше нам важно знать точное число объектов, которые мы получим с каждой страницы.
Приведу простое определение:
«Web scraping – это процесс извлечения информации с веб-страницы, пользуясь повторяющимися паттернами в исходном коде страницы».
Веб-скрейпинг позволяет собрать неупорядоченные данные в интернете и сохранить их в структурированном формате.
Осмотрев элементы с цитатами, замечаем, что каждый из них заключен в тег div, принадлежащий классу с названием ”quote”. При помощи вызова driver.get_elements_by_class(«quote») мы получим все элементы, соответствующие этому шаблону.
Эту команду обернем в функцию len() и таким образом получим точное количество цитат на текущей странице. Сохраним его в переменную items, чтобы ограничить наш итератор.
Последний шаг
В извлечении данных нам помогут вышеупомянутые паттерны в разметке страницы. Получим снова список всех цитат через уже использованный метод, но в этот раз не будем передавать его в функцию len() , так как нам нужны отдельные элементы.
Затем построим внутренний цикл, который итерирует по цитатам и извлекает каждую запись. Как видим на картинке выше, сами цитаты расположены в теге span с именем класса “text”. А автор помечен тегом small и классом “author” .
Наконец, поместим переменные quote_text и author в кортеж, который добавим в список под названием total.
Теперь при помощи библиотеки pandas инициализируем dataframe для хранения всех записей (список total). Укажем названия для колонок: quote и author. И в завершение, экспортируем dataframe в CSV-файл, названный в нашем случае quoted.csv.
И не забудьте закрыть драйвер Chrome с помощью driver.close() .
Дополнительные источники
1. Поиск элементов
Как вы заметили, мы пользовались в этом примере методом find_elements_by_class . Но это не единственный способ поиска элементов. В этом руководстве автор подробно объясняет, ка использовать другие селекторы.
2. Видео
Если вы предпочитаете уроки в формате видео, вам могут пригодиться вот эти материалы Lucid Programming.
3. Лучшие практики применения Selenium в Python
Теперь, смеем надеяться, вы тоже умеете делать простые веб-скрейперы на Python с Selenium ?. Успехов!
Создаем многопоточный веб-парсер с Python и Selenium
В этом посте мы разберем вопрос, как ускорить выполнение скрипта Python при парсинге информации с веб-страниц, используя модуль concurrent.futures для реализации многопоточности его выполнения. Также мы разберем сам сценарий и покажем, как проверить корректность его работы с помощью pytest .
После прочтения этой статьи вы сможете:
- Собирать информацию и перемещаться по страницам веб-сайтов с помощью Selenium и Beautiful Soup
- Настроить pytest для проверки функциональности процедур вашего кода.
- Запускать парсер для параллельного выполнения запросов, используя модуль concurrent.futures .
- Настроить драйвер ChromeDriver Selenium для работы в headless режиме работы.
Настройка проекта
Сначала клонируем репозиторий с кодом нашего проекта. Для этого из командной строки выполним следующие команды:
Приведенные выше команды могут отличаться в зависимости от вашей среды разработки.
Установите ChromeDriver глобально. (Мы же используем версию 85.0.4183.87).
Разбор работы скрипта
Скрипт перебирает и собирает информацию с первых 20 страниц сайта Hacker News о последних статьях, используя Selenium для автоматизации взаимодействия с сайтом и Beautiful Soup для анализа HTML.
Начнем с основного файла. Вначале определим функцию run_process в которой будет запущен основной цикл работы нашего скрипта. Затем проверяем будет ли ChromeDriver работать в headless режиме, после чего определим и инициализируем несколько переменных, а также инициализируем сам драйвер с помощью метода get_driver() , импортированного из файла scrapers/scraper.py . В цикле while перебираем страницы сайта с которых будем собирать нужные нам данные. Для этого вызывается функция run_process() , которая непосредственно управляет соединением WebDriver и вызовами, соответствующих функций для сбора информации со страниц. Для этого в run_process() передается экземпляр WebDriver, а также номер страницы, которые передаются в функцию connect_to_base() , которая импортируется из файла scraper.py , код которого приводится ниже.
Функция connect_to_base() пытается подключиться к сайту Hacker News, а затем использует функционал явного ожидания explicit wait Selenium, чтобы убедиться, что элемент с id = ‘hnmain’ загружен на страницу, прежде чем продолжить и получить из него нужные нам данные.
Просмотрите документацию Selenium для получения дополнительной информации о порядке использования explicit wait.
Чтобы подражать человеку-пользователю, вызывается метод sleep(2) который вносит задержку на 2 секунды после того, как драйвер подключился к Hacker News. После загрузки страницы и выполнения sleep(2) , драйвер захватывает HTML код страницы, который затем передается в функцию parse_html() .
В свою очередь функция parse_html() использует возможности библиотеки Beautiful Soup для синтаксического разбора HTML кода страницы для сбора нужной информации со страниц, которая будет помещена в список dicts . Эта функция также передает URL адрес статьи в функцию get_load_time() .
Эта функция загружает страницу статьи по переданному в нее URL адресу article_url , а также запоминает время ее загрузки.
Полученная информация о статье добавляется в CSV файл после передачи списка с полученными данными о статьях в функцию write_to_file .
После запуска скрипта нам потребовалось ожидать около 355 секунд (почти 6 минут) до окончания его работы:
Имейте в виду, что контент может быть не на всех 20 страницах, поэтому прошедшее время может отличаться в вашем случае. Когда этот скрипт запускался, контент присутствовал на 18 страницах (около 530 записей).
Это достаточно большое время. Ну что ж, сначала добавим к нашему коду возможность базового тестирования.
Тестируем наш код
С целью проверить функциональность нашего парсера без запуска браузера и, таким образом, не повторять GET запросы к сайту Hacker News, вы можете загрузить HTML код страницы и сохранить его в папку test/test.html , а затем парсить его локальную копию. Это поможет избежать блокировки вашего IP-адреса из-за слишком быстрого выполнения большого количества запросов, в ходе отладки и тестирования функций парсинга данных, этот подход также сэкономит ваше время, поскольку вам не нужно запускать браузер при каждом запуске скрипта.
Ниже представлен код файла тестов, который находится в папке test/test_scraper.py:
Убедимся, что все работает как надо:
Код выполнялся всего 20 секунд. Попробуем имитировать работу функции get_load_time() , исключив отправку GET запроса.
Настраиваем многопоточность
А теперь самое интересное! Внеся всего лишь несколько изменений в код нашего сценария, мы можем ускорить процесс его выполнения:
В модуле concurrent.futures класс ThreadPoolExecutor используется для создания пула потоков и асинхронного выполнения вызовов функций run_process() . Метод submit принимает функцию вместе с аргументами, передаваемыми при ее вызове, и возвращает результат выполнения функции run_process . Метод wait используется для блокировки запущенного асинхронно кода до завершения всех выполняемых им задач.
Стоит отметить, что вы можете легко использовать в вашем коде многопроцессорность, используя класс ProcessPoolExecutor , поскольку и ProcessPoolExecutor , и ThreadPoolExecutor реализуют для один и тот же интерфейс:
Почему в нашем примере мы используем многопоточность вместо многопроцессорности?
Скарпинг веб-страниц в большей степени связан с выполнением операций ввода-вывода I/O, поскольку получение по сети HTML кода (I/O) происходит медленнее, чем непосредственно его парсинг (ЦП). Чтобы узнать об этом больше, а также о разнице между параллелизмом parallelism (многопроцессорностью) и параллелизмом concurrency (многопоточностью), ознакомьтесь со статьей Speeding Up Python with Concurrency, Parallelism, and asyncio.
Запустим наш усовершенствованный парсер:
С кодом нашего парсера, работающего в многопоточном режиме, можно ознакомиться по ссылке.
Чтобы еще более ускорить процесс, мы можем запустить Chrome в headless режиме, передав в качестве аргумента в командной строке значение headless :
Заключение
Немного модифицировав исходный код нашего веб-скарпера мы смогли распараллелить его работу, что позволило сократить время выполнения сценария от 385 секунд до чуть более 35 секунд. В нашем случае этот подход позволил увеличить быстродействие скрипта на 90%, что является достаточно эффективным решением.