【问题标题】:scrape and parse table from nested URLs in python从 python 中的嵌套 URL 中抓取和解析表
【发布时间】:2020-07-27 18:19:02
【问题描述】:

我想抓取并从嵌套的 URL 链接中解析表格,并创建一个 pandas 数据框并将其导出。如果页面有一个表格,我想出了如何从 HTML 页面 scrape 表格,但现在我需要 scrape 并从母链接上的子链接解析表格,我想我需要遍历所有子链接以解析我感兴趣的表。我想知道是否有任何有效的方法可以使用BeautifulSoup 来做到这一点。谁能指出我如何做到这一点?

我的尝试

这是我目前从 HTML 页面抓取和解析单个表格的尝试,但我不知道如何从嵌套的 HTML 页面抓取和解析具有唯一表格名称的表格并创建最后是一个熊猫数据框。

def scrape_table(url):
    response = requests.get(url, timeout=10)
    bs= BeautifulSoup(response.content, 'html.parser')
    table = bs.find('table')
    list_of_rows = []
    for row in table.findAll('tr'):
        list_of_cells =[]
        for cell in row.findAll('td'):
            text = cell.text
            list_of_cells.append(text)
        list_of_rows.append(list_of_cells)
    x= list_of_rows[1:]
    df = pd.DataFrame(x, index=None)
    df.to_csv("output.csv")

但这是我想做的:

main_entry_html = "http://www.bom.gov.au/climate/current/statement_archives.shtml"
child_url_1= "http://www.bom.gov.au/climate/current/month/aus/archive/202001.summary.shtml"
child_url_2 = "http://www.bom.gov.au/climate/current/month/aus/archive/202002.summary.shtml"
child_url_2 = "http://www.bom.gov.au/climate/current/month/aus/archive/202003.summary.shtml"
...

以此类推,我需要通过2015-01 to 2020-07访问所有月度汇总链接,scrape并解析标题为Area-average rainfall的表格,最后创建数据框作为我想要的输出。

我想我可以使用 for 循环来迭代每个子 URL 链接(又名月份摘要链接),然后通过查看表名来解析我想要的表。我不确定如何在 python 中实现这一点?谁能指出我如何做到这一点?有什么可能的想法吗?

想要的输出

这是我想要在 scraping 并解析所有子 URL 链接中的所有表之后获得的所需数据帧。这是带有虚拟值的示例数据框:

有什么方法可以得到我想要的数据框吗?我如何抓取并从nested-url-link 解析表格?谁能给我关于如何实现预期输出的可能想法?谢谢

【问题讨论】:

    标签: python pandas web-scraping beautifulsoup


    【解决方案1】:

    您可以使用pandas inbuild 函数pandas.read_html() 直接查找表并将其转换为DataFrame。在此之后,您可以将所有 DataFrame 保存到一个列表中并concate 它们。另外我建议使用string formatting 来获取所有网址,因为您可以循环遍历所有日期,这也让您有机会将日期保存为 DataFrame 中的一列。

    import pandas as pd
    from bs4 import BeautifulSoup
    import requests
    import time
    
    dates = [201901, 201902, 201903, 201904, 201905]
    
    year = ['2019','2020']
    dates = []
    
    for year in year:
        for month in range(1,13):
            if year == '2020' and month == 7: break 
            if month <= 9: 
                dates.append(year + '0' + str(month))
            else:
                dates.append(year + str(month))
    
    frames = []
    
    for date in dates:
        r = requests.get(f'http://www.bom.gov.au/climate/current/month/aus/archive/{date}.summary.shtml', timeout = 10)
        soup = BeautifulSoup(r.content, 'html.parser')
    
        table = soup.find_all('table')   
        table = pd.read_html(str(soup))[2]      
        result = pd.DataFrame(table)
    
        dates_col = pd.DataFrame({'dates': [str(date)[:4] + '-' + str(date)[4:] for i in range(len(result.index)+1)]})
        result.insert(0, 'date', dates_col)
        result.columns = ['dates', 'region', 'rank', 'average', 'departure from mean', 'comment']
    
        frames.append(result)
        time.sleep(1)
    
    
    full_df = pd.concat(frames, ignore_index=True)
    

    【讨论】:

    • 感谢您的输出。我们可以使用参数方式而不是使用硬编码方式来处理dates 吗?我的意思是有什么方法可以将start_dateend_date 作为参数传递,以便我们可以相应地废弃数据?有什么可能的想法吗?谢谢
    • 是的,我认为有几种可能性。我在上面编辑了一个。
    • 当我们重命名full_df时,会出现值错误,我想是因为Unnamed: 0_level_1出现了。如何解决这个问题?为什么我们使用这条线if year == '2020' and month == 7: break,它会再次被硬编码吗?谢谢你漂亮的回答:)
    • 啊,是的,好点子。最好在循环中命名列,然后将它们连接起来。我在上面编辑了我的解决方案。
    猜你喜欢
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-28
    • 2021-10-24
    • 2020-01-11
    相关资源
    最近更新 更多