【问题标题】:How to scrape a pivot table with Beautiful Soup如何用 Beautiful Soup 抓取数据透视表
【发布时间】:2023-02-11 02:07:08
【问题描述】:

这是我第一次在这里提问,所以我提前为任何错误道歉。欢迎提出改进此问题的建议。

我正在尝试使用 Beautiful Soup 抓取 a complex Wikipedia table(我不确定用术语“数据透视表”概括此类表是否合适),希望在 Pandas 中重新创建一个更简单、更易分析的版本。

JLPT "Applications and results" table on English Wikipedia

作为概览,从左侧开始:该表列出了 JLPT 举行的年份,当年开放的考试级别,然后是顶部列定义的统计数据。聚合列对我的目的来说并不重要,但如果有一种方法可以像这样抓取和重建它们,那就太好了。

使该表难以重建的原因是它对行进行了分组(“年”列下的年份),但那一年的行被放置在与年份标题相同的层次级别中,而不是在。此外,不是在每个 <tr> 行中都有一个 <th> 年份标签,它只出现在年份组的第一行中:

HTML structure of the table

另一个问题是年份标题在它们的标签或属性中没有任何类型的定义标识符,所以我也不能只选择其中包含年份的行。

这些事情使得无法按年份对行进行分组。

到目前为止,我能够重建的唯一方法一些表格的作者是:

  1. 抓取整个表格,
  2. 将每个<tr> 元素附加到列表中,
  3. 因为每年都有方括号中的引用:删除每个带有[的字符串实例,导致每行中的元素长度统一
  4. 将它们转换为 pandas 数据框(手动添加列名、使用正则表达式删除剩余的 HTML 等),不带年份:

    Row elements in a list

    Processed dataframe (minus the years)

    走到这一步之后,现在我意识到,如果不手动进行,仍然很难按年份对行进行分组。我想知道是否有一种更简单、更直接的方法来仅使用 BeautifulSoup 本身来抓取类似的复杂表,并且在 pandas 中几乎没有后处理。在这种情况下,如果无法以其原始数据透视表格式获取表格也没关系,我只想获得每一行的年份值。就像是:

    Dataframe goal

    先感谢您。

【问题讨论】:

    标签: pandas web-scraping beautifulsoup html-table wikipedia


    【解决方案1】:

    您不需要使用 BeautifulSoup 来执行此操作。相反,您可以直接使用pd.read_html 来获得您需要的东西。当您从维基百科阅读 HTML 时,它会将所有表格拉入列表。如果你浏览列表,你会看到它是第 10 个数据帧。

    df = pd.read_html('https://en.wikipedia.org/wiki/Japanese-Language_Proficiency_Test')[10]
    

    从那里,您将进行一些数据清理以创建您需要的表。

    # Convert multi-level column into single columns
    df.columns = df.columns.map('_'.join)
    
    #Fix column names
    df = df.rename({'Year_Year': 'dummy_year',
                    'Level_Level': 'level',
                    'JLPT in Japan_Applicants': 'japan_applicants',
                    'JLPT in Japan_Examinees': 'japan_examinees',
                    'JLPT in Japan_Certified (%)': 'japan_certified',
                    'JLPT overseas_Applicants': 'overseas_applicants',
                    'JLPT overseas_Examinees': 'overseas_examinees',
                    'JLPT overseas_Certified (%)': 'overseas_certified'},
                   axis=1)
    
    # Remove text in [], (). Remove commas. Convert to int.
    df['japan_certified']    = df['japan_certified'].str.replace(r'([^)]*)', '', regex=True).str.replace(',', '').astype(int)
    df['overseas_certified'] = df['overseas_certified'].str.replace(r'([^)]*)', '', regex=True).str.replace(',', '').astype(int)
    df['dummy_year']         = df['dummy_year'].str.replace(r'[.*?]', '', regex=True)
    

    输出:

        dummy_year  level  ...  overseas_examinees  overseas_certified
    0         2007  1 kyū  ...              110937               28550
    1         2007  2 kyū  ...              152198               40975
    2         2007  3 kyū  ...              113526               53806
    3         2007  4 kyū  ...               53476               27767
    4         2008  1 kyū  ...              116271               38988
    ..         ...    ...  ...                 ...                 ...
    127     2022-1     N1  ...               49223               17282
    128     2022-1     N2  ...               54542               25677
    129     2022-1     N3  ...               41264               21058
    130     2022-1     N4  ...               40120               19389
    131     2022-1     N5  ...               30203               16132
    

    【讨论】:

      猜你喜欢
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多