如何批量采集网页表格数据?
本文的视频讲解已经上传到千聊平台 凡购买199元系列课的童鞋可直接免费观看学习
Comming Soon! Python&Stata数据分析课寒假工作坊
我们最想要的数据格式就是表数据,但这表格并不是excel文件,而是存在于网页上的表数据。比如本教程实验网站
http://s.askci.com/stock/a
一般遇到这种数据,最简单的方法就是复制粘贴,但是当
复制粘贴格式混乱
粘贴工作量太多 就需要写爬虫帮我们处理这件事情。但是常见的爬虫教程没有详细教大家怎么处理这种网址,今天我就简单说下办法。
审查网页
我们可以发现
视图(平常肉眼所见)与html(图片下方的html)的一一对应对关系
html中有
<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>
: 定义表格单元
看到table这样的表格数据,我们可以利用pandas模块里的read_html函数方便快捷地抓取下来。
爬虫思路
寻找表格网页对应的url的网址规律
试图对某一个表格网页url进行访问
定位到所想要的table部分,得到
<table>...</table>
字符串table_str使用pd.readhtml(tablestr),得到一个df
循环2-4,得到的dfs(存放多个df的列表)
多个df合并为新的alldf(alldf为dataframe)结果保存到csv文件中, alldf.to_csv(filename)
实战
1. 网址规律
先尝试点击翻页,看看网址栏中url变化
我们很幸运的发现网址前后有变化
http://s.askci.com/stock/a/0-0?reportTime=2019-09-30&pageNum=2#QueryCondition
http://s.askci.com/stock/a/0-0?reportTime=2019-09-30&pageNum=3#QueryCondition
所以网址规律
http://s.askci.com/stock/a/0-0?reportTime=2019-09-30&pageNum={pageNum}#QueryCondition
2. 尝试对任意页面(page2)进行访问
尝试对第二页进行访问,只要 肉眼见到 和 html中的数据 对应上,就证明访问是正常的。
这里用 深圳市特力(集团)股份有限公司来验证
这次很幸运,因为requests.get(url)访问时并没有伪装成浏览器,网站也没有封锁这个python爬虫。我们继续
3. 定位table数据
如图,F12打开开发者工具, 我们定位到 id=myTable04
的table。
from bs4 import BeautifulSoup
import pandas as pd
bsObj = BeautifulSoup(resp.text)
table = bsObj.find('table', {'id': 'myTable04'})
table
Run
<table cellpadding="0" cellspacing="0" class="fancyTable" id="myTable04">
<thead>
<tr>
<th>序号</th>
<th>股票代码</th>
<th>股票简称</th>
<th>公司名称</th>
<th>省份</th>
3. table_str2dataframe
使用pd.readhtml(tablestr)将table字符串变为dataframe
table_str = str(table)
df = pd.read_html(table_str)
df
Run
[ 序号 股票代码 股票简称 公司名称 省份 城市 主营业务收入(201909) 净利润(201909) \
0 21 25 特力A 深圳市特力(集团)股份有限公司 深圳市 罗湖区 4.26亿 6541.11万
1 22 26 飞亚达A 飞亚达(集团)股份有限公司 深圳市 南山区 27.40亿 1.79亿
2 23 27 深圳能源 深圳能源集团股份有限公司 深圳市 福田区 157.80亿 19.40亿
3 24 28 国药一致 国药集团一致药业股份有限公司 深圳市 福田区 388.76亿 11.27亿
4 25 29 深深房A 深圳经济特区房地产(集团)股份有限公司 深圳市 罗湖区 19.03亿 4.40亿
员工人数 上市日期 招股书 公司财报 行业分类 \
0 337 1993-06-21 -- NaN 汽车销售
1 5162 1993-06-03 -- NaN 珠宝首饰
2 6649 1993-09-03 -- NaN 火电
3 24821 1993-08-09 -- NaN 医药商业
4 1954 1993-09-15 -- NaN 房地产开发
产品类型 \
0 主营业务:汽车销售、汽车检测维修及配件销售、物业租赁及服务
1 主营业务:主要从事钟表及其零配件的设计、开发、制造、销售和维修业务,包括"飞亚达"表的产品经...
2 主营业务:各种常规能源和新能源的开发、生产、购销,以及城市固体废物处理、城市燃气供应和废水处理等
3 主营业务:药品的研发、生产,中西成药、中药材、生物制品、生化药品、保健品、医疗器械的批发销售...
4 主营业务:商品住宅销售、物业租赁及管理服务、酒店客房服务,以及建筑安装装修服务。
主营业务
0 汽车销售、汽车检测维修及配件销售、物业租赁及服务
1 主要从事钟表及其零配件的设计、开发、制造、销售和维修业务,包括"飞亚达"表的产品经营和世界名...
2 各种常规能源和新能源的开发、生产、购销,以及城市固体废物处理、城市燃气供应和废水处理等
3 药品的研发、生产,中西成药、中药材、生物制品、生化药品、保健品、医疗器械的批发销售和连锁零售。
4 商品住宅销售、物业租赁及管理服务、酒店客房服务,以及建筑安装装修服务。 ]
跟我们设想的不太一样,结果不是dataframe应该有的样子。经过谷歌和百度,解决办法如下
df = pd.read_html(str(table))[0]
df.head()
4. 完整代码
重复操作
根据url规律,对多个表格url网页进行批量访问
重复之前对操作,得到dfs(存放多个df的列表)
将dfs转化为名为alldf的dataframe,并alldf.to_csv
import requests
import pandas as pd
from bs4 import BeautifulSoup
#伪装为浏览器
headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"}
#df列表容器,存放每个表格df
dfs = []
#一共有185个页面,这里仅仅就前10页进行测试
template = 'http://s.askci.com/stock/a/0-0?reportTime=2019-09-30&pageNum={pageNum}#QueryCondition'
for page in range(1, 11):
print(page)
url = template.format(pageNum=page)
resp = requests.get(url, headers=headers)
#定位table,得到table字符串
bsObj = BeautifulSoup(resp.text)
table_str = str(bsObj.find('table', {'id': 'myTable04'}))
df = pd.read_html(table_str)[0]
dfs.append(df)
#将dfs转为alldf,注意dfs是列表,而alldf是dataframe
alldf = pd.concat(dfs)
#输出为csv
alldf.to_csv('data.csv')
1
2
3
4
5
6
7
8
9
10
5. 采集结果
参数学习
pandas.read_html(io,
header=None,
skiprows=None,
attrs=None,
parse_dates=False,
encoding=None,
converters=None)
常用的参数:
io: 实际上io这个位置不仅可以是table字符串,还可以是表格的url、表格的html文本、或者本地文件等;但建议爬在本案例中用table_str而不是url。网址不接受https,尝试去掉s后爬去
header:标题所在的行数;
skiprows:跳过的行;
attrs:表格属性,对指定复合attrs值的表格进行解析,比如
attrs={‘id’:‘xxx’}
解析id为xxx的tableparse_dates:是否解析日期,默认不解析
encoding: 文件编码格式,常见的有 utf-8、 gbk等,默认为None
converters: 字典格式,指定某一列按照某一类型数据去处理。如converters={'股票代码': str},对股票代码这一列按照字符串(而不是数字)方式处理
近期文章
课件获取方式,公众号后台回复关键词“20191229”
如果想做文本分析