WebCrawler, только несколько товаров имеют сниженные цены - ошибка индекса

Я новичок в программировании и пытаюсь создать свой первый небольшой веб-сканер на python.

Цель: Сканирование страницы со списком товаров – получение названия бренда, названия статьи, исходной цены и новой цены – сохранение в CSV-файле.

Статус: мне удалось получить название бренда, название артикула, а также первоначальную цену и расположить их в правильном порядке в списке (например, 10 продуктов). Поскольку для всех товаров есть название бренда, описание и цена, мой код заносит их в CSV в правильном порядке.

Код:

    import bs4 
    from urllib.request import urlopen as uReq
    from bs4 import BeautifulSoup as soup

    myUrl = 'https://www.zalando.de/rucksaecke-herren/'

    #open connection, grabbing page, saving in page_html and closing connection 
    uClient = uReq(myUrl)
    page_html = uClient.read()
    uClient.close()

    #Datatype, html paser
    page_soup = soup(page_html, "html.parser")

    #grabbing information
    brand_Names = page_soup.findAll("div",{"class": "z-nvg-cognac_brandName-2XZRz z-nvg-cognac_textFormat-16QFn"})
    articale_Names = page_soup.findAll ("div",{"class": "z-nvg-cognac_articleName--arFp z-nvg-cognac_textFormat-16QFn"})
    original_Prices = page_soup.findAll("div",{"class": "z-nvg-cognac_originalPrice-2Oy4G"})
    new_Prices = page_soup.findAll("div",{"class": "z-nvg-cognac_promotionalPrice-3GRE7"})

    #opening a csv file and printing its header
    filename = "XXX.csv"
    file = open(filename, "w")
    headers = "BRAND, ARTICALE NAME, OLD PRICE, NEW PRICE\n"
    file.write(headers)

    #How many brands on page?
    products_on_page = len(brand_Names)

    #Looping through all brands, atricles, prices and writing the text into the CSV 
    for i in range(products_on_page): 
            brand = brand_Names[i].text
            articale_Name = articale_Names[i].text
            price = original_Prices[i].text
            new_Price = new_Prices[i].text
            file.write(brand + "," + articale_Name + "," + price.replace(",",".") + new_Price.replace(",",".") +"\n")

    #closing CSV
    file.close()

Проблема: мне не удается вставить цены со скидкой в ​​CSV-файл в нужном месте. Не у каждого товара есть скидка, и в настоящее время я вижу две проблемы с моим кодом:

  1. Я использую .findAll для поиска информации на веб-сайте — поскольку товаров со скидкой меньше, чем всего товаров, мой new_Prices содержит меньше цен (например, 3 цены на 10 товаров). Если бы я мог добавить их в список, я предполагаю, что они появятся в первых трех строках. Как я могу убедиться, что новые_цены добавлены в правильные продукты?

  2. Я получаю сообщение «Ошибка индекса: индекс списка вне диапазона». Я предполагаю, что это вызвано тем, что я перебираю 10 продуктов, однако для new_Prices я достигаю конца быстрее, чем для других моих списков? Имеет ли это смысл и верно ли мое предположение?

Я очень ценю любую помощь.

Благодарить,

Торстен


person Thorstein Torento    schedule 05.11.2017    source источник
comment
Пожалуйста, не публикуйте скриншоты своего кода, скопируйте соответствующий код в блоки кода.   -  person bgse    schedule 05.11.2017
comment
опубликуйте пример ввода тоже   -  person Guilherme    schedule 05.11.2017
comment
@bgse обновлен кодом в блоки   -  person Thorstein Torento    schedule 05.11.2017
comment
@ Гильерме не уверен, понимаю ли я, не могли бы вы уточнить? Что вы имеете в виду под примером ввода   -  person Thorstein Torento    schedule 05.11.2017
comment
@ThorsteinTorento Я полагаю, что Гильерме просит вас опубликовать ссылку на рассматриваемый сайт. Это помогло бы нам понять, что не работает в вашем коде.   -  person emporerblk    schedule 05.11.2017
comment
Я прошу пример списка продуктов, который вы получаете, чтобы увидеть проблему и подумать, как ее обойти.   -  person Guilherme    schedule 05.11.2017
comment
@emporerblk Спасибо за разъяснения. Я обновлю код через 1 секунду   -  person Thorstein Torento    schedule 06.11.2017
comment
@Guilherme: Спасибо за разъяснения. Я обновил код с помощью myUrl = 'zalando.de/rucksaecke-herren'. Спасибо. за вашу помощь, ребята, надеюсь, вы можете мне помочь   -  person Thorstein Torento    schedule 06.11.2017
comment
Только 6 из 24 предметов имеют класс z-nvg-cognac_infoContainer-MvytX. Вы можете выбрать товары по '.z-nvg-cognac_infoContainer-MvytX', а затем найти марку, артикул, цену, новую цену (или нет)   -  person t.m.adam    schedule 06.11.2017
comment
Привет @t.m.adam, спасибо за подсказку! Я думал об этом, но изо всех сил пытался перейти к нужному div по цене. До сих пор я просматривал container.div.div для каждого примера, но это всегда поможет мне глубже погрузиться в первый div. Мне нужно было бы перейти ко второму, так что что-то вроде contains.div[x].div, но я не уверен в синтаксисе. Не могли бы вы помочь?   -  person Thorstein Torento    schedule 09.11.2017


