@Groslog

Как правильно спарсить характеристики с сайта и записать в отдельные колонки эксель?

Учусь писать парсеры, как мне cделать чтобы с сайта собиралась вся характеристика и записывалась в отдельные колонки эксель? ( пример ниже )

Проблема в том что в каждой карточке могут быть разные характеристики и в разном порядке (если в карточке нет такой характеристики этот столбец оставить пустым или написать no)

Пример
636909143821d404986438.jpeg
Код на сайте выглядит вот так
Скриншот фрагмента кода удален модератором.
думаю что нужно сделать списком(масивом) тогда нужен алгоритм

если повтор -> то он добавляет в эту строку

если новая -> то создает новую строку.

Код парсера:
# -*- coding: utf-8 -*-
import csv
import requests
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from time import sleep

headers = {"user-agent": UserAgent().random}

def get_request(url):
    r = requests.get(url, headers=headers)
    return r.text


def get_content(html):
    soup = BeautifulSoup(html, 'lxml')
    category = soup.find('ul', class_='catalogue-root-menu__list catalogue-root-menu__list_inline')

    # копируем все ссылки на категории
    for i in category:

        link = 'https://star-tex.ru' + i.find('a').get('href') # получаю каждую как ссылку
        r = requests.get(link, headers=headers)  # захожу в каждую

        soup = BeautifulSoup(r.text, 'lxml')
        page = soup.find_all('li', class_='pagination-page')[-1].text
        # чекаю сколько там есть страниц
        for numb in range(1, int(page) + 1):

            urls = link + f'page_{numb}/' # добавляю к ссылке кол-во страниц
            print(f'Парсинг страницы {numb}')

            r = requests.get(urls, headers=headers) # заход в каждую страницу
            soup = BeautifulSoup(r.text, 'lxml')

            for k in soup.find('div', class_='item-groups-list-container').find_all('div', class_='item-groups-list-item'):
                yield 'https://star-tex.ru/' + k.find('a', class_='link').get('href')

def get_content_parser(html):
    soup = BeautifulSoup(html, 'lxml')

    df = []

    head = soup.find('div', class_='item-group-data__header')
    detail = soup.find('div', class_='item-group-data__details')

    try:
        name = head.find('h1', class_='item-group-data__name').text.strip()
    except:
        name = 'No name'

    try:
        price = head.find('span', class_='integer').text.strip()
    except:
        price = '000'
    try:
        price_cop = head.find('span', class_='fractional').text.strip()
    except:
        price_cop = '00'

    try:
        hh = detail.find('div', class_='item-group-params-container')
        for i in hh.find('ul', class_='item-group-params'):
            title_har = i.find('span', class_='item-group-params__item-title').text.strip()
            no_tit_har = i.find('span', class_='item-group-params__item-value').text.replace("\xa0", '').strip()
            df.append((title_har, no_tit_har))


    except:
        pass

    print(df)
    data = {
            'name': name,
            'price': price + '.' + price_cop,

        }

    return data


# def csv_wr():
#     with open("test_pars.csv", "w", newline='') as file:
#         writer = csv.writer(file, delimiter=";")
#         writer.writerow(("Название", "Цена", "Характеристика"))
#
# def save_content(data):
#     with open("test_pars.csv", "a", newline='') as file:
#         writer = csv.writer(file, delimiter=";")
#
#         writer.writerow( [data['name'], data['price'], data['char']] )

def main():
    url = 'https://star-tex.ru/'
#    csv_wr()
    link = get_content(get_request(url))

    for i in link:

        html = get_request(i)
        data = get_content_parser(html)
#        save_content(data)

if __name__ == '__main__':
    main()
  • Вопрос задан
  • 342 просмотра
Решения вопроса 1
Недавно решал подобную задачу, при парсинге характеристик товаров, получал результирующий файл в Excel, где помимо общих данных товара: наименование, цена, описания, картинки и т.д., все характеристики были разнесены по разным колонкам, как то так:

636cb8986b6f6371909319.jpeg
Для получения данных с сайта источника, тоже использовал библиотеку BeautifulSoup,

