You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用BeautifulSoup提取网页表格数据?解决IndexError问题

问题:使用BeautifulSoup提取网页表格指标数据时触发IndexError

我用BeautifulSoup从网页抓取数据,初始代码如下:

import urllib2
from bs4 import BeautifulSoup
soup = BeautifulSoup(urllib2.urlopen('http://open.dataforcities.org/details?4[]=2016').read())

当前soup对象包含的目标表格片段如下:

<table>
<tr class="theme-cells" id="profile_indicators" ng-mouseover='updateIndicatorsScroll( "Profile Indicators" )'>
<td class="theme-text">
<h1>4 Profile Indicators</h1>
</td>
<td class="metrics">
<div class="metric-p metric-title"></div>
</td>
</tr>
<tr class="indicator-cells" ng-mouseover='updateIndicatorsScroll( "Profile Indicators" )' onmouseout="$(this).removeClass('indicator-cells-hover')" onmouseover="$(this).addClass('indicator-cells-hover')">
<td class="indicator-text">
<h2>4.1 Total city population (Profile)</h2>
</td>
<td class="metrics">
<div class="metric-p metric-title"></div>
<div class="metric-p-also bigger">669 469 (2015)</div>
<div class="full-bar" style="width:100%">
<div class="metric-bar" style="width:3.6411942141077174%; background-color:#ffffff"></div>
</div>
</td>
</tr>
<tr class="indicator-cells" ng-mouseover='updateIndicatorsScroll( "Profile Indicators" )' onmouseout="$(this).removeClass('indicator-cells-hover')" onmouseover="$(this).addClass('indicator-cells-hover')">
<td class="indicator-text">
<h2>4.2 City land area (Profile)</h2>
</td>
<td class="metrics">
<div class="metric-p metric-title"></div>
<div class="metric-p-also bigger">125 km² (2010)</div>
<div class="full-bar" style="width:100%">
<div class="metric-bar" style="width:1.9604120789229098%; background-color:#ffffff"></div>
</div>
</td>
</tr>
<tr class="indicator-cells" ng-mouseover='updateIndicatorsScroll( "Profile Indicators" )' onmouseout="$(this).removeClass('indicator-cells-hover')" onmouseover="$(this).addClass('indicator-cells-hover')">
<td class="indicator-text">
<h2>4.3 Population density (Profile)</h2>
</td>
<td class="metrics">
<div class="metric-p metric-title"></div>
<div class="metric-p-also bigger">5 354 /km² (2015)</div>
<div class="full-bar" style="width:100%">
<div class="metric-bar" style="width:27.890485963282238%; background-color:#ffffff"></div>
</div>
</td>
</tr>
<tr class="indicator-cells" ng-mouseover='updateIndicatorsScroll( "Profile Indicators" )'

我尝试按照《Web scraping with Python》里的示例提取数据,代码如下:

soup = BeautifulSoup(urllib2.urlopen('http://open.dataforcities.org/details?4[]=2016').read())
for row in soup('table', {'class': 'metrics'})[0].tbody('tr'):
    tds = row('td')
    print tds[0].string, tds[1].string

但触发了如下错误:

IndexError Traceback (most recent call last)
<ipython-input-71-d688ff354182> in <module>()
----> 1 for row in soup('table', {'class': 'metrics'})[0].tbody('tr'):
      2     tds = row('td')
      3     print tds[0].string, tds[1].string
IndexError: list index out of range

请问怎么正确从这个soup对象里提取表格里的指标数据?


解决方法

错误原因分析

你的代码有两个关键问题:

  1. 定位表格错误:metrics是<td>标签下<div>的class,不是<table>的class。目标表格的id是profile_indicators,应该用这个来定位表格。
  2. tbody标签问题:从你提供的HTML片段可以看到,表格里没有<tbody>标签——浏览器会自动补全,但BeautifulSoup解析原始HTML时不会添加这个标签,所以调用.tbody会返回None,进一步操作会出错。

正确的提取代码

下面是修正后的代码,能准确提取指标名称和对应数值:

import urllib2
from bs4 import BeautifulSoup

soup = BeautifulSoup(urllib2.urlopen('http://open.dataforcities.org/details?4[]=2016').read())

# 1. 通过id定位目标表格
table = soup.find('table', id='profile_indicators')

# 2. 遍历表格中所有class为indicator-cells的tr(排除标题行)
for row in table.find_all('tr', class_='indicator-cells'):
    # 提取指标名称:从td里的h2标签获取文本
    indicator_name = row.find('td', class_='indicator-text').h2.get_text(strip=True)
    # 提取指标数值:从td里的metric-p-also类div获取文本
    indicator_value = row.find('td', class_='metrics').find('div', class_='metric-p-also').get_text(strip=True)
    
    print(f"{indicator_name}: {indicator_value}")

代码解释

  • 定位表格:用find('table', id='profile_indicators')精准找到目标表格,避免匹配到其他无关表格。
  • 筛选行:用find_all('tr', class_='indicator-cells')只获取包含指标数据的行,跳过顶部的标题行(theme-cells类的tr)。
  • 提取文本:
    • 指标名称在indicator-text类的td下的<h2>标签里,用get_text(strip=True)去除多余的空格和换行。
    • 指标数值在metrics类的td下的metric-p-also类div里,同样用get_text(strip=True)清理文本。

运行这段代码后,你会得到清晰的指标数据输出:

4.1 Total city population (Profile): 669 469 (2015)
4.2 City land area (Profile): 125 km² (2010)
4.3 Population density (Profile): 5 354 /km² (2015)

内容的提问来源于stack exchange,提问作者emax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:40:20