В данной статье напишем парсер торговой площадки Wildberries на python, который основан на взаимодействии с API данного сайта.

Введение

В наше время большой популярностью пользуется торговая площадка Wildberries. Так как на Вайлдберриз можно найти большое количество скидок и распродаж, урвать товар по привлекательно низкой цене, а в случае чего, даже вернуть товар без вреда своему кошельку, главное не злоупотреблять этим =)

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

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

Код проекта на Github

Пример того, что мы получим

Для примера прикреплю скриншот файла excel, который был сформирован после выполнения нашего парсера (см. Рисунок 1.) Собранные нами данные: наименование, id, скидка, цена, цена со скидкой, бренд, количество отзывов, рейтинг товара, ссылка. Сбор производится по API Wildberries, поэтому сбор проходит очень быстро. Ограничение в 10 тысяч позиций (или 100 страниц выдачи) В будущем мы будем собирать более подробное описание товара и даже вытаскивать из API остатки товара со складов Вайлдбериз.

Рисунок 1. Пример результата сбора данных каталога «велосипеды»
Рисунок 1. Пример результата сбора данных каталога «велосипеды»

Последовательность написания скрипта парсера

Краткое содержание всех функций парсера Вилдберрис:

  • Получение списка всех каталогов wildberries. Функция get_catalogs_wb
  • Поиск пользовательской категории в списке каталогов. Функция search_category_in_catalog
  • Извлечение данных из json файла с информацией о товарах. Функция get_data_from_json
  • Постраничный сбор данных. Функция get_content
  • Сохранение данных в эксель файл при помощи библиотеки pandas. Функция save_excel
  • Основная функция выполняющая все выше перечисленные. Функция parser

Получаем список каталогов для парсера (get_catalogs_wb)

Все возможные каталоги площадки WildBerries мы получим через API. Для этого откроем «консоль разработчика (devtool)», выберем «сеть (network)» (пункт 1, рисунок 2), включим фильтр «XHR» (пункт 2, рисунок 2), очистим все что там загрузилось (пункт 3, рисунок 2). Далее нажмем на поиск (пункт 4, рисунок 2) и справа в нашем списке подгруженных файлов появится main-menu-ru-ru.json

Рисунок 2. Консоль разработчика (devtool)

Выберем на main-menu-ru-ru.json (пункт 1, рисунок 3) Посмотрим его содержимое, для этого нажмем «ответ (response)» (пункт 2, рисунок 3) и убедимся что это то, что мы искали, каталог (пункт 3, рисунок 3)!

Рисунок 3. Поиск файла с каталогами
Рисунок 3. Поиск файла с каталогами

Далее к полученной ссылке применим библиотеку requests (не забудем ее заранее установить pip install requests и импортировать). И ответ сохраним в виде json. Для наглядности рекомендую сохранить результат в .json файл, чтобы было легче понять, что откуда берется.

