带BeautifulSoup的网络抓取问题

时间:2020-07-18 11:47:24

标签: python web web-scraping

我不熟悉Python网络抓取,并且正在抓取productreview.com进行审核。以下代码提取了单次审核所需的所有数据:

#Scrape TrustPilot for User Reviews (Rating, Comments)
import pandas as pd
import numpy as np
from bs4 import BeautifulSoup as bs
import json
import requests
import datetime as dt
final_list=[]
url = 'https://www.productreview.com.au/listings/world-nomads'
r = requests.get(url)
soup = bs(r.text, 'lxml')
for div in soup.find('div', class_ = 'loadingOverlay_24D'):
    try:
        name = soup.find('h4', class_ = 'my-0_27D align-items-baseline_kxl flex-row_3gP d-inline-flex_1j8 text-muted_2v5')
        name = name.find('span').text
        location = soup.find('h4').find('small').text
        policy = soup.find('div', class_ ='px-4_1Cw pt-4_9Zz pb-2_1Ex card-body_2iI').find('span').text
        title = soup.find('h3').find('span').text
        content = soup.find('p', class_ = 'mb-0_2CX').text
        rating = soup.find('div', class_ = 'mb-4_2RH align-items-center_3Oi flex-wrap_ATH d-flex_oSG')
        rating = rating.find('div')['title']
        final_list.append([name, location, policy, rating,  title, content])
    except AttributeError:
        pass
reviews = pd.DataFrame(final_list, columns = ['Name', 'Location', 'Policy', 'Rating', 'Title', 'Content'])
print(reviews)

但是当我编辑

for div in soup.find('div', class_ = 'loadingOverlay_24D'):

for div in soup.findAll('div', class_ = 'loadingOverlay_24D'):

我没有收到所有评论,只是一次又一次地循环访问相同的条目。

任何帮助将不胜感激。

谢谢!

2 个答案:

答案 0 :(得分:0)

问题1:循环内重复的数据

您的循环格式如下:

for div in soup.find('div' , ...):
    name = soup.find('h4', ... )
    policy = soup.find('div', ... )
    ...

请注意,您正在find对象的循环内调用soup。这意味着每次您尝试查找name的值时,它将从头开始搜索整个文档,并在每次迭代中返回第一个匹配项。

这就是为什么要不断获取相同数据的原因。

要解决此问题,您需要在当前所在的当前评论find内致电div。那是:

 for div in soup.find('div' , ...):
    name = div.find('h4', ... )
    policy = div.find('div', ... )
    ...

问题2:数据丢失和错误处理

在您的代码中,循环内的任何错误都将被忽略。但是,在解析和提取值时实际上会发生许多错误。例如:

    location = div.find('h4').find('small').text

并非所有评论都有位置信息。因此,代码将提取h4,然后尝试找到small,但找不到任何内容,返回None。然后,您在该.text对象上调用None,从而导致异常。因此,此评论不会添加到结果数据框中。

要解决此问题,您需要添加更多错误检查。例如:

  locationDiv = div.find('h4').find('small')
  if locationDiv:
      location = locationDiv.text
  else:
      location = ''

问题3:识别和提取数据

您要解析的页面的HTML损坏,并使用了看上去随机或至少不一致的CSS类。您需要为要提取的数据找到正确且唯一的标识符,以使它们严格匹配所有条目。

例如,您正在使用CSS类div提取评论容器loadingOverlay_24D。这是不正确的。这个CSS类似乎是用于“加载”占位符div或类似的东西。实际评论包含在div块中,如下所示:

<div itemscope="" itemType="http://schema.org/Review" itemProp="review">
.... 
</div>

请注意,唯一标识属性是itemProp属性。您可以使用以下方法提取这些div块:

soup.find('div', {'itemprop': 'review'}):

类似地,您必须找到要提取的其他数据的正确标识属性,以确保您完全正确地获取所有数据。

还有一件事情,当一个标签具有多个CSS类时,通常只有一个是您要使用的标识属性。例如,对于名称,您具有:

name = soup.find('h4', class_ = 'my-0_27D align-items-baseline_kxl flex-row_3gP d-inline-flex_1j8 text-muted_2v5')

但是实际上,您不需要所有这些类。在这种情况下,第一类足以识别名称h4

name = soup.find('h4', class_ = 'my-0_27D')

示例:

下面是一个从评论页面提取作者姓名的示例:

 for div in soup.find_all('div', {'itemprop': 'review'}):
    name = div.find('h4', class_ = 'my-0_27D')
    if (name): 
        name = name.find('span').text
    else:
        name = '-'
    print(name)

输出:

Aidan
Bruno M.
Ba. I.
Luca Evangelista
Upset
Julian L.
Alison Peck
...

答案 1 :(得分:-2)

该页面提供了损坏的html代码,而html.parser更好地处理了它。

soup = bs(r.text, 'lxml')更改为soup = bs(r.text, 'html.parser')