Продолжаем изучать парсинг с Python. В прошлый раз мы использовали библиотеки Requests, Beautifulsoup и Selenium. Сегодня поговорим о библиотеке Scrapy. Она позволяет быстро собирать информацию с сайтов, работать с динамическими сайтами, когда информация подгружается автоматически, работать с api. На примере книжного магазина мы попробуем разобраться, как работает эта библиотека.
Работать со Scrapy удобнее в редакторе Visual Studio Code. Если вы еще не знаете, как работать в VStudio и устанавливать виртуальное окружение, посмотрите уроки, где мы показывал, как написать робота для анализа госконтрактов. В них мы подробно рассказывали, как установить редактор, разбирали базовые команды в терминале, учились устанавливать и запускать виртуальное окружение.
Этот урок мы начнем с момента, когда VStudio и виртуальное окружение установлено.
В редакторе мы открываем терминал. Через терминал создадим папку, где будет лежать наш проект, назовем его our_spider. Команда: mk dir our_spider.
Перейдем в эту папку: cd our_spider.
И запустим виртуальное окружение: pipenv shell
Теперь установим библиотеку: pip install scrapy
Чтобы было легче смотреть на команды, которые мы вводим, можно иногда очищать терминал. Для этого на Mac есть горячие клавиши: control+L.
Создадим наш первый проект, назовем его bestsellers. Для этого вводим команду: scrapy startproject bestsellers.
Перейдем в папку: cd bestsellers.
Откроем эту папку и в редакторе: open folder → our_spider.
Мы видим, что в папке с проектом появилось несколько папок и файлов. Это файлы конфигурации, настройки нашего скрейпера. Подробнее о том, что содержится в каждом файле, написано в официальной документации Scrapy. Но сейчас нас эти файлы не интересуют. Мы заходим в папку spiders. В ней будут лежать пауки — это файлы, в которых мы прописываем, что именно нужно собирать на сайте. Создадим первого паука. Для этого нужно нажать на значок папки рядом с названием основной папки — bestsellers. Назовем нашего паука book_spider.py. Не забываем указывать расширение .py для питона.
Посмотрим на сайт, с которым мы будем работать. Это онлайн-магазин Book24. Мы будем собирать информацию о книгах из раздела «бестселлеры». Для этого нужно зайти на страницу каждой книги, собрать нужную информацию, выйти и зайти на страницу уже следующей книги. И так пройти по всем книгам раздела.
Прежде чем начинать писать код, посмотрим, какие команды будут доставать нужные нам элементы. Для этого перейдем в scrapy shell. Scrapy shell — это оболочка, что-то вроде отдельной комнаты, в которой можно пробовать обращаться к сайту с помощью отдельных команд и смотреть, какие ответы возвращаются. При этом не нужно писать целый код.
Чтобы перейти в scrapy shell, набираем в терминале команду scrapy shell и нажимаем enter.
Теперь обратимся к сайту, с которым будем работать. Для этого воспользуемся командой fetch. В скобках и кавычках передадим ссылку на страницу книги. В ответе мы увидели число 200. Это значит, сайт работает и готов отдавать нам информацию.
Чтобы обратиться к сайту, воспользуемся командой response. Обращаться к элементам мы можем через css-элементу или xpath. Об этом мы уже рассказывали в прошлых уроках. Попробуем обратиться по css-элементу.
Название книги
Посмотрим, где лежит название нашей книги. Для этого нажмем правой кнопкой мыши на название и выберем «посмотреть код». Откроются элементы разработчика. Заголовок книги лежит в теге h1 c классом product-detail-page__title. Копируем его.
В терминале набираем response.css('h1.product-detail-page__title'). Нажимаем enter. Мы получили весь css-селектор.
В этой записи сложно что-то разобрать. Чтобы было легче понять, что мы достали, после названия класса напишем два двоеточия и слово text: response.css('h1.product-detail-page__title::text'). Чтобы каждый раз не переписывать команду в терминале с нуля, можно вызывать последнюю команду с помощью горячих клавиш: на Mac это стрелка вверх.
Мы получили строку, в которой есть нужный нам заголовок. Чтобы достать его, допишем в конец команды get, он выводит первый элемент из списка css-селекторов: response.css('h1.product-detail-page__title::text').get(). Мы получили нужный нам заголовок.
Чтобы избавиться от пробелов по бокам, воспользуемся уже известным нам методом strip: response.css('h1.product-detail-page__title::text').get().strip()
Эту команду можно скопировать в какой-нибудь текстовый документ. Она нам еще понадобится.
Количество покупок
Теперь попробуем собрать информацию о количестве покупок. Она находится в теге p с классом product-detail-page__purchased-text.
Возвращаемся в терминал. Опять пишем команду response.css, только теперь вставляем тег p и нужный класс, сразу допишем text и get: response.css('p.product-detail-page__purchased-text::text').get()
Мы получили строку: « Купили 939 раз ». Но мы хотим извлечь только число, для этого будем использовать уже известный нам метод split. Он разбивает строку по пробелам и возвращает список слов.
Нам нужен первый элемент в списке. Не забываем, что в программировании отсчет начинается с нуля: response.css('p.product-detail-page__purchased-text::text').get().split()[1]
Мы получили нужное нам число. Скопируйте или запомните эту команду, она нам понадобится.
Название раздела
Третий элемент, который мы будем собирать на странице книги — информация о разделе. Опять нажимаем на название раздела правой кнопкой мыши и выбираем «просмотреть код».
Мы видим, что название раздела находится в атрибуте title тега a, который вложен в тег div. Скопируем класс тега div и вернемся в терминал.
Набираем команду response. Так как заголовок был вложен в атрибут тега а, после названия класса напишем тег a и через два двоеточия слово attr и в скобках название атрибута: response.css('div.product-characteristic__value a::attr(title)').
Получили на выход список элементов. Если мы посмотрим на сайт, поймем, что это разные характеристики книги, просто они находятся в одном и том же теге.
Нужная нам характеристика лежит в третьем теге. Так как в программировании отсчет начинается с нуля, обратимся ко второму элементу списка: response.css('div.product-characteristic__value a::attr(title)')[2].get().
Мы получили название раздела, которое нам нужно. Скопируйте эту команду, она нам еще понадобится.
Теперь, когда мы понимаем, как доставать нужные элементы, можем написать нашего первого паука. Для начала импортируем scrapy: import scrapy.
В библиотеке scrapy каждый паук, который собирает информацию, — это класс. Нам нужно создать его.
Дадим пауку имя — book24. И начальную ссылку, с которой нужно начать поиск. В нашем случае это раздел бестселлеров.
Правила скрейпинга мы задаем, используя метод parse. Cтандартная запись для всех методов Scrapy: def parse(self, response):
Мы посмотрели как брать информацию со страницы книги, но сначала до этой страницы нужно добраться. А для этого нужно собрать ссылки на каждую книгу. Посмотрим, где они находятся. Для этого вернемся в терминал и передадим команде fetch ссылку на раздел бестселлеров.
Посмотрим, где лежит ссылка на страницу книжек. Ссылка — это атрибут href в теге а, который вложен в тег div.
Мы можем вызвать ее так же, как вызывали название раздела, только укажем правильное название класса, а вместо атрибута title напишем href: response.css('div.product-card__image-holder a::attr(href)').get().
Нам нужно, чтобы паук проходился по каждой ссылке и заходил в нее. Для этого напишем цикл. Скажем, что нужно проходиться по каждой ссылке из нашего списка. Здесь мы уже не дописываем get к команде response.css, потому что get выводит первый элемент из списка css-селекторов, а нам нужны ссылки на все книги.
Для перехода по ссылке воспользуемся этого методом follow. Пишем yield, это ключевое слово в Scrapy, которое позволяет выполнить разные действия. Затем пишем response.follow и в качестве атрибута передадим нашу ссылку. А еще напишем callback — правило, по которому паук поймет, что именно нужно сделать на странице, на которую он перейдет. Если не напишем callback, паук просто перейдет по ссылке и ничего не сделает. В callback передадим новый метод parse_book.
Метод parse.book мы пишем так же, как до этого писали метод parse. В нем мы зададим, что именно нужно собирать на странице каждой книги. Для этого опять напишем ключевое слово yield и создадим словарь. Ключами в нашем словаре будут слова: name, buy и type (название книги, количество покупок, название раздела). А в качестве значений передадим команды, которые мы вызывали в scrapy shell (помните, мы советовали их скопировать).
Нам осталось сделать добавить переход по страницам. Так как переходить на новую страницу нужно, когда собрана вся информация на первой странице, код с переходом добавим внизу метода parse. Переходить мы будем с помощью уже известного слова yield и команды response.follow. Только на этот раз переходить будем не по ссылке внутрь книги, а по сслыке на новую страницу.
Как получить ссылку на следующую страницу с помощью f-строк мы рассказывали в первом уроке по скрейпингу.
В callback прописываем, что нужно сделать после перехода: зайти внутрь каждой книги. Эту задачу мы прописывали в методе parse, поэтому пишем callback=self.parse
Теперь наш паук полностью готов, осталось его запустить. Для начала нужно выйти из scrapy shell. На Mac для этого есть горячие клавиши control+d. Если мы уверены, что хотим выйти — нажимаем y на клавиатуре. Не забываем сохранить нашего паука.
Набираем в терминале последнюю команду scrapy crawl, передаем название нашего парсера — book24.
Чтобы записать данные в файл, допишем: -O book.csv. Паук соберет все данные в файл с названием book и расширением csv.
После этой команды паук начал ходить по всем страницам и на каждой странице собирать информацию по каждой книжке. Мы видим, что в папке с нашим проектом появился файл book.сsv. Проверяем: если в файле лежим вся информация, которую мы хотели собрать, значит наш паук сработал правильно.
Если у вас что-то не получилось — пишите в наш Telegram-чат, постараемся подсказать.