url = 'https://www.wildberries.ru/webapi/menu/main-menu-ru-ru.json'
headers = {'Accept': "*/*", 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
response = requests.get(url, headers=headers)
data = response.json()
with open('wb_catalogs_data.json', 'w', encoding='UTF-8') as file:
    json.dump(data, file, indent=2, ensure_ascii=False)
    print(f'Данные сохранены в wb_catalogs_data.json')

На Рисунке 4 Показана часть файла полученного json файла с сервера wildberries. Из него нас интересует поля url (ссылка на каталог), shard (название каталога), query (подразделы)

Рисунок 4. Фрагмент из файла полученного с сервера wildberries с содержанием всех каталогов
Рисунок 4. Фрагмент из файла полученного с сервера wildberries с содержанием всех каталогов

Создадим список data_list и заполним его всеми возможными подкаталогами. А именно из childs будем брать url, shard, query. Функция get_catalogs_wb будет возвращать этот список. Ниже код этой функции.

def get_catalogs_wb():
    url = 'https://www.wildberries.ru/webapi/menu/main-menu-ru-ru.json'
    headers = {'Accept': "*/*", 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
    response = requests.get(url, headers=headers)
    data = response.json()
    data_list = []
    for d in data:
        try:
            for child in d['childs']:
                try:
                    category_name = child['name']
                    category_url = child['url']
                    shard = child['shard']
                    query = child['query']
                    data_list.append({
                        'category_name': category_name,
                        'category_url': category_url,
                        'shard': shard,
                        'query': query})
                except:
                    continue
                try:
                    for sub_child in child['childs']:
                        category_name = sub_child['name']
                        category_url = sub_child['url']
                        shard = sub_child['shard']
                        query = sub_child['query']
                        data_list.append({
                            'category_name': category_name,
                            'category_url': category_url,
                            'shard': shard,
                            'query': query})
                except:
                    continue
        except:
            continue
    return data_list

Фукция на входе получает url каталога от пользователя и список всех возможных каталогов (data_list) от предыдущей функции.

def search_category_in_catalog(url, catalog_list):
    try:
        for catalog in catalog_list:
            if catalog['category_url'] == url.split('https://www.wildberries.ru')[-1]:
                print(f'найдено совпадение: {catalog["category_name"]}')
                name_category = catalog['category_name']
                shard = catalog['shard']
                query = catalog['query']
                return name_category, shard, query
            else:
                # print('нет совпадения')
                pass
    except:
        print('Данный раздел не найден!')

Если каталог пользователя найден, возвращает название категории (name_category), ссылку на нее (shard) и подразделы данного каталога (query), иначе вывод сообщения «категория не найдена»

Извлекаем из json данные (get_data_from_json)

Извлекаем данные с полученного json файла (пример карточки 1 товара на Рисунок 5). Например я взял: наименование, id, скидку (sale), цену (priceU), цену со скидкой (salePriceU), бренд (brand), отзывы (feedbacks), рейтинг (rating) и ссылку товара

Рисунок 5. Пример карточки одного товара из json.
Рисунок 5. Пример карточки одного товара из json.

Функция возвращает нам список data_list в который мы записали все вышеперечисленные поля каждого товара. Код функции целиком:

def get_data_from_json(json_file):
    data_list = []
    for data in json_file['data']['products']:
        try:
            price = int(data["priceU"] / 100)
        except:
            price = 0
        data_list.append({
            'Наименование': data['name'],
            'id': data['id'],
            'Скидка': data['sale'],
            'Цена': price,
            'Цена со скидкой': int(data["salePriceU"] / 100),
            'Бренд': data['brand'],
            'id бренда': int(data['brandId']),
            'feedbacks': data['feedbacks'],
            'rating': data['rating'],
            'Ссылка': f'https://www.wildberries.ru/catalog/{data["id"]}/detail.aspx?targetUrl=BP'
        })
    return data_list

Сбор контента со всех страниц выдачи (get_content)

Сразу скажу, wildberries выдает только первые 100 страниц выдачи, или 10 000 позиций, для решения данной проблемы я решил добавить фильтр по ценовому диапазону, который будет сокращать количество выдачи.

На входе функция будет получать shard (название каталога), query (подразделы) и для снижения количества выдачи ценовой диапазон от low_price до top_price.

Создаем цикл из 100 страниц и идем по нему до тех пор пока, количество выдачи не будет равно 0 или не закончатся страницы (100 штук)

def get_content(shard, query, low_price=None, top_price=None):
    headers = {'Accept': "*/*", 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
    data_list = []
    for page in range(1, 101):
        print(f'Сбор позиций со страницы {page} из 100')
        url = f'https://catalog.wb.ru/catalog/{shard}/catalog?appType=1&curr=rub&dest=-1075831,-77677,-398551,12358499' \
              f'&locale=ru&page={page}&priceU={low_price * 100};{top_price * 100}' \
              f'®ions=64,83,4,38,80,33,70,82,86,30,69,1,48,22,66,31,40&sort=popular&spp=0&{query}'
        r = requests.get(url, headers=headers)
        data = r.json()
        print(f'Добавлено позиций: {len(get_data_from_json(data))}')
        if len(get_data_from_json(data)) > 0:
            data_list.extend(get_data_from_json(data))
        else:
            print(f'Сбор данных завершен.')
            break
    return data_list

Функция сохранения данных в excel (save_excel)

Пишем функцию записи в эксель файл, используя библиотеку pandas. На входе принимаем сами данные (data) и название файла (filename) под которым будем их сохранять

def save_excel(data, filename):
    df = pd.DataFrame(data)
    writer = pd.ExcelWriter(f'{filename}.xlsx')
    df.to_excel(writer, 'data')
    writer.save()
    print(f'Все сохранено в {filename}.xlsx')

Основная функция (parser)

Разберем основную функцию:

Первым делом спарсим каталог применив функцию get_catalogs_wb сохранив его в переменную catalog_list

Далее в блоке try-except из найденного каталога извлекаем название категории (name_category), название каталога (shard) и подраздел (query) при помощи функции search_category_in_catalog.

Создаем список данных о товаре использовав функцию get_content, в которую передаем раздел и подраздел (shard, query) и ограничение в виде ценового диапазона, от нижней цены (low_price) до верхнего предела (top_price) для сокращения результатов выдачи (рекомендую если ее свыше 10 тысяч).

По итогу выполнения кода, полученные результаты передадим в save_excel для сохранения в excel файл, передав сами данные и названия файла в формате: названиекатегории_from_нижняяцена_to_верхняяцена.

def parser(url, low_price, top_price):
    catalog_list = get_catalogs_wb()
    try:
        name_category, shard, query = search_category_in_catalog(url=url, catalog_list=catalog_list)
        data_list = get_content(shard=shard, query=query, low_price=low_price, top_price=top_price)
        save_excel(data_list, f'{name_category}_from_{low_price}_to_{top_price}')
    except TypeError:
        print('Ошибка! Возможно не верно указан раздел. Удалите все доп фильтры с ссылки')
    except PermissionError:
        print('Ошибка! Вы забыли закрыть созданный ранее excel файл. Закройте и повторите попытку')

Запуск парсинга Вайлбериз (Wildberries)

Для запуска воспользуемся конструкцией if __name__ == ‘__main__’

if __name__ == '__main__':
    url = input('Введите ссылку на категорию для сбора: ')
    low_price = int(input('Введите минимальную сумму товара: '))
    top_price = int(input('Введите максимальную сумму товара: '))

    parser(url, low_price, top_price)

Добавлю в код тестовые данные: ссылку на каталог велосипедов (url), нижнюю и верхнюю цены товаров 50000 и 100000 соответственно:

if __name__ == '__main__':
    url = 'https://www.wildberries.ru/catalog/sport/vidy-sporta/velosport/velosipedy'
    low_price = 5000
    top_price = 100000

    parser(url, low_price, top_price)

Запустим парсер и следим за процессом выполнения скрипта (Рисунок 6):

Процесс выполнения программы
Рисунок 6. Процесс выполнения кода

Мы получили 274 позиции и сохранили их в эксель файл Велосипеды_from_50000_to_100000.xlsx. Откроем и проверим, что у нас получилось (Рисунок 7):

Полученный результат
Рисунок 7. Результат парсинга wildberries

От автора

Спасибо за прочтение, по всем возникающим вопросам и предложениям пишем Мне

По данной статье и коду принимаю критику, предложения по улучшению кода. Парсер будет улучшаться, добавляться новые функции, такие как подтягивание более полных данных о товаре, остатки товаров на складах. Так же свои отзывы и предложения вы можете писать в нашу группу ВКонтакте или в чате Telegram.

Статью про парсер комментариев с Wildberries можете почитать в нашей новой статье: Selenium webdriver в python. Поиск элементов на примере парсера комментариев Wildberries

Подпишитесь на рассылку

Если это было вам полезно — вы можете сказать нам спасибо!