【问题标题】:Web Scraping shopee.sg with selenium and BeautifulSoup in python在 python 中使用 selenium 和 BeautifulSoup 抓取 shopee.sg
【发布时间】:2021-03-22 13:31:19
【问题描述】:

每当我尝试使用 selenium 和 BeautifulSoup 抓取 shopee.sg 时,我都无法从单个页面中提取所有数据。

示例 - 对于包含 50 种产品的搜索结果,前 15 种产品的信息被提取,而其余的产品则为空值。

现在,我知道这与滚动条有关,但我不知道如何使它工作。知道如何解决这个问题吗?

截至目前的代码

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from time import sleep
import csv

# create object for chrome options
chrome_options = Options()
#base_url = 'https://shopee.sg/search?keyword=disinfectant'

# set chrome driver options to disable any popup's from the website
# to find local path for chrome profile, open chrome browser
# and in the address bar type, "chrome://version"
chrome_options.add_argument('disable-notifications')
chrome_options.add_argument('--disable-infobars')
chrome_options.add_argument('start-maximized')
#chrome_options.add_argument('user-data-dir=C:\\Users\\username\\AppData\\Local\\Google\\Chrome\\User Data\\Default')
# To disable the message, "Chrome is being controlled by automated test software"
chrome_options.add_argument("disable-infobars")
# Pass the argument 1 to allow and 2 to block
chrome_options.add_experimental_option("prefs", { 
    "profile.default_content_setting_values.notifications": 2
    })


def get_url(search_term):
    """Generate an url from the search term"""
    template = "https://www.shopee.sg/search?keyword={}"
    search_term = search_term.replace(' ','+')
    
    #add term query to url
    url = template.format(search_term)
    
    #add page query placeholder
    url+= '&page={}'
    
    return url

