Всем привет! В данном уроке мы займемся разработкой парсера каталога товаров.
Код будем писать на языке программирования Python в среде разработки Python IDLE. В рамках данной статьи не рассматривается установка и настройка последних. Для получения подробной информации вы можете посетить официальный сайт Python.
Собирать данные о товарах будем со специального сервиса на нашем сайте – тестового каталога, где к каждому полю подписан CSS-селектор, по которому можно найти элемент на странице – это нам пригодится в процессе парсинга. Сохранять результат будем в файл формата JSON.
Библиотеки, используемые в данной статье:
1. requests – официальную страница.
2. beautifulsoup4 – ссылка на документацию.
3. json – является частью стандартной библиотеки python.
По любым возникающим в ходе урока вопросам оставляйте комментарии ниже.
Ссылка на полный текст исходного кода находится в конце статьи.
Шаг 1. Подготовка
Предварительно проанализируем наш каталог товаров: видим 10 страниц, 117 товаров. Каждая карточка содержит поля: название, цена, артикул, таблица характеристик, изображение и описание. Выберем для парсинга название, цену и характеристики.
Алгоритм на поверхности:
1. Пройтись по страницам с 1 по 10 и собрать ссылки на страницы с товарами.
2. Зайти на страницу каждого из товаров и спарсить интересующие нас данные.
3. Сохранить полученные данные в файл формата JSON.
Шаг 2. Установка зависимостей
Использовать мы будем две сторонние библиотеки:
1. requests – для загрузки HTML-кода страниц по URL.
2. beautifulsoup4 – для парсинга данных с HTML, поиска элементов на странице.
Установим зависимости командами:
pip install requests
pip install beautifulsoup4
Также мы будем использовать стандартную библиотеку json для записи результата в файл JSON формата.
Шаг 3. Программирование
Приступим к написанию кода – создадим основной скрипт main.py.
Объявим функцию crawl_products, которая будет принимать на вход количество страниц с карточками товаров, а на выходе возвращать список ссылок на товары с этих страниц.
Объявим вторую функцию parse_products, которая принимает список URL-адресов, парсит необходимую информацию по каждому товару и добавляет её в общий массив data, который и возвращает
Нам известно, что всего 10 страниц с карточками товаров, объявим глобальную переменную PAGES_COUNT – количество страниц.
# -*- coding: utf-8 -*-
PAGES_COUNT = 10
def crawl_products(pages_count):
urls = []
return urls
def parse_products(urls):
data = []
return data
def main():
urls = crawl_products(PAGES_COUNT)
data = parse_products(urls)
if __name__ == '__main__':
main()
Для парсинга страницы нам необходимо получить HTML-код страницы с помощью библиотеки requests и создать объект BeautifulSoup на основе этого кода.
Создадим метод get_soup, который принимает на вход URL-адрес страницы и возвращает объект BeautifulSoup, который мы будем использовать для поиска элементов на странице по CSS-селекторам. Если не удалось загрузить страницу по переданному URL, метод возвращает None.
def get_soup(url, **kwargs):
response = requests.get(url, **kwargs)
if response.status_code == 200:
soup = BeautifulSoup(response.text, features='html.parser')
else:
soup = None
return soup
Приступим к реализации функции crawl_products. Смотрим, как формируется URL-адрес конкретной страницы, на примере страницы с номером 2:
https://parsemachine.com/sandbox/catalog/?page=2
Видим GET-параметр page=2, который является номером страницы. Объявим переменную fmt, которая будет шаблоном URL-адреса страницы с товарами. Пройдемся по страницам с 1 по pages_count.
Cформируем URL-адрес страницы с номером page_n и получим для него объект BeautifulSoup, используя метод get_soup. Если soup страницы является None, то произошла ошибка при получении HTML-кода страницы, поэтому мы прерываем обработку с помощью оператора break.
Если soup получен, то нас интересует элемент «Название товара», который является ссылкой на товар. Копируем CSS-селектор .product-card .title и передаем его аргументов в метод soup.select(), который вернет множество найденных на странице элементов. Для каждого из них мы получим атрибут href, а затем сформируем URL и добавим его в общий массив. Ниже приведен полный текст данной функции.
def crawl_products(pages_count):
urls = []
fmt = 'https://parsemachine.com/sandbox/catalog/?page={page}'
for page_n in range(1, 1 + pages_count):
print('page: {}'.format(page_n))
page_url = fmt.format(page=page_n)
soup = get_soup(page_url)
if soup is None:
break
for tag in soup.select('.product-card .title'):
href = tag.attrs['href']
url = 'https://parsemachine.com{}'.format(href)
urls.append(url)
return urls
Переходим к реализации parse_products. Пройдемся по каждому товару и получим soup аналогично предыдущей функции.
Первое поле, которое мы парсим – «Название товара». Вызываем метод select_one у объекта soup, который аналогичен методу select за исключением, что возвращает один элемент, найденный на странице. В качестве аргумента передаем CSS-селектор #product_name. Получаем видимый текст и очищаем от пробельных символов с обеих сторон.
Аналогично получаем поле «Цена» по CSS-селектору #product_amount.
Переходим к парсингу характеристик товара. Найдем строки по CSS-селектору #characteristics tbody tr и для каждой из них получим значения ячеек, которые сохраним в переменную techs. Данные по товару мы спарсили, теперь сохраним их в переменую item и добавим в массив data. Ниже представлен полный код функции.
def parse_products(urls):
data = []
for url in urls:
print('product: {}'.format(url))
soup = get_soup(url)
if soup is None:
break
name = soup.select_one('#product_name').text.strip()
amount = soup.select_one('#product_amount').text.strip()
techs = {}
for row in soup.select('#characteristics tbody tr'):
cols = row.select('td')
cols = [c.text.strip() for c in cols]
techs[cols[0]] = cols[1]
item = {
'name': name,
'amount': amount,
'techs': techs,
}
data.append(item)
return data
Осталось добавить запись в файл и парсер будет готов!
Импортируем библиотеку json для записи данных в JSON-файл. Объявляем глобальную переменную OUT_FILENAME – имя файла для записи результата.
Переходим к сохранению. Вызовем метод json.dump() с передачей аргументов:
1. Сохраняемый объект – у нас это data.
2. Файл.
Также передадим ensure_ascii=False, чтобы символы кириллицы не экранировались при записи в файл. И зададим отступ в 1 символ indent=1.
Итог
Все, сохранение в файл готово, и разработка парсера завершена!
import json
...
OUT_FILENAME = 'out.json'
...
def main():
urls = crawl_products(PAGES_COUNT)
data = parse_products(urls)
with open(OUT_FILENAME, 'w') as f:
json.dump(data, f, ensure_ascii=False, indent=1)
Ссылка на полный текст исходного кода – catalog.py
Если у вас возникли какие-то вопросы, можете задать их в комментариях к этой статье или под видео на YouTube. Предлагайте темы следующих уроков.
Спасибо за внимание!