【问题标题】:efficient web scraping Python高效的网页抓取 Python
【发布时间】:2021-07-06 23:57:48
【问题描述】:

您好,我是网页抓取的新手,想用 beautifulsoop 抓取一个网站。现在我想知道 关于如何编写高效的代码。这是关于一个自行车网站,他们有几辆自行车,每辆自行车都有价格、状态、距离和持续时间的功能。他们都有相同的“产品壮举”。将所有这些功能放入 pandas 数据框的最有效方法是什么?我特别问是因为所有功能都具有相同的类,并且循环对我来说似乎效率低下。

<div class="product-smalltext">
<p class="product-feat">
<span>GBP 6'900 <small>(changeable)</small></span><br/> price
                </p>
<p class="product-feat">
<span>new</span><br/> state
                </p>
<p class="product-feat">
<span>10'000 km</span><br/> distance
                </p>
<p class="product-feat">
<span>48 months</span><br/> duration
                  </p>

我用一个字典(之后我将转换为一个数据框)和一个双循环来完成它,见下文。但这真的是 最有效的方法是什么?使用循环和附加/扩展?还是有更好的办法?

#empty dictionary
content={'price':[], 
         'state':[],
         'distance':[],
         'duration':[]
        }


listing_content = soup.find_all("div",{"class":"product-smalltext"})
#double loop through all bikes and features
for smalltext in listing_content:
    for feat in smalltext.find_all("p",{"class":"product-feat"}):
        content['price'].extend(re.findall(r'(?s)^.*?(?=\s*price)', feat.get_text().strip().replace('\xa0',' ')))
        content['state'].extend(re.findall(r'(?s)^.*?(?=\s*state)', feat.getText().strip()))
        content['distance'].extend(re.findall(r'(?s)^.*?(?=\s*distance)', feat.getText().strip()))
        content['duration'].extend(re.findall(r'(?s)^.*?(?=\s*duration)', feat.getText().strip()))

【问题讨论】:

    标签: python html pandas web-scraping beautifulsoup


    【解决方案1】:

    您可以使用列表推导并直接构造数据框:

    pd.DataFrame([[y.text
        for y in x.select('p.product-feat > span')]
        for x in soup.select('div[class="product-smalltext"]')],
        columns=['price', 'state', 'distance', 'duration'])
    

    输出(我又添加了一个 &lt;div&gt; 以确保它适用于每页多个项目):

                         price state   distance   duration
    0   GBP 6'900 (changeable)   new  10'000 km  48 months
    1  GBP 16'900 (changeable)   old  20'000 km  48 months
    

    更新:我们还可以从文本中提取产品特征的名称并将其用作列名。如果缺少某些值,我们将在数据框中得到NaNs:

    pd.DataFrame([{y.find('br').next_sibling.strip(): y.find('span').text
        for y in x.select('p.product-feat')}
        for x in soup.select('div[class="product-smalltext"]')])
    

    更新2:具体网站:

    resp = requests.get('https://www.leasingmarkt.ch/listing')
    soup = bs4.BeautifulSoup(resp.text)
    
    df = pd.DataFrame([{y.find('br').next_sibling.strip(): y.find('span').text
        for y in x.select('p.product-feat')}
        for x in soup.select('div[class="product-smalltext"]')])
    
    df
    

    输出:

    
                    Anzahlung Erstzulassung Jährliche Fahrleistung  \
    0       CHF 0 (anpassbar)      Neuwagen  10'000 km (anpassbar)   
    1  CHF 13'250 (anpassbar)      Neuwagen  10'000 km (anpassbar)   
    2   CHF 2'000 (anpassbar)      Neuwagen              10'000 km   
    3  CHF 10'000 (anpassbar)      Neuwagen  15'000 km (anpassbar)   
    4   CHF 1'500 (anpassbar)      Neuwagen              10'000 km   
    5   CHF 3'500 (anpassbar)      Neuwagen              10'000 km   
    6   CHF 6'900 (anpassbar)      Neuwagen              10'000 km   
    7  CHF 10'500 (anpassbar)      Neuwagen  10'000 km (anpassbar)   
    8       CHF 0 (anpassbar)       08/2015              10'000 km   
    9       CHF 0 (anpassbar)      Neuwagen  10'000 km (anpassbar)   
    
                    Laufzeit Kilometerstand                  Leistung  
    0  48 Monate (anpassbar)           0 km  204 PS (150 kW), Elektro  
    1  48 Monate (anpassbar)           0 km   245 PS (180 kW), Benzin  
    2              48 Monate           0 km   190 PS (140 kW), Benzin  
    3  48 Monate (anpassbar)           0 km   150 PS (110 kW), Benzin  
    4              48 Monate           0 km  204 PS (150 kW), Elektro  
    5              48 Monate           0 km   320 PS (235 kW), Benzin  
    6              48 Monate           0 km    129 PS (95 kW), Benzin  
    7  48 Monate (anpassbar)           0 km   184 PS (135 kW), Benzin  
    8              48 Monate      46'000 km   190 PS (140 kW), Diesel  
    9  48 Monate (anpassbar)           0 km   320 PS (235 kW), Benzin  
    

    更新 3:这里是selenium

    url = 'https://www.leasingmarkt.ch/listing'
    driver = webdriver.Chrome()
    driver.get(url)
    
    html = driver.page_source
    soup = bs4.BeautifulSoup(html)
    
    df = pd.DataFrame([{y.find('br').next_sibling.strip(): y.find('span').text
        for y in x.select('p.product-feat')}
        for x in soup.select('div[class="product-smalltext"]')])
    

    【讨论】:

    • 非常感谢。我刚刚意识到,如果某些行有一个空状态,你会怎么做?如何在数据框中将其设为 nan 以及如何避免所有值都向左移动?
    • 太好了,谢谢。剩下的唯一一件事是这不起作用,因为还有类 'product-smalltext Listing' 所以 soup.select('div.product-smalltext')]) 将同时提供 product-smalltext 列表和 product-smalltext 并且仅product-smalltext 具有 product-feat。这适用于循环解决方案,但你将如何在这里做呢?
    • 酷,所以你想指定它只有“product-smalltext”类。您可以使用...for x in soup.select('div[class="product-smalltext"]')]... 进行操作,请参阅更新后的更新
    • 不知何故它仍然选择类product-smalltext listing,而不仅仅是product-smalltext,我得到错误AttributeError: 'NoneType' object has no attribute 'text'
    • 使用 selenium 添加了更新 3(对我来说很好,与更新 2 中的输出相同)
    猜你喜欢
    • 2018-02-21
    • 1970-01-01
    • 2015-10-23
    • 1970-01-01
    • 2016-08-25
    • 1970-01-01
    • 2021-01-12
    • 2022-01-27
    • 1970-01-01
    相关资源
    最近更新 更多