这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战
0x1、引言
无独有偶,跟上节《Van ♂ Python | 某站点课程的简单爬取》一样,爬取的原因都是付费服务即将过期 (根本原因还是贫穷T﹏T)
技术方案同样是: 点点点 + 抓包,本节顺带试试想用很久的自动化神器 → Pyppeteer
严正声明:
本文仅用于记录爬虫技术研究学习,不会提供直接爬取脚本,所爬数据已删且未传播。请勿用于非法用途,如有其它非法用途造成损失,于本文无关。
0x2、Pyppeteer速成
1、与Puppeteer的渊源?
Puppeteer
是Google官方出品的通过DevTools协议口控制 Headless Chrome
的 NodeJS 库,通过其提供的API可直接控制Chrome模拟大部分用户操作,来进行UI Test、爬虫访问页面采集数据。Pyppeter
可以理解为 puppeteer
的python版本。
2、与Selenium相比?
与Selenium库相比,Pyppeteer无需繁琐的环境配置,在首次运行时会检测是否按照Chromium,未安装程序会帮我们自动安装和配置。而且Pyppeteer基于Python的新特性async实现(Python 3.5以上),故它的一些执行也支持异步操作,相比之下效率提高了不少。
3、API文档
- 官方仓库:github.com/pyppeteer/p…
- 官方文档:pyppeteer.github.io/pyppeteer/r…
- 官方文档(中文-puppeteer):github.com/zhaoqize/pu…
4、Puppeteer架构图
简述 (了解就行,不用记):
Puppeteer
:通过 DevTools协议 与浏览器进行通信;Browser
:可持有浏览器上下文;BrowserContext
:定义了一个浏览器会话,并可拥有多个页面;Page
:至少有一个框架,主框架;Frame
:至少有一个执行上下文,默认的执行上下文(框架的JavaScript)被执行;Worker
:具有单一执行上下文,切便于与WebWorkers进行交互;
5、Pyppeteer安装
- Step 1:pip安装pyppeteer
1 | bash复制代码pip install pyppeteer |
- Step 2:安装chromium
随手写个用到pyppeteer库的简单程序,跑一下,会自动下载,比如生成掘金首页截图的脚本:
1 | python复制代码import asyncio |
当然,谷歌源,下载起来不一定顺畅,有时可能卡住不动,可利用淘宝源,下载压缩包解压,然后在 launch() 时指定 executablePath,方法如下:
1 | bash复制代码# ① 获取原下载地址 |
附:代码流程解析
1 | python复制代码async # 声明一个异步操作 |
关于API就了解这些,更多的可自行查阅官方文档,或者善用搜索引擎,接着分析下爬取流程。
0x3、数据爬取
① 访问登录页
访问登录页:未登录 → 会显示下述登录面板,已登录 → 自动跳转至主页。
爬取流程
- 请求登录页,判断是否有登录二维码结点,有的话休眠10s等待扫码登录;
- 没有二维码,说明进入主页;
- 上述两步都进入分组获取;
② 分组获取
左侧面板可以看到:创建/管理的星球 及 加入的星球:
F12看下结点:
爬取流程
- 通过selector选择器定位到两处结点,获取所有的星球名及链接,输出供用户选择想爬取的星球;
- 附选择器示例:div.created-group > div:nth-child(2) > a
③ 内容爬取
一开始只是想爬下 精华 分类的,后面发现有的星球可能是像这样没数据:
索性就直接爬全部,内容列表做的分页,滚动到底部再加载更多,Ajax无疑了,在不尝试破解接口规则的情况下,最简单获取数据的方式莫过于:模拟滚动 + 解析节点 的形式了。
但在这个场景,解析节点的效率太低了,标签+图文+链接的搭配样式太多了,需要写很多解析规则,而采用 拦截特定请求的方式 就更无脑和高效一些了。
接着看下这个ajax请求的特点,如组成url,等下过滤请求用到,打开Network选项卡,清空,选中XHR,网页滚动到底部,看下加载的请求:
打开看下,确定是所需数据,拦截到这样的请求,把数据保存到本地,最后再统一进行批处理。
④ 确定滚动何时停止的两种思路
一直向下滚动,需要确定数据何时爬完,停止滚动,说下笔者的两个思路:
- 方法一:死循环 + asyncio.sleep() + pyppeteer查找底部结点
就是死循环,一直去检查底部结点是否可见,如此站点滑动到底部:
1 | ini复制代码<div _ngcontent-isv-c98="" class="no-more">没有更多了</div> |
- 方法二:js定时器 + 滑动距离与高度判断
就是开启开启一个定时器,记录滚动距离与当前页面高度,比较前者>=后者时,就可能滑动到底部。
对,是可能,因为存在列表没load的情况,所以可以加入一个重试次数,当重试次数达到阈值时才算完成。
Tips:笔者使用的方法二,以为对js语法不了解,不知道怎么暂停和启动一个计时器,所以把重试阈值设置得很大,也算间接实现休眠。
⑤ 初始化浏览器
流程摸清楚了,接着开始写代码实现爬取,先初始化浏览器:
1 | python复制代码import asyncio |
⑥ 新建页面
接着通过browser.newPage()来新建一个浏览器页面,除了常规设置外,还添加防WebDrivder检测~
1 | python复制代码# 新建页面 |
⑦ 登陆与拉取星球列表
利用page.waitForSelector()设置超时,检测是否有登陆二维码结点,执行后续逻辑:
1 | python复制代码# 登录 |
运行结果如下:
⑧ 拦截请求、响应和数据保存
1 | python复制代码# 拦截请求 |
⑨ 无限滚动
1 | python复制代码# 拉取内容 |
最后调用下:
1 | python复制代码if __name__ == '__main__': |
运行后可以看到控制台输出对应的爬取信息:
也可以看到爬取到本地的json文件:
呦西,数据都保存到本地了,接着到数据处理环节~
0x4、数据处理
① 关键数据提取
随手打开几个爬取的json样本,很好看出json中的关键部分:
定义出提取实体类:
1 | python复制代码class Talk: |
接着就是遍历文件,json.loads转成dict,按需拿字段,非常简单:
1 | python复制代码import cp_utils |
遍历10个文件试试效果:
打开json文件看看:
② 将json转成Markdown
json肯定不是和便于阅读的,可以生成一波Markdown,拼接字符串而已,这里的主要难点是:
text的解析,有标签、普通文本、外部链接、表情…
可以通过 re.sub() + 反向引用 替换一波标签和外部链接,写个测试代码试试水:
1 | python复制代码 # 替换正则 |
看下解析结果:
Good,接着补全图片及文件相关:
1 | python复制代码# 转换成md文件 |
细心的你可能发现了,代码中把图片给download下来了,并没有采用远程图片的方式,原因是站点图片资源url,没有图片后缀名,Markdown语法识别不了,导致预览时图片显示不出来。生成后的md文件:
46257个字符,PyCharm打开预览直接卡死,MarkdwonPad2也难逃一劫,滚一下卡一下,还是有必要分成几个md文件存~
0x5、小结
本文借着爬某星球的契机,把Pyppeteer的用法过了一波,爬虫技巧 Level Up↑,另外,文件类这里只存了一个id,真实下载地址还得调用另外的接口获取,而且还得处于登录态,感兴趣的同学可自行尝试一波。
好的,就说这么多,有问题欢迎评论区指出,感谢~
参考文献:
- Pyppeteer入门及中文教程
- Pyppetter - 你的自动化利器!
- 爬虫系列之Pyppeteer:比selenium更高效的爬虫界的新神器
- (最新版)如何正确移除 Pyppeteer 中的window.navigator.webdriver
- puppeteer自动化测试系列之三—端对端测试中常用的 Puppeteer 操作
本文转载自: 掘金