def main(search_term):
# invoke the webdriver
    driver = webdriver.Chrome(options = chrome_options)


    item_cost = []
    item_name = []
    url=get_url(search_term)

    for page in range(0,3):
        driver.get(url.format(page))
        delay = 5 #seconds


        try:
            WebDriverWait(driver, delay)
            print ("Page is ready")
            sleep(5)
            html = driver.execute_script("return document.getElementsByTagName('html')[0].innerHTML")
            #print(html)
            soup = BeautifulSoup(html, "html.parser")
            #find the product description
            for item_n in soup.find_all('div',{'class':'col-xs-2-4 shopee-search-item-result__item'}):
                try:
                    description_soup = item_n.find('div',{'class':'yQmmFK _1POlWt _36CEnF'})
                    name = description_soup.text.strip()
                except AttributeError:
                    name = ''
                print(name)    
                item_name.append(name)

            # find the price of items
            for item_c in soup.find_all('div',{'class':'col-xs-2-4 shopee-search-item-result__item'}):
                try:
                    price_soup = item_c.find('div',{'class':'WTFwws _1lK1eK _5W0f35'})
                    price_final = price_soup.find('span',{'class':'_29R_un'})
                    price = price_final.text.strip()
                except AttributeError:
                    price = ''
                print(price)
                item_cost.append(price)
  
        except TimeoutException:
            print ("Loading took too much time!-Try again")
        sleep(5)
    rows = zip(item_name, item_cost)
    
    
    with open('shopee_item_list.csv','w',newline='',encoding='utf-8') as f:
        writer=csv.writer(f)
        writer.writerow(['Product Description', 'Price'])
        writer.writerows(rows)```

【问题讨论】:

    标签: python selenium web-scraping beautifulsoup


    【解决方案1】:

    问题在于,当您向下滚动页面时,您尝试抓取的产品会动态加载。可能有比我更优雅的解决方案,但我使用 driver.execute_script 实现了一个简单的 javascript 滚动条(附加资源:https://www.geeksforgeeks.org/execute_script-driver-method-selenium-python

    滚动条

    滚动到页面高度的十分之一,暂停 500 毫秒,然后继续。

    driver.execute_script("""
        var scroll = document.body.scrollHeight / 10;
        var i = 0;
        function scrollit(i) {
           window.scrollBy({top: scroll, left: 0, behavior: 'smooth'});
           i++;
           if (i < 10) {
               setTimeout(scrollit, 500, i);
           }
        }
        scrollit(i);
    """)
    
    

    此外,您有两个 for 循环,用于 soup.find_all(...) 中的 item_n,用于 soup.find_all(...) 中的 item_c,它们在同一类中的 div 上进行迭代.我在我的代码中修复了这个问题,这样您就可以在只使用一个 for 循环的同时获取每件商品的价格和名称。

    您还有 try-except 语句(以防出现 AttributeError,即如果您在 soup.find_all 中找到的项目是 NoneTypes)。我将它们简化为 if 语句,比如这个

    name = item.find('div', {'class': 'yQmmFK _1POlWt _36CEnF'})
    if name is not None:
        name = name.text.strip()
    else:
        name = ''
    

    最后,您将 zip 用于两个不同的列表(名称和价格),以添加到 csv 文件。我在 for 循环 中将这些单独的列表组合成一个嵌套列表,而不是附加到两个单独的列表并在最后压缩。这节省了一个步骤,尽管它是可选的,可能不是您需要的。

    完整(更新)代码

    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    from bs4 import BeautifulSoup
    import csv
    from time import sleep
    # create object for chrome options
    chrome_options = Options()
    # base_url = 'https://shopee.sg/search?keyword=disinfectant'
    
    # set chrome driver options to disable any popup's from the website
    # to find local path for chrome profile, open chrome browser
    # and in the address bar type, "chrome://version"
    chrome_options.add_argument('disable-notifications')
    chrome_options.add_argument('--disable-infobars')
    chrome_options.add_argument('start-maximized')
    # chrome_options.add_argument('user-data-dir=C:\\Users\\username\\AppData\\Local\\Google\\Chrome\\User Data\\Default')
    # To disable the message, "Chrome is being controlled by automated test software"
    chrome_options.add_argument("disable-infobars")
    # Pass the argument 1 to allow and 2 to block
    chrome_options.add_experimental_option("prefs", {
        "profile.default_content_setting_values.notifications": 2
    })
    
    
    def get_url(search_term):
        """Generate an url from the search term"""
        template = "https://www.shopee.sg/search?keyword={}"
        search_term = search_term.replace(' ', '+')
    
        # add term query to url
        url = template.format(search_term)
    
        # add page query placeholder
        url += '&page={}'
    
        return url
    
    
    def main(search_term):
        # invoke the webdriver
        driver = webdriver.Chrome(options=chrome_options)
        rows = []
        url = get_url(search_term)
    
        for page in range(0, 3):
            driver.get(url.format(page))
            WebDriverWait(driver, 20).until(EC.presence_of_all_elements_located((By.CLASS_NAME, "shopee-search-item-result__item")))
            driver.execute_script("""
            var scroll = document.body.scrollHeight / 10;
            var i = 0;
            function scrollit(i) {
               window.scrollBy({top: scroll, left: 0, behavior: 'smooth'});
               i++;
               if (i < 10) {
                setTimeout(scrollit, 500, i);
                }
            }
            scrollit(i);
            """)
            sleep(5)
            html = driver.page_source
            soup = BeautifulSoup(html, "html.parser")
            for item in soup.find_all('div', {'class': 'col-xs-2-4 shopee-search-item-result__item'}):
                name = item.find('div', {'class': 'yQmmFK _1POlWt _36CEnF'})
                if name is not None:
                    name = name.text.strip()
                else:
                    name = ''
    
                price = item.find('div', {'class': 'WTFwws _1lK1eK _5W0f35'})
                if price is not None:
                    price = price.find('span', {'class': '_29R_un'}).text.strip()
                else:
                    price = ''
                print([name, price])
                rows.append([name, price])
    
        with open('shopee_item_list.csv', 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            writer.writerow(['Product Description', 'Price'])
            writer.writerows(rows)
    
    

    【讨论】:

    • 嘿!谢谢你的解决方案。实际上,我被延迟加载部分卡住了,即使我想出了一种滚动页面的方法,我也无法同时提取信息。
    • 您能否建议任何其他资源可供参考,因为我在提取产品级别的信息时遇到了困难。例如(shopee.sg/…),如果我尝试抓取评分星数或产品名称、价格或品牌,它会返回一个空列表。我之前没有使用 javascript 的经验,因此我面临这些问题。
    • WebDriverWait 和某种标识符?要获得星级评分,我将使用 WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.CLASS_NAME, "_1mYa1t"))) 来等待找到该 single class 的元素,或者使用带有 xpath 的 WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, '//*[@id="main"]/div/div[2]/div[2]/div[2]/div[2]/div[3]/div/div[2]/div[1]/div[1]')))。之后,使用soup 获取文本。您可以对要提取的其他元素执行相同的操作。这等待找到元素。
    • 想得到你给我的产品名称,shopee.sg/…,我可以用WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.CLASS_NAME, "attM6y"))) html = driver.page_source soup = BeautifulSoup(html, "html.parser") for item in soup.find_all('div', {'class': 'attM6y'}): name = item.find('span').text print(name) 等待div元素,得到span标签within的文本i> div 元素。这给了我:“Dettol Disinfectant Spray Original Pine,225ml”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-23
    • 1970-01-01
    • 2020-11-23
    相关资源
    最近更新 更多