【问题标题】:Python, web scraping and BeautifulSoup but does not workPython、网页抓取和 BeautifulSoup 但不起作用
【发布时间】:2021-03-29 08:23:14
【问题描述】:

我想从以下 html 代码中抓取所有产品的列表,以及它们是“instock”还是“outofstock”。

<div class="js-product-sizer sizes__layout built" data-scope="conversion-zone">   
            <div class="sizes__wrapper sizes__wrapper--visible" data-id="1042303" data-sellableonline="true">
              <span class="sizes__button sizes__button--selected" role="button" data-quantity="">Taglia</span>
              <ul class="sizes__list" role="listbox">
                  <li class="sizes__size" data-id="969834" data-belowthreshold="false" data-quantity="true" data-available-quantity="1631" data-backinstockqualified="false" data-locale="it" data-storeprice="€1,49" data-storename="Cavallino, Lecce" data-price="1.49" data-weight="0.528" data-favstore-stock="10" data-favstore-above-threshold="false" data-favstore-cnc1h="true" aria-labelledby="size-selector-title" role="option">
                    <span class="sizes__info" data-tnr-size-selector-bootstrap-by-text="">0,5 KG</span>
                    <span class="sizes__stock"                                                             
                                  <span class="sizes__stock__info" data-tnr-size-selector-stock-info="">Disponibile</span>                        
                    </span>
                  </li>
                
                  <li class="sizes__size" data-id="969842" data-belowthreshold="false" data-quantity="false" data-available-quantity="0" data-backinstockqualified="true" data-locale="it" data-storeprice="€3,49" data-storename="Cavallino, Lecce" data-price="3.49" data-weight="1.074" data-displayname="Disco ghisa bodybuilding 28mm" data-favstore-stock="0" data-favstore-above-threshold="false" data-favstore-cnc1h="false" aria-labelledby="size-selector-title" role="option">
                    <span class="sizes__info" data-tnr-size-selector-bootstrap-by-text="">1 KG</span>
                    <span class="sizes__stock">   
                              <span class="sizes__stock__info sizes__stock__info--nostock" data-tnr-size-selector-stock-info="">0 disponibili</span>
                            

我已经运行了以下代码:

import requests
from bs4 import BeautifulSoup
import time

r = requests.get('https://www.decathlon.it/p/disco-ghisa-bodybuilding-28mm/_/R-p-7278?mc=1042303&c=NERO')
soup = BeautifulSoup(r.text, 'html.parser')

for anchor_tag in soup.find_all(class_="js-product-sizer sizes__layout built")[0].findChildren():
    if "sizes_stock" in anchor_tag['class']:
        print(f"Size {anchor_tag.text} OOS")
    else:
        print(f"Size {anchor_tag.text}  in stock!")

但它给了我以下错误:

IndexError: list index out of range

【问题讨论】:

  • 您从哪里复制了这段 HTML 代码?来自您的浏览器还是来自请求?请注意,您无法使用这种方式通过 JavaScript 获取动态创建的内容。如果是 JavaScript 动态创建的内容,那么你应该使用 Selenium。
  • 你可以给我一个硒代码的例子吗?
  • 我没有使用 Selenium。我在我的项目中使用了 BeautifulSoup。在考虑 selenium 之前试试这个:像这样更改 soup.find_all(...) 中的 for anchor_tag 部分:soup.find_all("div",{"class"="js-product-sizer sizes__layout built"})

标签: python python-3.x web-scraping beautifulsoup python-requests


【解决方案1】:

如前所述,他们有一种方法可以检测自动化过程以提取信息。如果您使用 selenium 访问页面 a,它确实会调出 html。所以你可以使用 selenium 获取页面源,然后进行解析。

还在那里稍微清理了您的逻辑。您正在打印'in stock',无论它是否有任何可用数量。

另外,如果您在使用 find_all() 时只是要提取第一个元素 (soup.find_all(class_="js-product-sizer sizes__layout built")[0]),不妨只使用 find(),因为这正是它返回的内容,它找到的第一个元素.

代码:

from selenium import webdriver
from bs4 import BeautifulSoup
import time

url = 'https://www.decathlon.it/p/disco-ghisa-bodybuilding-28mm/_/R-p-7278?mc=1042303&c=NERO'


while True:

    driver = webdriver.Chrome(executable_path=r'C:/chromedriver_win32/chromedriver.exe')

    meta = 'ROBOTS'
    while meta == 'ROBOTS':
        driver.get(url)
        page = driver.page_source 
        soup = BeautifulSoup(page,'html.parser')
        if 'name' in soup.find('meta').attrs:
            meta = soup.find('meta')['name']
        else:
            meta = False
        
    driver.close()

    anchor_tag = soup.find(class_="js-product-sizer sizes__layout built")
    items_list = anchor_tag.find_all('li')
    for item in items_list:
        if item['data-weight'] == '0.528':
            continue
        quantity = item['data-available-quantity']
        itemStr = item.text.split('\n')
        if int(quantity) > 0:
            itemStr.insert(2,quantity)
        itemStr = ' '.join(itemStr).strip()
        if item['data-available-quantity'] == '0':
            print(f"Size {itemStr} OOS")
        else:
            print(f"Size {itemStr}  in stock!")
    
    time.sleep(60)

输出:

Size 0,5 KG 1584  Disponibile  in stock!
Size 1 KG  0 disponibili OOS
Size 2 KG  0 disponibili OOS
Size 5 KG  0 disponibili OOS
Size 10 KG  0 disponibili OOS
Size 20 KG  0 disponibili OOS

【讨论】:

  • 哦,谢谢你。有用。只是一个问题:如果我想每分钟重复一次代码?怎么可能加个while真:time.sleep(1)
  • 我创建了一个函数并添加了while True: func() time.sleep(1) 但是每次打开一个新的promt窗口,我都可以解决吗?
  • 你会想要 time.sleep(60) (持续 60 秒)。但是,你可以创建一个函数,或者像我上面所做的那样简单地添加两行代码
  • 好吧,但是对于每个 while 循环,程序都会打开一个提示窗口,我可以在每个 while 循环结束时关闭它吗?
  • 当然。或者只是让它保持打开状态。真的取决于你。但如果你让它保持打开状态,请将driver = webdriver.Chrome(executable_path=r'C:/chromedriver_win32/chromedriver.exe') 移动到while True 之前,
【解决方案2】:

你可以通过调用来查看你的代码有什么问题

r.raise_for_status()

或手动检查r.status_code

你会看到requests.exceptions.HTTPError: 403 Client Error: Forbidden for url:...。因此,您的响应文本中实际上没有该类的 div,并且无法获取索引为 0 的元素并引发IndexError。您尝试抓取的网站正在检测简单的抓取请求,请尝试至少设置一些标头(也许fake_useragent 可以帮助您)。

【讨论】:

  • 不需要用户代理库。 requests.get(url, headers={"User-Agent":"Mozilla/5.0"}) 就足够了。
  • @electromeow,在这种情况下不是。即使是简单的用户代理也会返回 &lt;html style="height:100%"&gt;&lt;head&gt;&lt;meta content="NOINDEX, NOFOLLOW" name="ROBOTS"/&gt;... 所以这行不通
猜你喜欢
  • 2021-05-10
  • 1970-01-01
  • 1970-01-01
  • 2020-11-07
  • 2016-06-19
  • 2018-04-25
  • 2014-06-20
  • 1970-01-01
相关资源
最近更新 更多