Проблема с очисткой данных с веб-сайта с использованием красивого супа

I am trying to scrape list of 41 items & their prices from a website. But my output csv is missing some 2-3 items which come at the end of the page. Reason for this being, some devices have their price mentioned in different class than rest of the devices. Recursion in my code is running for name and price together and for items where price is mentioned under different class, it is taking the price value from the next device. Hence, it is skipping last 2-3 items as prices for those devices already entered in recursion for previous devices. Below is the referred code:

# -*- coding: cp1252 -*-
import csv
import urllib2
import sys
import time
from bs4 import BeautifulSoup
page = urllib2.urlopen('http://www.att.com/shop/wireless/devices/smartphones.deviceListGridView.xhr.flowtype-NEW.deviceGroupType-Cellphone.paymentType-postpaid.packageType-undefined.html?taxoStyle=SMARTPHONES&showMoreListSize=1000').read()
soup = BeautifulSoup(page)
soup.prettify()
with open('AT&T_2012-12-28.csv', 'wb') as csvfile:
    spamwriter = csv.writer(csvfile, delimiter=',')
    spamwriter.writerow(["Date","Month","Day of Week","Device Name","Price"])
    items = soup.findAll('a', {"class": "clickStreamSingleItem"},text=True)
    prices = soup.findAll('div', {"class": "listGrid-price"})
    for item, price in zip(items, prices):
        textcontent = u' '.join(price.stripped_strings)
        if textcontent:            
            spamwriter.writerow([time.strftime("%Y-%m-%d"),time.strftime("%B"),time.strftime("%A") ,unicode(item.string).encode('utf8').replace('™','').replace('®','').strip(),textcontent])

Цена обычно упоминается в listGrid-price , но для некоторых 2-3 предметов, которые находятся на складе, в настоящий момент цена находится под listGrid-price-outOfStock Мне нужно включить это также в моя рекурсия, так что правильная цена идет до того, как элемент и цикл будут запущены для всех устройств.

Прошу простить мое невежество, поскольку я новичок в программировании

1
добавлено
Просмотры: 1

1 ответы

Вы можете использовать функцию компаратора, чтобы выполнить произвольное сравнение и передать его в свой findAll() .

Поэтому, если вы измените свою строку с помощью prices :

prices = soup.findAll('div', class_=match_both)

и определите функцию как:

def match_both(arg):
    if arg == "listGrid-price" or arg == "listGrid-price-outOfStock":
        return True
    return False

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

он будет таким образом сопоставляться с обоими и возвращать совпадение в любом из случаев.

Более подробную информацию можно найти в документации . (Вариант has_six_characters)

Теперь, поскольку вы также спрашивали, как исключить определенный текст.

text argument to findAll() can also have custom comparators. So in this case, you don't want text saying Write a review to match and cause a shift in price vs text.

Следовательно, отредактированный скрипт исключает часть обзора:

# -*- coding: cp1252 -*-
import csv
import urllib2
import sys
import time
from bs4 import BeautifulSoup

def match_both(arg):
    if arg == "listGrid-price" or arg == "listGrid-price-outOfStock":
        return True
    return False

def not_review(arg):
    if not arg:
        return arg
    return "Write a review" not in arg

page = urllib2.urlopen('http://www.att.com/shop/wireless/devices/smartphones.deviceListGridView.xhr.flowtype-NEW.deviceGroupType-Cellphone.paymentType-postpaid.packageType-undefined.html?taxoStyle=SMARTPHONES&showMoreListSize=1000').read()
soup = BeautifulSoup(page)
soup.prettify()
with open('AT&T_2012-12-28.csv', 'wb') as csvfile:
    spamwriter = csv.writer(csvfile, delimiter=',')
    spamwriter.writerow(["Date","Month","Day of Week","Device Name","Price"])
    items = soup.findAll('a', {"class": "clickStreamSingleItem"},text=not_review)
    prices = soup.findAll('div', class_=match_both)
    for item, price in zip(items, prices):
        textcontent = u' '.join(price.stripped_strings)
        if textcontent:
                spamwriter.writerow([time.strftime("%Y-%m-%d"),time.strftime("%B"),time.strftime("%A") ,unicode(item.string).encode('utf8').replace('™','').replace('®','').strip(),textcontent])
0
добавлено
То же самое, напишите пользовательский компаратор :)
добавлено автор favoretti, источник
Или, если «написать отзыв» - единственное, самым простым было бы: add `if« Написать отзыв »не в item.string:` сразу после , если textcontent:
добавлено автор favoretti, источник
Ах, нет, я вижу твою проблему сейчас, дай мне еще один момент.
добавлено автор favoretti, источник
См. Edit :) Похоже, это то, что вы хотите. Дайте мне знать, если вам нужно больше объяснять, как это работает.
добавлено автор favoretti, источник
Благодаря! это работает, я думаю, но из-за еще одной подобной проблемы я не могу получить желаемый результат. Проблема: все имена элементов находятся под классом «clickStreamSingleItem», теперь есть еще одна вещь, входящая в этот класс, который является «Написать отзыв». Это снова создает проблемы в рекурсии и вызывает несоответствие между именами позиций и ценами. Пожалуйста, помогите мне с этим.
добавлено автор user1915050, источник
Как это реализовать в этой проблеме? Класс здесь одинаковый, но мне нужно игнорировать строку «Написать отзыв», когда она попадает под класс, так что мой csv свободен от этого.
добавлено автор user1915050, источник
Но если я поставил `if 'Write a review', а не в item.string:` сразу после , если textcontent , он пропустит эти цены, которые будут сразу же после «написать отзыв», поскольку рекурсия запущена для оба пункта и цены вместе и снова некоторые устройства будут пропущены
добавлено автор user1915050, источник
Большое спасибо! это работает, вы оказали большую помощь. :)
добавлено автор user1915050, источник
Можете ли вы пройти через этот stackoverflow.com/questions/14068259/… и помогите мне, если сможете.
добавлено автор user1915050, источник