如何用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对象里提取表格里的指标数据?
解决方法
错误原因分析
你的代码有两个关键问题:
- 定位表格错误:
metrics是<td>标签下<div>的class,不是<table>的class。目标表格的id是profile_indicators,应该用这个来定位表格。 - 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
相关产品推荐
相关产品推荐