Фрагмент HTML кода с характеристиками товара на сайте источнике для парсинга:
<div id="content_features" class="ty-wysiwyg-content content-features">
	<div class="ty-product-feature">
		<div class="ty-product-feature__label">Ширина:</div>
		<div class="ty-product-feature__value">1400<span class="ty-product-feature__suffix"> мм</span></div>
	</div>
	<div class="ty-product-feature">
		<div class="ty-product-feature__label">Высота:</div>
		<div class="ty-product-feature__value">1345<span class="ty-product-feature__suffix"> мм</span></div>
	</div>
	<div class="ty-product-feature">
		<div class="ty-product-feature__label">Глубина:</div>
		<div class="ty-product-feature__value">880<span class="ty-product-feature__suffix"> мм</span></div>
	</div>	
</div>

Фрагмент кода получения характеристик товара:
propsBody = []
        try:
            propsBody = soup.find('div', {'id' : 'content_features'}).findAll('div', class_='ty-product-feature')
        except:
            pass
        if len(propsBody) > 0:
            #self.properties.clear()
            propsItems = []
            for props in propsBody:
                item = {
                    'label' : props.find('div', class_='ty-product-feature__label').get_text(strip=True),
                    'value' : props.find('div', class_='ty-product-feature__value').get_text(strip=True)
                }
                propsItems.append(item)
            self.properties = propsItems

В результате, полученные данные характеристик, для каждого товара в виде словаря, типа:
[{"label": "Высота:", "value": "220мм"}, 
{"label": "Глубина:", "value": "295мм"}, 
{"label": "Материал:", "value": "Сталь"},
{"label": "Вес:", "value": "4,3кг"},
{"label": "Длина:", "value": "650мм"}]

Где в label - название характеристики, а в value – значение.

Учитывая, что у всех товаров разные типы, названия характеристик, собирал их в множество – так как в множестве могут быть только уникальные значения. Затем, при записи данных в Excel, как предлагал выше shurshur, автоматически добавлял колонки из полученных данных.

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

spoiler

class ExcelFile:
    data = {}
    labels = set() # множество, перменная, свойство для названий характеристик товаров

    def __init__(self, data):
        self.data = data 

    def get(self):
        # В цикле проходим по данным полученным в результате парсинга,
        # и собираем названия характеристик в множество
        for item in self.data:
            if len(item['properties']) > 0:
                for prop in item['properties']:
                    self.labels.add(prop['label'])

        book = openpyxl.Workbook()
        sheet = book.active
        
        # Поля, колонки Excel - файла, для разных данных товаров
        sheet['A1'].value = 'src'
        sheet['B1'].value = 'name'
        sheet['C1'].value = 'category'
        sheet['D1'].value = 'art_namber'
        sheet['E1'].value = 'price'
        sheet['F1'].value = 'main'
        sheet['G1'].value = 'more'
        sheet['H1'].value = 'descrip'
        sheet['I1'].value = 'JSON_properties'
        sheet['J1'].value = 'video'
        sheet['K1'].value = 'docs'
        # Добавляем дополнительные колонки в Excel из названий характеристик
        if len(self.labels) > 0:
            nam = 12
            for nameProp in self.labels:
                sheet.cell(row=1, column=nam, value=nameProp)
                nam += 1
        row = 2
        # Записываем в строки Excel- файла данные товаров
        for res in self.data:
            sheet[row][0].value = res['src']
            sheet[row][1].value = res['name']
            sheet[row][2].value = res['category']
            sheet[row][3].value = res['art_namber']
            sheet[row][4].value = res['price']
            sheet[row][5].value = res['main']
            sheet[row][6].value = ','.join(res['more'])
            sheet[row][7].value = res['descrip']
            sheet[row][8].value = res['json']
            sheet[row][9].value = ','.join(res['video'])
            sheet[row][10].value = ','.join(res['docs'])
            # Записываем значения характеристик товара в соответствующие названиям колонки Excel- файла
            if len(self.labels) > 0:
                namberCell = 12
                while namberCell < len(self.labels) + 12:
                    propLabel = sheet.cell(row=1, column=namberCell).value
                    for property in res['properties']:
                        if property['label'] == propLabel:
                            sheet.cell(row=row, column=namberCell).value = property['value']
                        else:
                            sheet.cell(row=row, column=namberCell)
                    namberCell += 1
            row += 1

        file_name = detNameExcelFile()
        book.save(file_name)
        book.close()

        return file_name



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

Другой вариант: разные категории помещать на разные листы (в случае с csv писать в разные файлы, ну или перейти на xlsxwriter). В этом случае таблица будет не такой разреженной, что в некоторых случаях может быть более удобно удобно.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы