【问题标题】:Extract data using BeautifulSoup, returning NoneType使用 BeautifulSoup 提取数据,返回 NoneType
【发布时间】:2019-12-25 01:37:27
【问题描述】:

使用soup.find提取表数据时,变量表返回NoneType。错误是:“NoneType”对象没有“查找”属性。来自网站的类名如下图所示,所以并不是我使用了错误的类名。我不确定我做错了什么,感谢任何帮助。

import requests
from bs4 import BeautifulSoup

url = "https://www.wunderground.com/history/monthly/KATL/date/2019-12"
page = requests.get(url)
soup = BeautifulSoup(page.content, 'html.parser')
table = soup.find("table",{"class":"days ng-star-inserted"})
table_body = table.find('tbody')

【问题讨论】:

  • 您是否检查了该类是否也存在于请求返回的 HTML 中?
  • 好吧,看来表确实是动态创建的。我相信这是返回数据的请求:https://api.weather.com/v1/location/KATL:9:US/observations/historical.json?apiKey=6532d6454b8aa370768e63d6ba5a832e&units=e&startDate=20191201&endDate=20191231.

标签: python html web-scraping beautifulsoup


【解决方案1】:

Requests 会“按原样”返回页面的源代码:它不执行任何 JavaScript 等。该页面中的表格是由对数据进行 API 调用的脚本动态创建的,这意味着当 BeautifulSoup 解析 HTML 时,该表格根本不存在。当我检查时,获取数据的确切请求是https://api.weather.com/v1/location/KATL:9:US/observations/historical.json?apiKey=6532d6454b8aa370768e63d6ba5a832e&units=e&startDate=20191201&endDate=20191231

对此有两种典型的解决方案:第一种是理想的解决方案,即找到页面用来获取数据的 API 调用/请求,然后自己复制这些调用/请求。第二个是以编程方式控制浏览器(或复制其功能),人们经常使用 Selenium 来做这件事。

幸运的是,该网站似乎提供了 API。当然,如果您的目标只是学习网络抓取,这并不是很好。

【讨论】:

    【解决方案2】:

    数据是通过您可以在网络标签中找到的 API 调用动态检索的。 API 密钥可以从几个 js 源文件中的任何一个中提取 - 需要额外的请求。您需要转换为 EST,然后使用 groupby 计算最大值、最小值、平均值。示例:

    import requests
    import pandas as pd
    import numpy as np
    from datetime import datetime
    from pytz import timezone
    import pytz
    import re
    
    def get_api_key():
        r = requests.get('https://www.wunderground.com/bundle-next/6-es2015.002cd6130a78daeda544.js')
        p = re.compile(r'sunApiKey:"(.*?)"')
        api_key = p.findall(r.text)[0]
        return api_key
    
    def get_est_localtime(timestamp):
        utc_dt =  utc.localize(datetime.utcfromtimestamp(timestamp))
        est_dt = utc_dt.astimezone(est_tz)
        return est_dt
    
    utc = pytz.utc
    fmt = '%Y-%m-%d'
    est_tz = timezone('US/Eastern')
    api_key = get_api_key()
    r = requests.get(f'https://api.weather.com/v1/location/KATL:9:US/observations/historical.json?apiKey={api_key}&units=e&startDate=20191201&endDate=20191231')
    df = pd.DataFrame(r.json()['observations'])
    df['valid_time_gmt'] = df['valid_time_gmt'].map(lambda x: get_est_localtime(x).strftime(fmt))
    df = df.groupby(by='valid_time_gmt').agg([min, max, np.mean])
    print(df['temp'])
    

    参考资料:

    1. pytz
    2. agg@MightyCurious
    3. pandas agg

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-17
      • 2020-10-18
      • 1970-01-01
      • 2019-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多