【问题标题】:Scrape a Wikipedia table using beautifulsoup使用 beautifulsoup 抓取维基百科表格
【发布时间】:2020-05-29 08:36:46
【问题描述】:

我一直在尝试使用 Beautifulsoup 在 Wikipedia 上抓取表格,但遇到了一些问题。

页面:https://en.wikipedia.org/wiki/New_York_City 桌子: enter image description here

表格:“种族构成”

在页面源中,表格似乎从第 1470 行开始。

这是我首先尝试的代码:

website_url = requests.get('https://en.wikipedia.org/wiki/New_York_City').text
soup = BeautifulSoup(website_url,'lxml')
table = soup.find('table',{'class':'wikitable sortable collapsible'})

headers = [header.text for header in table.find_all('th')]

table_rows = table.find_all('tr')        
rows = []
for row in table_rows:
   td = row.find_all('td')
   row = [row.text for row in td]
   rows.append(row)

with open('NYC_DEMO.csv', 'w') as f:
   writer = csv.writer(f)
   writer.writerow(headers)
   writer.writerows(row for row in rows if row)

这是错误:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-24-e6000bdafe11> in <module>
      3 table = soup.find('table',{'class':'wikitable sortable collapsible'})
      4 
----> 5 headers = [header.text for header in table.find_all('th')]
      6 
      7 table_rows = table.find_all('tr')

AttributeError: 'NoneType' object has no attribute 'find_all'

我想这是我们需要从维基百科页面获取的代码:

<tbody><tr>
<th>Racial composition</th>
<th>2010<sup id="cite_ref-QuickFacts2010_226-1" class="reference"><a href="#cite_note-QuickFacts2010-226">&#91;224&#93;</a></sup></th>
<th>1990<sup id="cite_ref-pop_228-0" class="reference"><a href="#cite_note-pop-228">&#91;226&#93;</a></sup></th>
<th>1970<sup id="cite_ref-pop_228-1" class="reference"><a href="#cite_note-pop-228">&#91;226&#93;</a></sup></th>
<th>1940<sup id="cite_ref-pop_228-2" class="reference"><a href="#cite_note-pop-228">&#91;226&#93;</a></sup>
</th></tr>
<tr>
<td><a href="/wiki/White_American" class="mw-redirect" title="White American">White</a></td>
<td>44.0%</td>
<td>52.3%</td>
<td>76.6%</td>
<td>93.6%
</td></tr>
<tr>
...

我猜它找不到正确的表?该页面上有很多表格,那么我如何正确指向该表格?

提前感谢您的帮助。

【问题讨论】:

标签: python dataframe beautifulsoup wikipedia


【解决方案1】:

问题是它不会返回带有class="wikitable sortable collapsible" 的表,因为它没有在 html 中明确显示。您将需要使用正则表达式来查找包含该子字符串的类,因为这样会起作用。其次,.find() 只会返回它找到的第一个元素。除非您尝试抓取的表具有特定且唯一的属性来识别它,否则使用.find() 将不起作用。如果有多个元素,您需要使用.find_all(),即使这样您也需要遍历这些元素以获得您想要的表格。

正如有人所说,您也可以使用 pandas 的.read_html()。这将返回列表中的所有表格标签,然后找到您想要的表格的索引位置。我为您提供了两个选项:

使用熊猫:

import pandas as pd

url = 'https://en.wikipedia.org/wiki/New_York_City'

df = pd.read_html(url)[9]
df.to_csv('NYC_DEMO.csv',index=False)

使用 BeautifulSoup:

import requests
from bs4 import BeautifulSoup

url = 'https://en.wikipedia.org/wiki/New_York_City'
website_url = requests.get(url).text
soup = BeautifulSoup(website_url,'html.parser')
tables = soup.find_all('table')
for table in tables:
    if 'Racial composition' in table.text:
        headers = [header.text.strip() for header in table.find_all('th')]
        rows = []
        table_rows = table.find_all('tr')    
        for row in table_rows:
           td = row.find_all('td')
           row = [row.text for row in td]
           rows.append(row)

df = pd.DataFrame(rows, columns=headers)       

输出:

print (df)
                 Racial composition 2010[224] 1990[226]   1970[226] 1940[226]
0                             White     44.0%     52.3%       76.6%     93.6%
1                     —Non-Hispanic     33.3%     43.2%  62.9%[227]     92.0%
2         Black or African American     25.5%     28.7%       21.1%      6.1%
3  Hispanic or Latino (of any race)     28.6%     24.4%  16.2%[227]      1.6%
4                             Asian     12.7%      7.0%        1.2%         –

【讨论】:

    【解决方案2】:

    我猜它找不到正确的表?

    似乎是这样,是的。如果你检查table 的值,你会看到它是None,这就是为什么调用find_all 失败的原因。

    如果您检查页面上的表格,您会看到它的类是 wikitable collapsible collapsed mw-collapsible mw-made-collapsible,并且其中没有 sortable 类。这就是为什么您的程序找不到任何匹配的 table 元素的原因。

    该页面上有很多表格,那么我如何正确指向该表格?

    首先,您可以连接到某个唯一标识符,例如元素的 id,但在您的情况下没有可用的标识符。如果它有任何 thead 或某种标题,您可以尝试使用它,但同样情况并非如此。

    然后,您需要更深入地查看 DOM 树,并检查其父级是否有任何唯一标识符。计划是在选择器中添加父级。不幸的是,维基百科文章的主体似乎只包含在一个大元素中,没有在语义上分隔各个部分。这使得它更难刮。

    此时,我想说您只需要查看浏览器页面并考虑如何自然地识别表格(非编程方式)。你看看它,发现它的标题中有种族构成。你可以用类似的东西来抓住它

    table_heading = soup.find('th', text='Racial composition')      # this gives you the `th`
    if table_heading:
        table = table_heading.find_parents('table')
    

    可能还有其他一些我不知道的beautifulsoup API,但您可以将其放入您的代码中,它应该可以工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-30
      • 1970-01-01
      • 2020-07-16
      • 2020-07-08
      • 2019-05-24
      • 1970-01-01
      • 1970-01-01
      • 2016-09-08
      相关资源
      最近更新 更多