网页里的信息很多,但表格是最特殊的一种——它天生就是结构化数据,一行一行、一列一列排好了,抓下来直接就能用,存成 CSV 打开就是整齐的表格。
今天教两种方法:
- 方法一:BeautifulSoup 手动解析——灵活,什么表格都能搞定
- 方法二:
pandas.read_html一键提取——懒人福音,一行代码
先看 HTML 表格长什么样
一个标准表格的结构:
<table><thead><tr><th>排名</th><th>电影名称</th><th>评分</th><th>年份</th></tr></thead><tbody><tr><td>1</td><td>肖申克的救赎</td><td>9.7</td><td>1994</td></tr><tr><td>2</td><td>霸王别姬</td><td>9.6</td><td>1993</td></tr></tbody></table>关键标签记一下:
| 标签 | 意思 |
|---|---|
<table> | 整个表格 |
<thead> | 表头部分 |
<tbody> | 表体,真正的数据在这 |
<tr> | 一行 |
<th> | 表头单元格 |
<td> | 普通数据单元格 |
抓表格的思路就一句话:先找到 table,一行一行读,每行再一格一格读。
方法一:BeautifulSoup 手动解析
基本思路
requests拿到网页 HTML- 找到目标
<table> - 提取表头
<th>当列名 - 遍历每行
<tr>,提取<td>内容 - 组装成 DataFrame
核心代码
frombs4importBeautifulSoupimportpandasaspd# 假设 html 是 requests.get(url).text 拿到的网页源码soup=BeautifulSoup(html,'lxml')table=soup.find('table',class_='movie-rank')# 找到目标表格# 第一步:提取表头headers=[]thead=table.find('thead')ifthead:header_row=thead.find('tr')headers=[th.get_text(strip=True)forthinheader_row.find_all('th')]else:# 没有 thead 就取第一行当表头first_row=table.find('tr')headers=[c.get_text(strip=True)forcinfirst_row.find_all(['th','td'])]# 第二步:提取数据行tbody=table.find('tbody')rows=tbody.find_all('tr')iftbodyelsetable.find_all('tr')[1:]data=[[c.get_text(strip=True)forcinrow.find_all('td')]forrowinrows]# 第三步:转成 DataFrame 并保存df=pd.DataFrame(data,columns=headers)print(df)df.to_csv('movie_rank.csv',index=False,encoding='utf-8-sig')两个小细节:
get_text(strip=True)自动去掉文字首尾的空格换行,表格数据干净很多- 没有
<thead>的表格,把第一行当表头兜底,不崩
方法二:pandas.read_html 一键提取
如果你能少写一行就绝不多写,这个方法你会爱上——一行代码,把页面所有表格全提取出来。
importpandasaspd# 就一行:直接从 URL 提取所有表格,返回 DataFrame 列表tables=pd.read_html('目标网页地址')print(f"页面上找到{len(tables)}个表格")df=tables[0]# 取第一个表格print(df.head())不用 BeautifulSoup,不用遍历 tr/td,pandas 全包了。
注意事项(坑)
1. 返回的是列表,不是单个 DataFrame。页面上可能有好几个表格,打印每个的行数列数来判断哪个是你要的。
2. 需要装 lxml:
pipinstalllxml3. 只能抓静态表格。JS 动态加载的表格,read_html抓不到(后面会讲怎么处理动态网页)。
4. 表头识别可能不准,手动指定:
tables=pd.read_html(url,header=None)df=tables[0]df.columns=['排名','名称','评分','年份']两种方法怎么选?
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 表格规整、简单 | pandas.read_html | 快,几行代码 |
| 只要文字数据 | pandas.read_html | 省事 |
| 有合并单元格 | BeautifulSoup 手动 | pandas 处理不了复杂结构 |
| 要提取链接、图片 | BeautifulSoup 手动 | read_html 只能拿文字 |
| 需要特殊清洗逻辑 | BeautifulSoup 手动 | 灵活可控 |
一句话:简单表格直接 pandas,复杂表格上 BeautifulSoup。
进阶技巧:表格里带链接一起抓
read_html只能拿到文字,拿不到链接。需要连网址一起抓下来(比如后续要爬详情页),用 BeautifulSoup:
forrowinrows:cells=row.find_all('td')link_tag=cells[1].find('a')# 第二列是链接movie_name=link_tag.get_text(strip=True)iflink_tagelsecells[1].get_text(strip=True)movie_url=link_tag.get('href')iflink_tagelse''data.append([cells[0].get_text(strip=True),movie_name,movie_url,cells[2].get_text(strip=True)])真实运行效果
两种方法在配套资源包里跑一遍:
方法一 BeautifulSoup 手动解析:表头、5 行数据全部提取,转成 DataFrame 并存入 CSV;方法二read_html同样一行拿到整个表格;进阶的带链接提取也正常。三种玩法全跑通。
今天学了什么
- HTML 表格结构:
table > thead/tbody > tr > th/td - 手动解析:BeautifulSoup 找 table → 提表头 → 遍历行 → 转 DataFrame
- 一键提取:
pd.read_html(url)返回表格列表,一行搞定 - 选择标准:简单用 pandas,复杂用 BeautifulSoup
- 编码存 CSV:
utf-8-sig,Excel 打开不乱码
完整可跑工程版(模拟电影排行榜实战 + read_html + 带链接进阶提取)在配套资源包里。
下一篇讲数据批量保存:怎么存成 CSV,怎么翻页一次抓好几页。
梅雅达编程工作室 · Python数据实战系列第3篇
简单表格直接 pandas,复杂表格上 BeautifulSoup——记住这一句,九成场景都不会慌。