Этим уроком мы начинаем курс, про который нас часто спрашивали — курс по парсингу данных с помощью Python. Мы уже рассказывали о парсинге без программирования, с помощью расширений для браузера, но их возможности ограничены и подходят не для всех сайтов. А вот Python поможет вам спарсить практически что угодно.

Съемка и монтаж: Глеб Лиманский

Чтобы правильно парсить, нужно понимать, как устроены сайты. Почти все они сверстаны с помощью языка HTML. Разберемся в его устройстве на примере сайта «КиноПоиск» и попробуем спарсить список из 250 лучших фильмов по версии пользователей сайта. 

Похожим на «КиноПоиск» образом устроены многие сайты, с которых журналистам бывает необходимо собрать данные, поэтому он хорошо подойдет для примера.

Для этого курса мы используем браузер Google Chrome, поэтому, чтобы вам было проще понимать наши действия и не путаться в названиях функций, советуем использовать его же. Но в других браузерах, Firefox или Opera, например, все эти функции тоже есть. 

Если вы кликнете на любое место сайта правой кнопкой мыши и нажмете на «Посмотреть код», то справа появится панель с кодом HTML (ее еще называют панелью разработчика).

Этот язык строится по тегам. Главный — html, в head обычно прописана метаинформация (заголовок, например), а нас сейчас интересует тег body, где лежит основная информация страницы. 

Чтобы посмотреть код конкретного элемента, нужно кликнуть именно на него и нажать «Посмотреть код» — тогда в панели разработчика подсветится нужный тег.

И наоборот, если двигаться по коду в панели справа, то слева, на самой странице, будет подсвечиваться сверстанный этим тегом элемент.

На страницах со списками одни и те же теги (название, оценка и так далее) повторяются от карточки к карточке. Поэтому нужно обнаружить и выписать эти конкретные теги. Но сначала вытащим весь код страницы, и здесь пригодится библиотека Requests — мы уже рассказывали о ней, советуем сначала прочитать тот урок и установить ее.

Открываем Jupyter, импортируем библиотеку с помощью import requests. Чтобы обратиться к сайту, создаем переменную url и присваиваем ей значение нужного сайта (у нас это ссылка на топ «КиноПоиска»).

Отправим на сервер запрос с помощью команды get, на вход которой мы передаем переменную url. С помощью команды text посмотрим, что получилось.

Как видите, код получился нечитаемым. Справиться с этим поможет другая библиотека, Beautifulsoup. Импортируем ее с помощью команды from bs4 import BeautifulSoup. Возможно, для этого понадобится сначала установить библиотеку через pip install

Мы должны передать библиотеке ответ, полученный с помощью Requests. Создадим переменную soup, передадим r.text и укажем необходимый формат (в нашем случае lxml).

Код упорядочен, теги идут один за одним — теперь мы можем вынимать из кода то, что нам нужно. Для этого вернемся в браузер, на страницу с данными. Как видно на скриншоте, подряд расположены несколько тегов div с классом desktop-rating-selection-file-item — это карточка каждого из фильмов в списке.

Сначала вытащим данные только из первой карточки, а затем перепишем код так, чтобы он сработал на все фильмы со страницы. Скопируем название класса — для этого два раза кликните на него мышкой и нажмите Ctrl+C (Cmd+C для macOS).

В Beautifulsoup есть команда find — укажем ей нужный тег (div) и через запятую класс (desktop-rating-selection-file-item). После class обязательно нужно поставить нижнее подчеркивание.

Отобразился уже не весь код, а только нужный элемент, в котором видно название фильма. Вернемся на сайт, чтобы углубиться в карточку и посмотреть, как называются нужные нам детали. Кликнем на название фильма «Зеленая миля» и снова выберем «Посмотреть код». 

Например, мы видим, что ссылка на фильм лежит в теге a и имеет собственный класс selection-film-item-meta__link. В теге a, в свою очередь, есть теги p с русским и оригинальным названиями фильмов. Эти три элемента мы и заберем.

Дополним нашу первую команду: ставим точку, снова пишем find, указываем тег a и его класс.

Чтобы сделать ссылку на фильм работающей, в начале нужно дописать к ней домен. Сама ссылка лежит внутри тега, поэтому find мы применить не сможем. Воспользуемся командой get и укажем нужный атрибут (в нашем случае href).

Допишем в начало команды домен «КиноПоиска» и запустим еще раз — так мы получим готовую ссылку. Положим этот код в переменную link

