Парсинг с помощью Python. Урок 4
Учимся оптимизированно переходить по ссылкам и скрейпить сайт с помощью библиотеки Scrapy
Продолжаем изучать парсинг на 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 тысяч книг.
Это уже полноценный датасет, который можно анализировать. Например, посмотреть, какую книгу покупали чаще всего. Попробуйте выделить пятерку самых популярных книг, результат получится неожиданный.