Ответы (1)


Поскольку некоторые элементы не имеют тега 'div.z-nvg-cognac_promotionalPrice-3GRE7', вы не можете надежно использовать индекс списка.
Однако вы можете выбрать все теги контейнера ('div.z-nvg-cognac_infoContainer-MvytX') и использовать find для выбора тегов для каждого элемента.

from urllib.request import urlopen
from bs4 import BeautifulSoup as soup
import csv

my_url = 'https://www.zalando.de/sporttaschen-reisetaschen-herren/'
client = urlopen(my_url)
page_html = client.read().decode(errors='ignore')
page_soup = soup(page_html, "html.parser")

headers = ["BRAND", "ARTICALE NAME", "OLD PRICE", "NEW PRICE"]
filename = "test.csv"
with open(filename, 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(headers)

    items = page_soup.find_all(class_='z-nvg-cognac_infoContainer-MvytX')
    for item in items:
        brand_names = item.find(class_="z-nvg-cognac_brandName-2XZRz z-nvg-cognac_textFormat-16QFn").text
        articale_names = item.find(class_="z-nvg-cognac_articleName--arFp z-nvg-cognac_textFormat-16QFn").text
        original_prices = item.find(class_="z-nvg-cognac_originalPrice-2Oy4G").text
        new_prices = item.find(class_="z-nvg-cognac_promotionalPrice-3GRE7")
        if new_prices is not None: 
            new_prices = new_prices.text 
        writer.writerow([brand_names, articale_names, original_prices, new_prices])

Если вы хотите получить более 24 элементов на странице, вы должны использовать клиент, который запускает js, например selenium.

from selenium import webdriver
from bs4 import BeautifulSoup as soup
import csv

my_url = 'https://www.zalando.de/sporttaschen-reisetaschen-herren/'
driver = webdriver.Firefox()
driver.get(my_url)
page_html = driver.page_source
driver.quit()
page_soup = soup(page_html, "html.parser")
...

Сноски:
соглашения об именах для функций и переменные в нижнем регистре с символами подчеркивания.
При чтении или записи CSV-файлов лучше всего использовать csv lib.
При работе с файлами вы можете использовать with заявление.

person t.m.adam    schedule 09.11.2017
comment
Привет @t.m.adam, очень ценю ваши отзывы и предложения! Я наконец-то добрался туда сам, но ваш код выглядит намного аккуратнее! Одна мысль, которую я заметил, заключается в том, что страница, должно быть, изменилась, так как теперь на странице более 24 элементов. Как ни странно, при запуске сканера он находит только 24 элемента. Есть идеи, почему? - person Thorstein Torento; 20.11.2017
comment
Да, остальные элементы загружаются js. Вы можете проверить это, если отключите js в своем браузере и посетите страницу. Вы можете получить все элементы с помощью selenium или иногда через ajax API. Я опубликую пример, когда у меня будет немного свободного времени. - person t.m.adam; 20.11.2017
comment
Привет @t.m.adam, отлично! Спасибо! Из интереса, почему страница настроена таким образом (загрузка этих 24 элементов, а затем остальных через JS)? Спасибо, Т - person Thorstein Torento; 20.11.2017
comment
Понятия не имею, впервые такое вижу. Это должен быть либо статический html, либо js для всех элементов в одной группе. Во всяком случае, вам удалось получить результаты с селеном? - person t.m.adam; 20.11.2017
comment
Привет @t.m.adam интересно! :) Я попробую, как только буду вне работы - только что посмотрел на него в обеденный перерыв! Я буду держать вас в курсе - person Thorstein Torento; 20.11.2017