【问题标题】:Web scraper using BeautifulSoup not working使用 BeautifulSoup 的网络刮板不起作用
【发布时间】:2021-06-06 06:37:11
【问题描述】:

我为 Stack Overflow 编写了一个网络爬虫,但它不起作用。显然,在我的汤中,有无处可寻的 NoneType 对象。这是网络爬虫代码:

import requests
from bs4 import BeautifulSoup

url = 'https://stackoverflow.com/questions?tab=newest&page='

r = requests.post(url)
soup = BeautifulSoup(r.text, 'lxml').find('div', id='questions').find_all('div')

for summary in soup: # FIXME: Prints each question twice
    try:
        print(f'Question: {summary.h3.text}')
        print(f'Tags: {", ".join(summary.find("div", class_="tags").text[1:].split(" "))}')
    except Exception as e:
        print(e) # Prints "'NoneType' has no attribute 'text'" which shouldn't be in the soup

我得到的错误(如果您没有阅读评论)是“'NoneType' 没有属性'text'”,这让我很困惑,因为汤中有 NoneType 对象。

我正在使用:

  • Windows 10
  • Python 3.8

【问题讨论】:

  • 您是否尝试使用 .find_all(class_='question-summary') 而不是 .find_all('div') ?当您使用.find_all('div') 时,它会针对每个容器中的所有 div。一些 div 不包含您要查找的任何内容,这就是导致该错误的原因。尝试找到一些独特的东西,如classid 等。顺便说一句,你也可以使用.find_all('div',recursive=False) 来解决这个问题。

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


【解决方案1】:

JP193 已经为您解答了为什么您会收到 NoneType 返回。

我在您的代码中发现的最大问题是,您使用BeautifulSoup(r.text,xml').find('div', id='questions').find_all('div') 找到的每个<div> 都不需要获取“问题”和“标签”,这就是为什么您会得到很多@ 987654324@ 消息。要浓缩相关的<div>,您需要仔细查看作为soup 的结果获得的代码。为了最好地浓缩soup 的结果,将找到所有<div>class"question-summary"。所以代码应该更像:

soup = BeautifulSoup(r.content, "html.parser").find("div", id="questions").find_all("div", {"class": "question-summary"})

此后,如何打印“问题”和“标签”的结果由您决定。我是这样做的:

    try:
        question = q.find("div", {"class": "summary"}).find('h3')
        print('Question: ' + question.text)

        tags = soup[0].find("div", {"class": "tags"}).text.split()
        print('Tags: ' + ', '.join(tags))
    except AttributeError:
        print('AttributeError div ' + str(int + 1)) 

综合我的解决方案是:

from bs4 import BeautifulSoup

url = 'https://stackoverflow.com/questions?tab=newest&page='

r = requests.get(url)
soup = BeautifulSoup(r.content, "html.parser").find("div", id="questions").find_all("div", {"class": "question-summary"})

for int, q in enumerate(soup):
    try:
        question = q.find("div", {"class": "summary"}).find('h3')
        print('Question: ' + question.text)

        tags = soup[0].find("div", {"class": "tags"}).text.split()
        print('Tags: ' + ', '.join(tags))
    except AttributeError:
        print('AttributeError div ' + str(int + 1))

【讨论】:

  • 关于你的代码的一个问题是从网站上抓取的所有问题都有一个共同的标签,这不是我想要我的抓取工具做的事情
  • 你在找什么?我试图在您的问题和您提供的代码之间阅读。你在找什么标签?
  • 我不是在寻找特定的标签,我只是想从网站上获得随机问题
  • 这行代码是做什么用的? print(f'Tags: {", ".join(summary.find("div", class_="tags").text[1:].split(" "))}')
  • 是为了打印标签
【解决方案2】:

soup 中没有 None 类型的对象,而是调用

... summary.h3 ...
... summary.find("div", class_="tags") ...

如果summary 没有h3 元素,或者当前summary 中没有类属性为tagsdiv 元素,则在您的循环内将返回None分别。

当您尝试访问None 类型元素的text 属性时,自然会在这些情况下抛出Exception

使用enumerate 显示导致Exception 被抛出的soup 的索引可能会有所帮助。

例如

for i, summary in enumerate(soup):
    try:
        print(f'Question: {summary.h3.text}')
        print(f'Tags: {", ".join(summary.find("div", class_="tags").text[1:].split(" "))}')
    except Exception as e:
        print(f'Exception raised during handling of soup[{i}]')
        print(e)

最好使用上述方法来更深入地了解响应结构,以便在解析过程中最好忽略这些元素。

当然,你总是可以对pass 在循环中抛出的任何AttributeErrors 做一些懒惰的事情,但仍然打印任何其他类型的Exception 抛出 - 尽管这显然不是最有效的方法。

for summary in soup:
    try:
        print(f'Question: {summary.h3.text}')
        print(f'Tags: {", ".join(summary.find("div", class_="tags").text[1:].split(" "))}')
    except AttributeError as ee:
        pass
    except Exception as e:
        print(e)

【讨论】:

    猜你喜欢
    • 2016-01-23
    • 1970-01-01
    • 1970-01-01
    • 2021-05-01
    • 2012-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多