【问题标题】:Issue in scraping data from a website using beautiful soup使用漂亮的汤从网站上抓取数据的问题
【发布时间】:2012-12-13 14:26:51
【问题描述】:

我正在尝试从网站上抓取 41 件商品及其价格的清单。但是我的输出 csv 缺少页面末尾的一些 2-3 项。原因是,某些设备的价格与其他设备不同。 我的代码中的递归是针对名称和价格一起运行的,对于在不同类别下提到价格的项目,它从下一个设备中获取价格值。因此,它会跳过最后 2-3 项,因为这些设备的价格已经在以前的设备中以递归方式输入。 以下是参考代码:

# -*- 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 下提及,但对于目前价格低于listGrid-price-outOfStock 的某些 2-3 件商品,我需要在递归中也包含此价格,以便正确的价格出现在商品和循环运行之前适用于所有设备。

由于我是编程新手,请原谅我的无知

【问题讨论】:

    标签: python-2.7 screen-scraping beautifulsoup


    【解决方案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
    

    (函数可以更简洁,这里的冗长只是为了让你了解它是如何工作的)

    因此它将与两者进行比较并在任何情况下返回匹配项。

    更多信息请访问documentation。 (has_six_characters 变体)

    现在,因为您还询问了如何排除特定文本。

    text 参数 findAll() 也可以有自定义比较器。 因此,在这种情况下,您不希望 Write a review 的文本匹配并导致价格与文本发生变化。

    因此您编辑的脚本排除了审查部分:

    # -*- 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])
    

    【讨论】:

    • 谢谢!我认为这是可行的,但是由于另一个类似的问题,我无法获得所需的输出。问题:所有项目名称都在“clickStreamSingleItem”类下,现在这个类下还有一个东西是“写评论”文本。这再次在递归中产生问题并导致项目名称和价格不匹配。请帮我解决这个问题。
    • 同理,写一个自定义比较器:)
    • 如何在这个问题中实现它?这里的类是相同的,但是当它在一个类下时我需要忽略“写评论”字符串,这样我的 csv 就没有它了。
    • 或者,如果“写评论”是唯一的事情,最简单的方法是:在if textcontent:之后添加`如果“写评论”不在item.string中:`
    • 但是如果我在if textcontent 之后将` if "Write a review" not in item.string:` 放在if textcontent 之后,它将跳过那些在"write a review" 之后立即出现的价格,因为递归是同时运行商品和价格,并且再次跳过某些设备
    猜你喜欢
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 1970-01-01
    • 2012-12-08
    • 2020-08-14
    • 1970-01-01
    • 1970-01-01
    • 2012-12-16
    相关资源
    最近更新 更多