常见的各种官网都有这样一种情况:
网站中很多表格
,我们想对这些表格进行
整理汇总
、或者是
筛选
,或者是
处理分析
。
于是我们需要考虑:如何将网页表格数据使用python保存为Excel文件?
-
如果需要抓的表格很少或只需要抓一次,那么推荐
快速抓取法
[引文1]
。 -
如果页数比较多,推荐
完整爬虫抓取法
[引文2]
。解析函数用了BeautifulSoup和css选择器,这种方法定位提取表格所在的id为#myTable04的table代码段,更为准确。
0 基础知识
table类型的表格网页结构
一般情况下,网页的表格数据主要是在
<table>
标签中,
<table>
类型的表格网页结构大致如下:
<table class="..." id="...">
<thead>
<tr>
<th>...</th>
</tr>
</thead>
<tbody>
<tr>
<td>...</td>
</tr>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
...
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
</tbody>
</table>
简单解释上文出现的几种标签含义:
<table> : 定义表格
<thead> : 定义表格的页眉
<tbody> : 定义表格的主体
<tr> : 定义表格的行
<th> : 定义表格的表头
<td> : 定义表格单元
使用pandas的
read_html()
方法就可以读取标签中的内容。
read_html()函数
pandas.read_html(io,
match='.+',
flavor=None,
header=None,
index_col=None,
skiprows=None,
attrs=None,
parse_dates=False,
tupleize_cols=None,
thousands=', ',
encoding=None,
decimal='.',
converters=None,
na_values=None,
keep_default_na=True,
displayed_only=True)
常用的参数:
- io:可以是url、html文本、本地文件等
- flavor:解析器;
- header:标题行;
- skiprows:跳过的行;
- attrs:属性,比如 attrs = {‘id’: ‘table’};
- parse_dates:解析日期
注意:返回的结果是
DataFrame
组成的
list
。
1 快速抓取法
1.1 思路
这里以
NBA Player Salaries – 2020-2021
为例,具体步骤如下:
-
step0:查看网页元素。
在目标网页中,右键“审查元素”,发现是
<table>
类型的网页结构,可以用
read_html()
抓取。
-
step1:确定抓取数据量和网页类型。
表数据共14页,数据量不大;且1-14页的网址只有page的变化,属于静态网页。 -
step2:代码思路。
核心思路:使用pandas库中的
read_html()
函数,采用
快速抓取法
完成网页表格数据的收集。建立空白DataFrame结构 – 建立爬取网址合集urls – 依次爬取每一页的表格数据并append – 导出最终数据到csv。
1.2 代码
# 导入库
import pandas as pd
# 建立空白DataFrame
df=pd.DataFrame()
# 建立爬取网址合集urls
url_ori='http://www.espn.com/nba/salaries/_/page/'
urls=[url_ori+str(i) for i in range(1,15)]
# 依次爬取每一页的表格数据并append
for i,url in enumerate(urls):
print(i+1)
df=df.append(pd.read_html(url),ignore_index=True)
df.shape
# 数据筛选,ignore_index没有删除数据行中夹杂的标题行
df=df[df[0]!='RK'][[1,2,3]].reset_index(drop=True)
# 其他处理:
# df[1].apply(lambda x:x.split(',')[0])
# df[3].apply(lambda x:x[1:])
# 数据导出到CSV
df.to_csv(r'd:/desktop/NBA Player Salaries-2020-2021.csv',header=['NAME','TEAM','SALARY'],index=False)
2 完整爬虫抓取法
2.1 思路
这里以
中商情报网
为例,具体步骤如下:
-
step0:查看网页元素。
在目标网页中,右键“审查元素”,发现是
<table>
类型的网页结构,可以用
read_html()
抓取。
-
step1:确定抓取数据量和网页类型。
表数据共208页,数据量较大;且1-208页的网址只有pageNum的变化,属于静态网页。 -
step2:代码思路。
核心思路:使用pandas库中的
read_html()
函数,采用
完整爬虫抓取法
完成网页表格数据的收集。将整个爬取分为网页提取、内容解析、数据存储等步骤,依次建立相应的函数 – 主函数建立空白DataFrame结构 – 依次爬取每一页的表格数据并append – 导出最终数据到csv。
2.2 代码
import pandas as pd
import requests
from bs4 import BeautifulSoup
from lxml import etree
from urllib.parse import urlencode # 编码 URL 字符串
import time
start_time = time.time() #计算程序运行时间
# 网页提取
def get_one_page(i):
try:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'}
paras = {
'reportTime': '2017-12-31',#可以改报告日期,比如2018-6-30获得的就是该季度的信息
'pageNum': i #页码
}
# https://s.askci.com/stock/a/?reportTime=2017-12-31&pageNum=1
# urlencode(paras)的值为:reportTime=2017-12-31&pageNum=1
url = 'http://s.askci.com/stock/a/?' + urlencode(paras)
response = requests.get(url,headers = headers)
if response.status_code == 200:
return response.text
return None
except RequestException:
print('爬取失败')
# 内容解析
def parse_one_page(html):
soup = BeautifulSoup(html,'lxml')
content = soup.select('#myTable04')[0] #[0]将返回的list改为bs4类型
tbl = pd.read_html(content.prettify(),header = 0)[0]# prettify()优化代码,[0]从pd.read_html返回的list中提取出DataFrame
tbl.rename(columns = {'序号':'serial_number',
'股票代码':'stock_code',
'股票简称':'stock_abbre',
'公司名称':'company_name',
'省份':'province',
'城市':'city',
'主营业务收入(201712)':'main_bussiness_income',
'净利润(201712)':'net_profit',
'员工人数':'employees',
'上市日期':'listing_date',
'招股书':'zhaogushu',
'公司财报':'financial_report',
'行业分类':'industry_classification',
'产品类型':'industry_type',
'主营业务':'main_business'},inplace = True)
return tbl
# tbl = pd.DataFrame(tbl,dtype = 'object') #dtype可统一修改列格式为文本
# 数据存储
def write_to_csv(df):
df.to_csv(r'd:\desktop\test.csv',index=False)
# 主函数
def main(page):
df=pd.DataFrame()
for i in range(1,page):
html = get_one_page(i)
tbl = parse_one_page(html)
df=df.append(tbl)
write_to_csv(df)
# 单进程
if __name__=='__main__':
# 爬取前n=3页的数据
n=3
main(n+1)
endtime = time.time()-start_time
print('程序爬了{}页的表格数据,运行了{:.2f}秒'.format(n,endtime))
3 小结
最后,需说明
不是所有表格都可以用这种方法爬取
。
比如这个网站中的表格,表面是看起来是表格,但在html中不是前面的
table
格式,而是
list
列表格式。这种表格则不适用
read_html
爬取。得用其他的方法,比如
selenium
。
引自:
[1]
关于python获取网页表格数据(read_html()方法)
[2]
利用pandas库中的read_html方法快速抓取网页中常见的表格型数据