掘金15W沸点简单分析(一)
一、另一种方式的爬虫
爬虫通常是:①得到目标网页URL;②发起HTTP请求得到网页数据;③使用各种方式解析网页得到想要的数据;
通常,在第②步,是不会去执行页面中的JS代码的。有些网站,会使用Ajax异步加载一些数据,然后再渲染到页面上的;或者使用JS对页面Dom做一些修改。这样都会导致第②步请求的页面中缺少、甚至是没有目标数据。这就需在获取到网页数据后,执行页面中的JS代码了。
最早是使用phantomjs+selenium。后面Chrome出了headless
模式,基本就一直使用Chrome了。处理逻辑大概:①请求获取网页,执行JS代码;②再将处理过的页面数据进行保存;③后续的处理(解析网页获取数据)。
1.1 Selenium使用示例
我们就以掘金征文为例,获取该文章下所有的评论。
注: 虽然可以直接通过接口获取,但我们假设无法直接获取数据,必须执行完JS才能得到目标数据。
使用Selenium + Chrome,首先需要下载与Chrome版本对应的ChromeDriver。
示例代码如下:
self.driver.get(self.article_url)
# 等待评论列表出现
WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, 'comment-list'))
)
self.save_page()
通过Selenium来操控Chrome加载网页时,通常会遇到这种问题:网络延迟,导致目标数据没有及时下载下来,但此时已将网页保存完毕。最简单的方式是,每次发生加载网页时,调用下time.sleep(5)
类似的方式,但这种方式虽然简单但却比较粗暴。更好的办法是使用Selenium提供的WebDriverWait
来处理。
官方文档,一定不要错过:selenium-python。
1.2 页面的后续处理
将渲染后的网页保存之后,接下来就要解析提取数据了。这次我们使用XPath
来解析数据。
还是先分析下网页
数据所处位置为://div[@class="comment-list-box"]/div[contains(@class, "comment-list")]/div[@class="item"]
。
为了处理方便,咱们仅获取第一级的评论用户和内容。
示例代码如下:
root = etree.HTML(page_source)
comments = root.xpath('//div[@class="comment-list-box"]/div[contains(@class, "comment-list")]/div[@class="item"]')
for comment in comments:
username = self.fix_content(comment.xpath('.//div[@class="meta-box"]//span[@class="name"]/text()'))
content = self.fix_content(comment.xpath('.//div[@class="content"]//text()'))
print(f'{username} --> {content}')
结果数据:
二、掘金沸点的获取
咱们直接看沸点的API接口。
接口分析:
沸点接口地址:https://apinew.juejin.im/recommend_api/v1/short_msg/hot 请求方式:POST 请求参数类型及格式:JSON格式数据,
{
cursor: "0", // 游标。 首次请求时为"0",请求响应中会含有该字段。后续请求直接使用即可
id_type: 4, // 沸点类别??(无关紧要)
limit: 20, // 分页大小
sort_type: 200 // 某种排序类型??(无关紧要)
}
然后我们就可以使用Python来模拟请求,获取沸点数据了。
我们使用requests来模拟请求,具体使用请看官方文档。
代码示例:
HOT_URL = 'https://apinew.juejin.im/recommend_api/v1/short_msg/hot'
json_form = {
'cursor': '0',
'id_type': 4,
'limit': 20,
'sort_type': 200,
}
resp = requests.post(HOT_URL, json=json_form)
print(resp.json())
# 数据可以正常返回
# {'err_no': 0, 'err_msg': 'success', 'data': [{'msg_id': '6864704084000112654', 'msg_Info': {'id': 980761, 'msg_id': '6864704084000112654', 'user_id': '2207475080373639', 'topic_id': '6824710203301167112', 'content': '别去找女朋友了,你的女朋友来了',
2.1 将所有数据保存下来
根据上面的示例,我们优化下代码:
def save_pins(idx=0, cursor='0'):
json_data = {
'id_type': 4,
'sort_type': 200,
'cursor': cursor,
'limit': 200 # 该数值无限制,但过大服务器报错,或者出现用户信息缺失等情况
}
resp = sess.post(url, json=json_data)
if resp.ok:
resp_json = resp.json()
with open(f'json_data/pins-{idx:04}.json', 'w+') as json_file:
json_file.write(resp.content.decode('UTF-8'))
# 是否还有更多
if resp_json['err_no'] == 0 and resp_json['err_msg'] == 'success':
logging.debug(f'no error, idx={idx}')
if resp_json['has_more']:
logging.debug(f'has more, next idx={idx+1}')
time.sleep(5)
save_pins(idx+1, cursor=resp_json['cursor'])
else:
# 出了异常
logging.warning(resp_json['err_msg'])
logging.debug(f'sleep 10s, retry idx={idx}')
time.sleep(10)
save_pins(idx, cursor)
部分数据如下:
三、后记
至此,整个热点数据获取已经结束。由于是直接请求接口返回的数据,除了部分数据重复外,几乎不用做任何数据处理。
3.1 后续处理
计划将数据简单处理、存储至MySQL数据库,然后使用superset
制作图表。
3.2 针对掘金热点的建议
- 对分页
limit
做限制,最大值限制为:20? - 针对未登录的情况,对可见数据量进行限制。
- Oracle dbms_random随机函数包
- volatile和Synchronized区别
- Oracle 快速插入1000万条数据的实现方式
- HashMap实现原理分析
- Oracle TM锁和TX锁
- Oracle给Select结果集加锁,Skip Locked(跳过加锁行获得可以加锁的结果集)
- select for update和select for update wait和select for update nowait的区别
- Android入门之动画
- Java 读写大文本文件
- 高级聚类
- Oracle 数据库名、实例名、Oracle_SID
- Oracle 多行、多列子查询
- Android入门介绍
- Oracle 数据表的管理
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【redis6.0.6】深入源码,一探究竟 -- redis服务器开机自启动
- 不是你记忆中的单例模式,但适用的程度,更胜一筹
- 安卓模拟器修改hosts文件
- 为容器化的 Postgres 数据库启用 ssl 连接
- 【SpringBoot web-1】web项目数据校验
- 【SpringBoot web-2】web项目参数传递
- 筛法求素数
- redis学习(十三)
- 【SpringBoot web-3】web项目统一数据封装与全局异常处理
- 送你 8 张图,好好理解一下。
- 【前端系列-1】ajax与Springboot通信将数据库数据渲染到前端表格
- 【前端系列-2】layui+springboot实现表格增删改查
- 容器技术|Docker三剑客之docker-swarm
- 【前端系列-3】layui表格使用自定义模板templet
- 【前端系列-4】layui表格集成select选择框和switch开关