小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
前言
本文将介绍通过抓包的方式,获取使用js进行动态渲染的网站数据爬取,并以股票信息数据为例,进行实战操作。
股票信息采集
要求:用requests和自选提取信息方法定向爬取股票相关信息,并存储在数据库中。
思路:
1. 抓包
由于该网页属于动态渲染网页,数据通过ajax进行动态渲染,因此需要进行抓包。
打开F12并刷新,获取浏览器发送的js请求。
如下进行解析:
1. 随便复制一个股票名称;
2. 启动搜索,粘贴复制的股票名称;
3. 搜索栏中出现一个(或多个 -> 随着时间会进行额外的数据刷新请求)含有该页所有股票代码信息的请求接口;
4. 右侧Response中可以预览到所有数据。
2. 参数解析
上步中获取到的接口url如下:
http://67.push2.eastmoney.com/api/qt/clist/get?cb=jQuery112407972169804676412_1634974778877&pn=1&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&fid=f3&fs=m:0+t:6,m:0+t:80,m:1+t:2,m:1+t:23&fields=f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f13,f14,f15,f16,f17,f18,f20,f21,f23,f24,f25,f22,f11,f62,f128,f136,f115,f152&_=1634974778878
复制代码
可以看到,在url后,跟着很多get参数,可以对参数进行修改来完善爬虫。
因此我进行了一些尝试(请求经过验证,均与实际页面相匹配):
首先,最先看到的是pn和pz两个参数。
在后端编写接口中,一般都会使用page_num和page_size对应页码和分页大小,pn和pz则是这两个变量的缩写。
然后是cb=jQuery.......参数。
jQuery是基于js的一个框架,虽然对前端这块不太了解,但是一般情况,前后端分离项目需有一定的请求规范,而爬取到的接口细看是在Restful API的基础上添加一层jQuery函数封装。
可以大胆推测,该接口是专门对jQuery进行的适配,而用于控制这个适配可能性最高的参数就是cb参数(因为里面出现了jQuery)。
删除后,接口数据使用json格式进行返回(这后端接口不规范):
很直观的看出,data中的diff对应是一个list类型数据,启动包含的 f* 就是我们所需要数据。 接着是数据范围限定
经过人工比对,各字段和参数名对应表如下:
| 股票代码 | f12 |
|---|---|
| 名称 | f14 |
| 最新报价 | f2 |
| 涨跌幅 | f3 |
| 涨跌额 | f4 |
| 成交量 | f5 |
| 成交额 | f6 |
| 振幅 | f7 |
| 最高 | f15 |
| 最低 | f16 |
| 今开 | f17 |
| 昨收 | f18 |
最后是其他一些未知作用的参数,可能是后端进行埋点使用,逐个进行筛选,去掉不必要参数,最后,参数添加如下:
# 需要的参数,其他的参数就不需要请求了
needParams = ['f12', 'f14', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f15', 'f16', 'f17', 'f18']
fields = ",".join(needParams) # 合并成fields参数
# 输入分页相关参数限定爬取数量和范围,fid为股票种类(f3为目的),fields限定返回参数
# 和爬取到的接口相比,去掉了cb=jQuery....参数,就可以直接返回json格式数据
url = f'http://60.push2.eastmoney.com/api/qt/clist/get' \
f'?pn={pageNum}' \
f'&pz={pageSize}' \
f'&po=1&np=1&fltt=2&invt=2' \
f'&fid={fid}&fs=m:1+s:2' \
f'&fields={fields}'
复制代码
(po=1&np=1&fltt=2&invt=2作用未知,但是删除将导致请求未空)
3. 数据库构建
db = DB()
db.driver.execute('use spider_test')
db.driver.execute('drop table if exists money')
sql_create_table = """ CREATE TABLE `money` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(64) DEFAULT NULL,
`name` varchar(64) DEFAULT NULL,
`zxbj` varchar(64) DEFAULT NULL,
`zdf` varchar(64) DEFAULT NULL,
`zde` varchar(64) DEFAULT NULL,
`cjl` varchar(64) DEFAULT NULL,
`cje` varchar(64) DEFAULT NULL,
`zf` varchar(64) DEFAULT NULL,
`high` varchar(64) DEFAULT NULL,
`low` varchar(64) DEFAULT NULL,
`jk` varchar(64) DEFAULT NULL,
`zs` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
"""
db.driver.execute(sql_create_table)
复制代码
原则上,数据库字段名不应该出现中文字符,因此,数据库命名看着有些随意。
插入操作和作业一中大同小异,这里就不再展示了。
复制代码
4. 结果展示
当前只创建一张数据表进行实验,如有实际需求需要使用按时间对表进行划分,不能把所有时间段数据杂在一张表内(不然没有意义)。
另外,仅保存源数据信息,方便数据分析或可视化操作,数据转义(加%或加万、亿等)不应该在数据库中进行。




近期评论