【问题标题】:Why am I receiving an attribute error for my BeautifulSoup code when the variable in question has a value?为什么当相关变量具有值时,我的 BeautifulSoup 代码会收到属性错误?
【发布时间】:2021-04-05 09:42:18
【问题描述】:

我使用 Python 3.9.1 和 selenium 和 BeatifulSoup 来为 Tesco 的网站创建我的第一个网络爬虫(一个自学的迷你项目)。但是,当我运行代码时,如下所示,我收到一个属性错误:

Traceback (most recent call last):
  File "c:\Users\Ozzie\Dropbox\My PC (DESKTOP-HFVRPAV)\Desktop\Tesco\Tesco.py", line 37, in <module>
    clean_product_data = process_products(html)
  File "c:\Users\Ozzie\Dropbox\My PC (DESKTOP-HFVRPAV)\Desktop\Tesco\Tesco.py", line 23, in process_products
    weight = product_price_weight.find("span",{"class":"weight"}).text.strip()
AttributeError: 'NoneType' object has no attribute 'find'

我不确定出了什么问题 - 标题和 URL 部分工作正常,但重量和价格部分返回此值。当我尝试打印 product_price 和 product_price_weight 变量时,它们返回了我期望的值(我不会在这里发布,它只是很长的 HTML)。

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from webdriver_manager.chrome import ChromeDriverManager
import time
from bs4 import BeautifulSoup


driver = webdriver.Chrome(ChromeDriverManager().install())

def process_products(html):
    clean_product_list = []
    soup = BeautifulSoup(html, 'html.parser')
    products = soup.find_all("div",{"class":"product-tile-wrapper"})

    for product in products:
        data_dict = {}
        product_details = product.find("div",{"class":"product-details--content"})
        product_price = product.find("div",{"class":"price-control-wrapper"})
        product_price_weight = product.find("div",{"class":"price-per-quantity-weight"})

        data_dict['title'] = product_details.find('a').text.strip()
        data_dict['product_url'] = ('tesco.com') + (product_details.find('a')['href'])
        weight = product_price_weight.find("span",{"class":"weight"}).text.strip()
        data_dict['price'] = product_price.find("span",{"class":"value"}).text.strip()
        data_dict['price'+weight] = product_price_weight.find("span",{"class":"value"}).text.strip()
        clean_product_list.append(data_dict)
    return clean_product_list 


master_list = []

for i in range (1,3):
    print (i)
    driver.get(f"https://www.tesco.com/groceries/en-GB/shop/fresh-food/all?page={i}&count=48")
    html = driver.page_source
    driver.maximize_window()
    clean_product_data = process_products(html)
    master_list.extend(clean_product_data)

print (master_list)

非常感谢任何帮助。 非常感谢,

【问题讨论】:

  • 您在循环中执行“product.find()”,因此它可以返回“无”。在使用从结果中分配的任何变量之前,您应该检查这一点。
  • 感谢您的建议。您究竟如何建议我这样做?
  • 你好 Ozgur,是的!不知何故,你的这个变量process_products weight 它的值是None,你正在对它做一个.find()。所以是的,它确实会导致错误。
  • 您通常如何检查“无”?
  • 您应该在您的问题中至少包含一个产品标签 - "div",{"class":"product-tile-wrapper"} - 格式化为代码。可能有更好的方法可以从中提取信息。

标签: python html selenium beautifulsoup attributeerror


【解决方案1】:

您可以通过更新您的process_products 函数来尝试此操作。再次注意在某些情况,您尝试执行.find() 的某些变量返回None,这仅意味着它没有 find基于.find() 函数中给出的参数的任何元素。

以这个为例:

假设这部分代码已经执行了

product_details = product.find("div",{"class":"product-details--content"})

现在,如果它基于tagsclass 找到一个元素,它将返回一个bs4 对象,但如果没有,它将返回None,所以假设它返回None

所以你的product_details 变量将是一个None 对象,所以一旦它在你的代码中再次成为None,你就可以这样做。同样,product_detailsNone

data_dict['title'] = product_details.find('a').text.strip()
#Another way of saying is 
#data_dict['title'] = None.find('a').text.strip() ##Clearly an ERROR

所以我在这里所做的就是把它放在try except 中,以简单地捕获这些错误并为您提供空字符串,表明您尝试执行的变量可能.find() 返回None或者可能是一些错误(关键是没有返回相关数据),这就是我使用tryexcept的原因,但你也可以只使用ifelse,但我认为这样做try except 更好。

def process_products(html):
    clean_product_list = []
    soup = BeautifulSoup(html, 'html.parser')
    products = soup.find_all("div",{"class":"product-tile-wrapper"})

    for product in products:
        data_dict = {}
        product_details = product.find("div",{"class":"product-details--content"})
        product_price = product.find("div",{"class":"price-control-wrapper"})
        product_price_weight = product.find("div",{"class":"price-per-quantity-weight"})

        try:
            data_dict['title'] = product_details.find('a').text.strip()
            data_dict['product_url'] = ('tesco.com') + (product_details.find('a')['href'])
        except BaseException as no_prod_details:
            '''
            This would mean that your product_details variable might be equal to None, so catching the error & setting
            yoour data with empty strings, indicating it can't do a .find()
            '''
            data_dict['title'] = ''
            data_dict['product_url'] = ''


        try:
            data_dict['price'] = product_price.find("span",{"class":"value"}).text.strip()

        except BaseException as no_prod_price:
            #Same here
            data_dict['price'] =''


        try:
            weight = product_price_weight.find("span",{"class":"weight"}).text.strip()
            data_dict['price'+weight] = product_price_weight.find("span",{"class":"value"}).text.strip()
        except BaseException as no_prod_price_weigth:
            #Same here again
            weight = ''
            data_dict['price'+weight] = ''



        clean_product_list.append(data_dict)




    return clean_product_list 

【讨论】:

  • 为什么不抓住AttributeError 并在整个for循环套件中放置一个try/except - 至少所有.finds - 然后记录、打印或传入除套件?
  • 首先是AttributeError 是的,但我不假设错误的类型,我会让提问者决定他会放什么,当然放的具体错误是好的。第二个在整个for循环周围放一个try/except?我不这么认为,因为在某些情况下,某些变量可能是 None 而有些不是实际找到结果文本...所以如果是这种情况,它可能会影响其他完全没有问题的结果.
  • 非常感谢。该程序终于开始工作了。然而,我不明白的是,为什么现在我已经添加了这个 try and except 代码,程序可以完美运行,但在最后创建的列表中,没有丢失的部分。如果之前有一个错误,并且添加了一个 try except ,那么肯定有人会期望一些数据丢失,因为一开始没有返回?我是这方面的业余爱好者,只是想了解这背后的推理和逻辑,因为这是正确的解决方案。
  • @wwii 这里的想法是,如果有一个元素返回None,那么只需分配一个空字符串,表示它没有找到任何元素来获取文本。
  • 对。我想我终于明白我做错了什么。感谢您的所有帮助,以及您回答我的问题所花费的时间。你让我很开心:)
猜你喜欢
  • 2022-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-13
  • 2016-09-05
  • 2022-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多