CoderCastrov logo
CoderCastrov
Парсер Python

Парсинг бестселлеров с использованием пакета `requests-html`

Парсинг бестселлеров с использованием пакета `requests-html`
просмотров
7 мин чтение
#Парсер Python

В моей предыдущей статье мы исследовали мир асинхронного веб-парсинга с использованием BeautifulSoup и httpx для ускорения процесса парсинга веб-страниц. Хотя эта комбинация работает хорошо, существуют и другие инструменты, которые также могут помочь достичь подобных целей. Один из таких инструментов - это пакет requests-html. В этой статье мы более подробно рассмотрим эту библиотеку и рассмотрим следующие вопросы:

Мы будем парсить бестселлеры с веб-сайта bookdepository.

Код для этой статьи доступен здесь

Для начала работы с requests-html давайте немного узнаем о пакете. requests-html - это пакет Python, который облегчает и интуитивно понятно разбор HTML. Он был создан Кеннетом Рейтцем, тем же человеком, который создал библиотеку requests. Он дополняет пакет requests и имеет следующие возможности:

  • Полная поддержка JavaScript!- CSS-селекторы (также известные как селекторы jQuery, благодаря PyQuery).- XPath-селекторы для тех, кто не боится.- Мокированный user-agent (как настоящий веб-браузер).- Автоматическое следование за перенаправлениями.- Пул подключений и сохранение куки.- Знакомый и любимый опыт работы с Requests, с волшебными возможностями разбора.- Поддержка асинхронности источник

Теперь давайте начнем с установки. Одной из крутых особенностей requests-html является то, что он имеет встроенную поддержку асинхронности. Это означает, что мы можем быстро парсить веб-сайты (асинхронно).


Начало работы

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

!pip install requests-html

Чтобы использовать пакет в Python, мы должны установить его, а затем импортировать.

from requests_html import AsyncHTMLSessionasession = AsyncHTMLSession()r = await asession.get("https://www.bookdepository.com/bestsellers")

В приведенном выше коде мы сначала импортировали класс AsyncHTMLSession из requests-html и создали экземпляр этого класса. Затем мы используем метод get сеанса для получения нашего веб-сайта. Теперь, если мы проверим код состояния, мы увидим, что он успешен (200).

r.status_code _# узнать, успешно ли выполнен запрос_

Получение заголовков

Теперь мы можем начать получать интересующие нас данные, начиная с заголовков книг. Чтобы узнать, какие CSS-выражения или xpath-выражения мы должны использовать для получения наших данных, мы можем изучить исходный код HTML с помощью инструментов разработчика в Chrome.

page source
# Получить все заголовки_страница = 1
заголовки = []
while страница != 35:
    url = f"[https://www.bookdepository.com/bestsellers?page={страница}](https://www.bookdepository.com/bestsellers?page=%7Bстраница%7D)"
    r = await asession.get(url)
    for x in r.html.find("h3.title"):
        заголовки.append(x.text)
    страница += 1

Обратите внимание, что мы сначала создали две переменные: страница, которая будет отслеживать страницы на веб-сайте, и заголовки, которая будет содержать наши данные. Затем мы использовали цикл while для обхода страниц и получения данных, пока наша страница меньше общего числа 35 (на веб-сайте есть 34 страницы). Я узнал это, используя инструменты разработчика Chrome, чтобы изучить пагинацию на странице.

Затем мы использовали метод find из requests-html для нахождения наших данных в HTML-контенте, передавая наш CSS-селектор (который является тегом h3 с классом title). После этого мы добавили результат в наш список заголовки. Теперь, если вы проверите длину нашего списка, вы увидите около 1020 заголовков.

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

len(заголовки) # проверить длину -> 1020
заголовки[:10] # взглянуть на первые 10
preview

Получение авторов

Далее мы получим авторов

_# получить всех авторов_page = 1
authors = []while page != 35:  
    url = f"[https://www.bookdepository.com/bestsellers?page={page](https://www.bookdepository.com/bestsellers?page=%7Bpage)}"
    r = await asession.get(url) 
    for x in r.html.find("p.author"):      
      authors.append(x.text)   
    page +=1

Затем мы проверим длину и часть данных, которые только что получили.

len(authors) -> 1020authors[:10] # проверить первые 10 строк

Получение цен

_# получить цены_page = 1
prices = []while page != 35:  
    url = f"[https://www.bookdepository.com/bestsellers?page={page](https://www.bookdepository.com/bestsellers?page=%7Bpage)}"
    r = await asession.get(url) 
    for x in r.html.find("span.sale-price"):      
      prices.append(x.text)   
    page +=1len(prices) # -> 1019prices[:10]

Очевидно, что нам нужно много очистки нашей переменной prices. Мы позаботимся об этом позже.