По аналогии достанем русскоязычное и оригинальное названия. Возьмем тот же код, допишем новый find, укажем нужные тег и класс. Чтобы очистить результат от ненужных элементов кода, добавим в конце команду text. Эти переменные назовем russian_name и original_name соответственно.

Еще мы можем достать из карточки страну производства и жанр — все будет работать точно так же, только класс будет не p, как у названий, а span. Страну положим в переменную country.

Однако в коде «КиноПоиска» страна и жанр имеют одинаковый класс selection-film-item-meta__meta-additional-item — а команда find находит только первое соответствие вашему запросу. Применим команду findAll, а все остальное оставим как раньше, чтобы получить список. 

Чтобы вытащить только жанр, добавим нумерацию в квадратных скобках (не забывайте, что в Python отсчет идет с 0, поэтому нам нужен номер 1) и команду text. Переменную назовем film_type.

Основную информацию о фильме мы достали, но можем получить и кое-что еще — например, оценку. Проверим, как называется класс этого элемента на странице «КиноПоиска» с помощью уже известного метода «Посмотреть код». Как видите, нам повезло — класс имеет уникальное название, глубоко копать не придется.

Скопируем ту часть изначальной команды find, где мы заходим в первый div карточки фильма и сразу укажем новый поиск по тегу span и классу rating__value rating__value_positive (и не забудем про text). Переменную назовем rate.

Мы уже сделали почти всю работу, но пока что наш код работает только для первой карточки фильма в списке. Исправим это. Создадим переменную films, зададим новый поиск по самому первому div, но уже с командой findAll. И заодно посмотрим длину списка с помощью команды len. Их будет 50, потому что список топ-250 разбит на 5 страниц. Далее мы научим код ходить и по другим страницам.

Напишем цикл для каждого film в списке films. Пока скопируем команду по поиску link и посмотрим, что там нужно поправить. Нам не нужен запрос к первому div, потому что до этого мы обратились к нему через findAll — заменим его на film. Повторим так с каждым элементом.

Создадим переменную data, в который будем добавлять все переменные, которые запрашиваем.

Запустим код и посмотрим, что отображается в data.

Все ровно так, как нам было нужно. Осталось только разобраться, как собирать данные и со следующих страниц. Если мы перейдем на вторую страницу списка, то увидим, что ссылка в адресной строке браузера изменилась — там появилось page=2

То есть, нам нужно передать этот параметр в цикл. Создадим перед циклом переменную url и воспользуемся методом f-строк, чтобы поместить внутрь фигурных скобок тот параметр, который нам нужен — номер страницы. Назовем параметр p и поместим его в цикл в промежутке от 1 до 6.

Переменную data при этом нужно вынести в начало кода, чтобы она не перезаписывалась с каждым новым циклом, а дополнялась. После этого скопируем код для запуска Requests и Beautifulsoup

Извините, на скрине ошибка: последнее значение в range должно быть 6, иначе скрипт пройдет только по четырем страницам

Код готов к работе. Но еще мы советуем добавить временные промежутки между запросами, чтобы не перегружать сервер, к которому обращаемся. Вернемся в область, где ранее импортировали Beautifulsoup, и импортируем из библиотеки time функцию sleep

В основной код допишем, чтобы между запросами проходило 3 секунды. Чтобы в реальном времени проверить работоспособность скрипта, добавим команду print.

Ошибка с позапрошлого скрина исправлена: последнее значение в range — 6, поэтому скрипт прошел по всем пяти страницам

Все должно сработать. Если сейчас снова проверить длину нашего списка командой len(data), там будет 250 — как и в топе «КиноПоиска». Теперь сохраним наши данные в таблицу. Снова вернемся в область импорта и добавим туда Pandas и используем его для записи в csv

Введем названия для колонок — пусть они дублируют названия переменных, которые мы присвоили ранее (не забудьте поместить их в 'кавычки'). И после этого экспортируем наши данные.

Код для импорта данных в таблицу csv
Итоговая таблица в csv

Последнее, что мы разберем на этом уроке — как научить код не ломаться и продолжать работу, если у фильма не будет указано какого-то из параметров. Пропишем, например, исключение для рейтинга фильма (rate) — если его нет, будет стоять прочерк.

«КиноПоиск» поддался нашему скрипту без проблем, но бывают сайты, запросы с которых не хотят отдавать нам необходимую информацию. Как с этим справиться — расскажем в следующих уроках.

Если у вас что-то не получилось — пишите в наш Telegram-чат, постараемся подсказать.