使用find_all('tr')爬取时为何会获取到表格表头?
维基百科标普500页面爬取:包含表头行的问题及解决
问题描述
作为网页爬取新手,我从维基百科标普500页面提取数据,使用
.find_all('tr')查找表格条目,但第一个匹配结果却是表格表头。我原本以为表头需要通过查找<th>标签获取。我的临时解决方法是将第一个条目加入跳过列表,并添加if语句进行过滤,代码如下:url = 'https://en.wikipedia.org/wiki/List_of_S%26P_500_companies' response = requests.get(url, headers=headers) soup = BeautifulSoup(response.content, 'lxml') table = soup.find('tbody') trs = table.find_all('tr') symbol_names = [] skip = ['Symbol'] for tr in trs: symbol = tr.a.text if symbol not in skip: symbol_names.append(symbol)想请教为何会出现这种情况?我哪里操作有误?
原因分析
这是因为维基百科的表格结构里,表头行本身就是一个<tr>元素,它的内部单元格用<th>标签定义,但整个表头行依然属于<tbody>下的子元素。你用table.find_all('tr')会把所有<tbody>内的<tr>都提取出来,自然包括表头那一行。
另外你之前的误区是:表头不是只能通过<th>获取,<th>只是表头的单元格标签,整个表头行还是由<tr>包裹的,所以它会出现在<tr>的搜索结果里。而且这个表头行的<th>里恰好也有<a>标签("Symbol"是带链接的),所以你能通过tr.a.text拿到表头文本,才需要额外过滤。
更合理的解决方法
没必要用跳过列表这种依赖文本内容的方式,直接从结构上区分表头行和数据行更可靠:
直接跳过第一行
因为表头固定是<tbody>下的第一个<tr>,可以直接切片去掉:trs = table.find_all('tr')[1:] # 从索引1开始取,跳过第一个表头行 symbol_names = [] for tr in trs: symbol = tr.a.text symbol_names.append(symbol)通过标签判断表头行
检查当前<tr>是否包含<th>标签,包含的话就是表头行,直接跳过:trs = table.find_all('tr') symbol_names = [] for tr in trs: if tr.find('th'): continue symbol = tr.a.text symbol_names.append(symbol)直接定位数据行
数据行的单元格用<td>标签,所以可以直接找包含<td>的<tr>:symbol_names = [] # 直接找带有<td>的<tr>,也就是数据行 data_rows = table.find_all('tr', lambda tag: tag.find('td') is not None) for tr in data_rows: symbol = tr.a.text symbol_names.append(symbol)
内容的提问来源于stack exchange,提问作者M33G0
相关产品推荐
相关产品推荐