Получение рейтингов

_# получаем страницу с рейтингами = 1
рейтинги = []
while страница != 35:
    url = f"[https://www.bookdepository.com/bestsellers?page={страница}](https://www.bookdepository.com/bestsellers?page=%7Bстраница})"
    r = await asession.get(url)
    for x in r.html.find("div.stars"):
        результат = len(x.find("span.full-star"))
        рейтинги.append(результат)
    страница += 1

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

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

len(рейтинги) -> 956рейтинги[:10] # проверяем рейтинги

Получение форматов книг

_# получаем форматы книг_страница = 1
formats = []while page != 35:  
    url = f"[https://www.bookdepository.com/bestsellers?page={page](https://www.bookdepository.com/bestsellers?page=%7Bpage)}"
    r = await asession.get(url) 
    for x in r.html.find("p.format"):   
      formats.append(x.text)   
    page +=1

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

len(formats) -> 1020formats[:10] # проверяем 10 строк

Создание DataFrame

Мы успешно спарсили наши данные, но они еще не чистые. Нам нужно очистить и сохранить их для последующей анализа. Для этого нам понадобится использовать pandas. Мы создадим DataFrame наших данных.

# Поместим все в DataFrame
import pandas as pddf = pd.DataFrame(list(zip(titles, authors, prices, formats)),
         columns=["titles", "authors", "prices", "formats"])
df["rating"] = pd.Series(ratings) # чтобы добавить звезды
df.shape # -> (1020, 5)
df.head() # проверяем наши данные
df.tail() _# проверяем последние значения_

Вы заметите, что у нас есть некоторые пропущенные значения в наших данных. Мы должны разобраться с этим позже. Есть много способов работы с пропущенными значениями. Вы можете либо удалить их, либо обработать. Поскольку удаление их для нашего небольшого набора данных не является вариантом, мы будем обрабатывать их. Есть много способов сделать это. Мы можем заполнить значения средним или медианой переменной вопроса или заменить их новыми значениями (не числовыми).

Очистка данных

Теперь, когда мы создали нашу таблицу данных с помощью pandas, пришло время очистить наши данные.

# начнем с очистки переменной pricesdf.prices.str.replace("US", "") # удалить символы US

Метод str.replace() вызывается для этого столбца, чтобы заменить все вхождения строки "US" на пустую строку (""). Это эффективно удаляет символы "US" из всех значений в столбце prices.

# удалим знак доллара
df.prices = df.prices.str.replace("$", "")

Это строка кода используется в DataFrame Pandas для удаления знака доллара ('$') из столбца с именем 'prices'. Часть кода df.prices выбирает столбец 'prices' из DataFrame df. Атрибут .str позволяет выполнять операции со строками над каждым элементом столбца 'prices', рассматривая его как строку. Метод .replace() заменяет символ '$' пустой строкой, эффективно удаляя его из столбца 'prices'. Обновленный DataFrame затем сохраняется обратно в столбец 'prices' путем присвоения результата метода df.prices.str.replace() столбцу.

Теперь таблица данных будет выглядеть так:

df.head()

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

df.info()
# преобразуем цены в тип float
df["prices"] = df.prices.astype("float")
df.info()

Мы подошли к моменту, когда мы будем работать с отсутствующими значениями в столбце рейтинга. Давайте проверим, сколько у нас отсутствующих значений. Мы видим, что у нас около 272 строки с отсутствующими значениями. Поскольку наши данные небольшие, мы не можем их удалить, поэтому мы заполним их средним значением столбца цен. Если мы снова проверим столбец рейтинга, мы увидим, что теперь у нас нет отсутствующих значений в нем.

# заполним отсутствующие значения в рейтинге
df.rating.isna().sum() # -> 63 
import numpy as np
df["rating"] = df["rating"].fillna(round(np.mean(df.rating), 1))
df.rating.isna().sum() # проверим на отсутствующие значения -> 0

Если мы проверим нашу окончательную таблицу, мы увидим, что данные чистые и столбцы имеют правильные типы. Теперь мы можем экспортировать наши данные в csv-файл для дальнейшего анализа.

df.head()

Последний шаг - сохранить данные для дальнейшего анализа.

df.to_csv("book_depo_clean.csv") # сохранить для дальнейшего анализа

Заключение

В этой статье мы рассмотрели использование пакета requests-html для парсинга веб-страниц с поддержкой асинхронности. Мы использовали эту библиотеку для сбора данных о бестселлерах книг и использовали pandas для очистки извлеченной информации. Использование пакета requests-html делает парсинг веб-страниц интуитивно понятной и простой задачей. Рассмотрите возможность использования этой библиотеки для вашего следующего проекта по парсингу веб-страниц.