Продолжаем изучать парсинг на Python. В прошлом уроке мы познакомились с библиотекой Scrapy и попробовали с ее помощью собрать информацию о книгах из одного раздела онлайн-магазина. А сегодня мы соберем информацию обо всех книгах. Сделаем это с помощью специального класса — crawlspider. Он позволяет легко и быстро искать ссылки на странице и переходить по ним.

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

Откроем проект, с которым работали на прошлом уроке и создадим еще одного паука. Для этого переходим в папку spiders и создаем новый файл с расширением «py». Назовем его «all_books_spider.py».

Импорт библиотек

Как и в прошлый раз, мы начинаем наш проект с импорта библиотеки.

Пишем: import scrapy

Кроме этого, нам нужно импортировать библиотеки, которые будут собирать ссылки со страницы. 

Пишем:

from scrapy.spiders import CrawlSpider, Rule

from scrapy.linkextractors import LinkExtractor

Создаем класс

Как и в прошлом уроке, создаем класс, в который запишем правила скрейпинга. Только на этот раз в скобках вместо scrapy.Spider мы напишем CrawlSpider: class BookSpider(CrawlSpider).

Даем пауку имя: name = 'all_books'. Задаем начальную ссылку — это будет стартовая страница онлайн-магазина: start_urls = ['https://book24.ru/'].

Сбор ссылок

Теперь нужно написать правило сбора ссылок. 

Пишем: rules =. Cоздаем кортеж. Кортеж похож на список, отличие в том, что список — изменяемый тип данных, а кортеж нет. 

Чтобы понять, какое правило задать, откроем сайт. Мы видим несколько категорий с книгами. Можно переходить по ним поэтапно, собирая информацию о каждой книжке на каждой странице каждого раздела. И так пройтись по всем разделам. Ссылку для каждого перехода нам бы пришлось задавать с помощью css-селектора, как мы это делали в прошлый раз. Класс Crawl spider позволяет выполнить задачу проще и быстрее.

Давайте посмотрим на ссылку одного из разделов. Например, художественная литература. 

Скопируем ссылку и посмотрим на нее. Чтобы эта строка не влияла на код, закомментируем ее, для этого нужно написать в начало решетку: #https://book24.ru/catalog/fiction-1592/.

Мы видим, что после основного названия сайта (https://book24.ru) есть слово «catalog», а затем конкретный раздел каталога: fiction. Если мы зайдем в любой другой раздел, в ссылке тоже будет слово «catalog». Значит, нам нужно создать правило, чтобы паук проходился по всем ссылкам, в которых есть «catalog». Так мы соберем все разделы. 

Теперь зайдем на страницу с книгой. Скопируем ссылку и посмотрим на нее. #https://book24.ru/product/portret-doriana-greya-1597309/.

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

Запишем наше правило: Rule(LinkExtractor(allow='catalog')). Команда allow говорит, чтобы паук перешел по всем ссылкам, в которых есть слово «catalog». С помощью deny мы могли бы задать, по ссылкам с каким словом переходить не нужно. 

Вторым правилом зададим переход по страницам книг, то есть ссылкам со словом «product»: Rule(LinkExtractor(allow='product').

За то, что паук делает на странице, отвечает callback. Ему мы передадим функцию parse_items, в которой будет лежать информация, что именно нужно собрать со страницы: Rule(LinkExtractor(allow='product'), callback='parse_items')

Мы получили два правила. Первое говорит пауку, что нужно пройти по всем ссылкам со словом «catalog», после этого включается второе правило, которое говорит переходить по ссылкам со словом «product» и собирать нужную информацию. 

Функция

В прошлом уроке первую созданную функцию мы называли parse. Работая с классом CrawlSpider, нельзя использовать это название. Иначе код будет работать некорректно. Это важно помнить. Поэтому мы назвали нашу функцию parse_items. Как собирать информацию со страницы книги, мы подробно разбирали на прошлом уроке. 

Напишем ключевое слово yield и создадим словарь. Собирать необходимые нам элементы будем с помощью css-селекторов. 

Путь к названию и количеству покупок скопируем из предыдущего паука.

'name': response.css('h1.product-detail-page__title::text').get().strip(),

'buy': response.css('p.product-detail-page__purchased-text::text').get().split()[1], 

Чтобы полученный датасет был хорошо структурирован, соберем информацию обо всех разделах, которым принадлежит книга. Они как матрешка вкладываются друг в друга. Для примера перейдем в раздел «художественная литература» → «проза» → «историческая проза» и откроем страницу с книгой.

Scrapy Shell

Чтобы собрать информацию об этих разделах, перейдем в scrapy shell. Для этого наберем в терминале команду scrapy shell.

После этого передадим команде fetch страницу книги: fetch('https://book24.ru/product/odin-den-v-drevnem-mire-zapiski-puteshestvennika-vo-vremeni-6024803/').

Мы увидели ответ 200, значит, с сайтом можно работать. 

Посмотрим, где лежат названия разделов. Нажмем правой кнопкой мыши, выберем «просмотреть код». Мы видим, что название раздела лежит в теге span, который вложен в тег a. 

Обратимся к нему с помощью уже известной команды response.css. Если какой-то тег вложен в другой, мы можем дописать его через пробел. Мы дописываем тег span. Слово text поможет увидеть, что именно находится в css-селекторе, к которому мы обращаемся: response.css('a.breadcrumbs__link.smartLink span::text') 

Мы получили на выход список css-selector, в которых видим нужные слова: 

  • Главная
  • Художественная литература
  • Проза
  • Классическая зарубежная литература

Нам нужно достать каждый раздел по отдельности. Так как это список, обратимся по номеру элемента. Художественная литература на втором месте после раздела главная. Так как в программировании отсчет идет с нуля, обратимся к первому элементу.

Напишем: response.css('a.breadcrumbs__link.smartLink span::text')[1]. На выход мы получим нужный css-селектор. 

Чтобы выбрать содержание селектора, допишем get(): response.css('a.breadcrumbs__link.smartLink span::text')[1].get().

Пробелы в строке по бокам уберем с помощью известного метода strip(): response.css('a.breadcrumbs__link.smartLink span::text')[1].get().strip().

Мы получили нужный элемент, добавим его в словарь. Ключ назовем «first_type».

Чтобы получить следующий раздел, к которому относится книга, обратимся ко второму элементу списка: response.css('a.breadcrumbs__link.smartLink span::text')[2].get().strip()

Назовем ключ «second_type»: 'second_type': response.css('a.breadcrumbs__link.smartLink span::text')[2].get().strip(). И так же достанем третий последний раздел.

Мы указали все элементы, которые нам нужно достать. Выходим из ScrapyShell — control+D

Запись в файл

Нам даже не нужно прописывать переход по страницам, потому что паук сам соберет все ссылки на сайте, которые соответствуют нашему правилу. Сохраняем файл и можем запускать паука. Результат запишем в csv-файл all_books.csv. Напишем команду в терминале: scrapy crawl all_books -O all_books.csv

Паук начал собирать информацию обо всех книгах на сайте. Это займет около 10 минут. В папке с проектом мы видим новый документ. В нем есть информация о каждой из 16 тысяч книг. 

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