如何让ChatGPT帮你完成一个mini spider

311 阅读15分钟

 

前言

近期ChatGPT大火,被传的神乎其神,尤其夸赞其代码写的极其棒,刚好作者最近有个小需求需要整个小爬虫,既然ChatGPT这么牛那帮忙写个Spider应该不过分吧?

带着这个想法,作者和ChatGPT经过一段时间的友好沟通,ChatGPT很快就帮作者完成一个自动登录、自动获取岗位相关视频链接、多线程下载、进度管理、故障自愈等一系列功能的Spider,是不是有点好奇如何实现的? 本文将展开介绍这个mini spider的架构演进历程,看它如何从“最简单的视频下载器”,升级成“自动抓取视频URL并下载的Spider”,最终摇身一变成成“待进度管理、支持多线程、多类型Worker的高效Spider”。基础版到高配版的背后,是笔者利用ChatGPT持续优化、逐渐提效的过程,接下来我将详细介绍开发时的心路历程,希望与你共同探讨其中的挑战和解法~

 

效果演示

在探讨挑战和解法之前,还是有必要先让大家看下效果的。该项目采用Python实现,整体开发差不多一天功夫,代码90%ChatGPT生成,剩下的就是作者进行简单微调。废话不多说,接下来我们将一一揭秘。

image.png

image.png

image.png

 

 

ChatGPT初体验

在动手整爬虫前,我们得对源站进行基础分析,确认下我们要爬的资源在哪,以及好不好爬,如何去爬等等。下面是基础的分析步骤,如果有经验的同学可以直接跳过看step4即可。

 

step1: 开启chrome开发者工具(mac: command+option+i 快捷键)

image.png

 

step2: 刷新页面,观察开发者工具中的接口返回

image.png

  • 我们发现视频的列表其实在该接口中已经全部返回,并且有相关的sid
  • 既然我们有了视频列表的sid,如何转换成我们下载的资源呢?带着这个疑惑我们来到step3

 

step3: 点击视频列表中对应视频项,查看视频详情页

image.png

  • 我们发现视频网站是用step2中视频列表中的视频id作为查询条件
  • 该接口中会直接返回一个video url,前端识别该url进行视频播放

 

step4: 有了url不就好办多了,来祭出ChatGPT,我们开始动动嘴给ChatGPT提个需求

image.png

step5: 看着代码写的还不错,不知道跑起来会不会有bug?让我们把代码贴到Pycharm中跑下看看,貌似完美运行,视频下载完成后也没有损坏啥的。ok恭喜你到这已经完成一个基础爬虫了。

image.png

image.png

 

其实到这我们通过ChatGPT完成把视频下载下来的功能,但是前提是要我们去视频列表中一个个去扒url。这效率未免有点太低是吧~,再说这种重复的事情,爬虫肯定能比我们干的更好。那带着我们的需求开始将mini spider进行升级吧!

 

让ChatGPT升级Mini Spider

如何批量获取视频url

image.png

我们分析了整体的的交互流程,发现获取一个岗位下所有视频的url,需要走上图中的是三个接口才能完整的

 

如何解决自动登录

那我们知道获取url的逻辑后是不是直接访问可以获取到了呢?我们可以试试访问看看,得到下图提示我们未登录或身份信息已过期错误,这个错误是啥原因呢?

image.png

其实这就涉及到访问认证的问题,但背后的本质还是一个Cookie机制。

Cookie: 因为http是无状态服务,一旦客户端与服务端交换数据就会断连接,再次给发服务端发请求就得重新建链接,重新进行鉴权,如果每次都要传输账号密码来鉴权:一是不安全,二是用户体验极差,所以就弄了一个cookie机制。
这个Cookie就好比一个临时身份凭证,新用户进来时就给你颁发一个,下次访问时带上这个凭证即可。

 

那我们的Cookie 是咋获取的呢?

根据刚才的解释,我们回想下,我们来拿到账号密码后来访问网站要求我们干的第一件事是啥呀?登录!!!那我们抓下登录的接口看下不就可以么?果不其然,在返回的Set-Cookie header头中就有我们想要的Cookie。那我们就知道了,只要我们模拟登录后,把Cookie带给抓视频的模块即可了嘛~

image.png

 

给ChatGPT提需求

按照刚才的逻辑分析,我们可以很轻松得到以下需求

    • 模拟登录,实现自动登录获取Cookie,并可以将Cookie传递给后续环节使用
    • 给定一个岗位id,解析获取对应的课程列表,再调用获取课程下的视频列表接口获取对应的视频,最后调用视频详情列表获取对应的视频链接
    • 视频以mp4格式保存在 岗位-课程-章节 目录下

 

那我们按照上述步骤作为需求提给ChatGPT,ChatGPT会给什么反馈呢?

image.png

  • ChatGPT很给力的帮我们把模块直接抽象出来了,看着结果忍不住夸他一句还行嘛。

 

凝练Prompt

看着这满屏的模块抽象,作者脑海中不禁YY是不是把想的模块设计贴出来ChatGPT就会帮我们直接实现了呀?带着这个想法我们实操下,结果理想很丰满,现实很骨感。以下是我让Chatgpt实现课程列表获取模块的表现:

image.png  

大家一眼可见这ChatGPT给的答案质量很低基本就是一个花壳子,拿来直接用是不现实的。为啥会这样呢?其中核心原因归根到底还是ChatGPT没有联网,不知道目标网站相关接口的逻辑。所以我们需要将我们诉求完整的表达给他,那怎么表达呢?这时候我们想起定义接口文档的方式,我们这不就访问一个接口,然后再加点处理逻辑嘛,既然这样那我们就按照这个思路做个Prompt试试呗。

 

好的,请帮我单独实现获取xxx模块,代码中不需要包含登录模块代码。
- 遍历章节列表,针对每个章节,调用视频详情接口发送请求,获取视频的下载地址。


接口请求方式:GET
入参: xxx
Header头:需要携带cookie;cookie格式是"GGSESSION=" 值是函数入参cookie 中的'GGSESSION'
接口出参为json格式如样例:
  xxxx
 
函数入参:
xxx
函数出参:
section_info 
- section_id
- section_name
- category_name
函数主体逻辑:
xxxxxx。

 

 

各模块实现

岗位课程模块:

按照这个Prompt我们来试试岗位课程模块。这个代码还不错哟,基本上我们修改成目标网站url即可开箱试用。

image.png

那照着这个Prompt 加上我们的大纲开始批量复制下剩下来的模块。

 

登录模块

image.png

获取章节详情模块

image.png  

视频详情获取模块

image.png  

下载模块

下载模块在初体验中其实已经给出,我们只要稍微改下就可以用了,这里不做过多赘述。

 

主逻辑模块

最后我们可以轻松的把主逻辑串起来,这块比较简单就没必要麻烦ChatGPT了。

image.png

我们可以看到可以开始飞快的下载了,剩下的交给时间吧。作者也有空去干点其他的活了。

 

问题

当一切有条不紊的下载一段时间后,作者看了眼spider一切都是那么顺其自然,目标网站也很给力,没有给我们整啥限流的幺蛾子啥的。一小时候后,作者回来美滋滋的打开文件夹,惊奇的发现好多视频都已经下载完成。正在嘴角微微上扬的那一霎那,作者眼睛不经意间瞄到视频大小,咦怎么看起来有点怪怪的,哪里来的15KB这么1小的视频?轻轻的点开下,作者眉头一紧发现视频压根没法播放。害,果然不可能那么顺利,感情暴风雨前的宁静,到底是啥原因导致的呢?

image.png

带着疑惑作者扒了下问题视频的视频详情接口,好家伙,果然不看不知道一看吓一跳,原来这视频是m3u8协议呀!m3u8协议和mp4的视频到底有啥区别呢?这个网站为啥视频不统一是mp4,为啥有的是m3u8的呢?作者是小小的脑袋装满了大大的疑惑。

image.png

 

m3u8文件到底是啥?

首先我们先宏观看下m3u8的这个文件里头有有啥东西。揭开面纱后果然一眼丁真。

image.png

  • m3u8其实是个协议不是视频,他本质就是一个索引文件,告诉播放器我有哪些切片视频文件(.ts),以及对应的顺序是啥样的。
  • 播放器会根据我们播放的位置来下载或者预下载对应的切片视频进行播放

为啥目标网站视频要用m3u8协议?

个人盲猜有几个因素哈

  • 性能和效率: mp4文件是一个整体,要想观看得整体下载完成,你试想下如果一个几十分钟的视频你要先下载10多分钟才能观看,估计用户都早跑光了吧。而m3u8协议本质上是把视频切成一块一块的,看哪里就下载哪里,比如开始就可以先下一个分片让用户先看着,等用户看的时候就继续下载其他的分片,这样性能更好,效率更快,用户体验也更棒。当然如果视频不大切片反而更复杂,所以直接用mp4即可。
  • 更丰富的功能支持: 对比mp4文件,m3u8其实可以支持观看时进行码率调整,而mp4是已成定局了无法做调整。
  • 当然我觉得还有一点,这是他们的一种防爬策略,通过混合机制造成局部缺陷不可用,进而劝退一波人。

 

解决办法

咱们有ChatGPT,既然也知道了m3u8的原理了,那我们不可能知难而退,来开始让ChatGPT帮我们实现下一个m3u8协议的视频下载器。

image.png

效果演示:

image.png

在我们的努力下,spider又开始愉快的开始工作了。这一次为了避免又出啥幺蛾子,作者开始全程围观咱们的mini spider 干活,但是看着看着作者又开始头疼。

首当其冲的是进度管理,一个岗位有几十上百个视频,这个打印的日志快的飞起,目前下到那个了,哪些在下哪些没下?哪些视频下载失败了?作者是一头雾水,太难受了,咱们得解决下。

其次就是下载速度,目前一个视频下载下来估计两三分钟中左右,要想下载一个岗位相关视频估计要三个小时左右,作者是等不起了,有啥办法能帮我们提提速么?

最后是故障自愈,作者是真担心万一中间某个视频因网络抖动等原因导致失败,最终引发程序终止,按目前的情况就只能从头再来。你想想当你开开心心下到最后一个视频了,结果告诉你不好意思失败了,你得重新从头开始下一遍,那时候的你是不是得被气的砸电脑了?

 

那到底有啥办法能帮我们解决这三个老大难问题呢?

 

架构优化

下面咱们带着疑惑开始一步步解决下。

 

进度管理

进度管理这块好说,我们是不是可以把每条视频作抽象成一条任务存在mysql中,再辅以一个状态机即可?

image.png

  • 失败的任务是可以回到初始态进行重跑的,我们可以查询每个状态的任务数量快速得知目前整体的进度如何

 

有了状态机后,任务管理模块就剩任务查询和状态机修改的实现即可,让我们一起通过ChatGPT来完成。

image.png

  • 哇塞太棒了,这么一长串代码,说实话给自己写的话,估计得写好大一会,关键是这些代码还比较枯燥无味,再说谁想当curd工程师呢是吧?

 

下载提速

解决完进度管理问题后,我们回到下载提速这块。有两条解决思路:

1、要么缩短单个视频下载耗时

  • 如果要缩短单个视频下载耗时的话,对于m3u8的视频,因为已经切了分片,我们可以起多个线程并发下载
  • 对于mp4格式的视频,其实本质就是http Get请求,而http 请求中其实提供了range 头给我们做分片下载(感兴趣可以戳这里了解)

2、要么让更多的爬虫帮我们下载

  • 我们可以单机起多线程来进行下载,只要不打满带宽和网卡即可
  • 我们可以在多台机器上起多进程下载,效率更快

 

考虑到整体实现复杂度,以及单个视频也就下载两三分钟,所以我们决定单机起多线程来提效,比如我们起5个线程,理论上一个岗位的视频就只要半个多小时了,效率一下子提升五倍。

故障自愈

至于故障恢复这块,考虑到我们有了状态机,其实只要起一个后台线程定时根据状态机捞那些失败的任务进行重试即可。

 

整体架构

我们把上文的方案整体凝练成一个架构,如下图:

image.png

  • 入口进行读取配置、完成登录获取cookie,并初始化调度器
  • 随后往队列中放入一个对应岗位的抓取视频url任务
  • 调度器调度抓取器worker获取对应岗位视频的所有url,并落入任务表,然后将下载任务丢入队列
  • 队列接到下载任务会调度空闲的下载worker进行下载,下载完成后变更状态保存到对应的磁盘上
  • 定时往队列中丢入重试检查的的任务,调度器换气任务重试worker捞取失败任务,变更初始态后丢入队列中等待下载

 

可以大家看完后会感觉略显复杂,比如说为什么抓取器,下载器都是worker,为啥这么抽象等等?下面我们慢慢道来。

为什么要抽象worker?

  • 复用: 我们发现其实下载器worker 也需要多线程,抓起器量大的时候也需要多线程,他们彼此都需要调度器进行管理,并且也大量和队列打交道,如果把他们单独实现,就会有大量重复的逻辑。
  • 扩展性: 如果只有抓取器和下载器,我们也可以写一个简单的生产者消费者模式,但是我们后续发现一个任务重试功能是,就得去修改整体架构,变得很麻烦,所以为了扩展性考虑,也需要对他们做整体的抽象,便于后续特性化的worker出现。

整理好思绪后,我们就可以按照上文中的方法凝练成对应的Prompt交给ChatGPT来实现,整体的工程结构如下,篇幅有限,具体实现不在这一一展开,留个作业有兴趣的小伙伴可以通过ChatGPT实现,有疑惑的话可以评论区留言一起探讨。

image.png

 

思考与总结

最后,我们来一起回顾下:首让通过ChatGPT帮完成了最基础的mp4格式视频下载器,然后一步步完成支持自动抓取相关视频url并实现多协议下载的爬虫,最后我们为了提升效率实现了多线程版本的爬虫。

整个Spider涉及到以下的技术和工具,当然你也可以基于该文章的思路扩展实现其他的Spider,比如给女朋友抢抢演唱会门牌啥的,欢迎留言一起探讨。

  • Python: 用于编写整个脚本和各个功能模块。
  • Cookie: 用于登录会话管理。
  • HTTP Requests: 实现Http请求的基础Python库。
  • JSON: 实现目标接口请求和响应的数据序列化。
  • PooleDB: python中用于mysql的链接管理,提供mysql链接池化能力的一个库。
  • Threading: python多线程库,支持多worker并发执行,提升整体Spider执行效率。
  • Queue: 同步的、线程安全的队列类,用于实现线程中的同步。
  • Mysql: 用于任务信息和任务进度的持久化。

 

从涉及的技术和工具角度看其实整体的复杂都和工作量是不小的,但看完文章后相比大家也都清楚我们并不需要关注这么复杂的技术和工具的使用,相反我们更多的是思考和抽象问题即可,实现交给ChatGPT来做分分钟就可完成。当然ChatGPT也并不是万能的,不是说你想要一个下载视频的爬虫,只需念个魔法口诀即可完成;因此我们想用好它得做好框架和规划,然后引导它,往里头一点点加东西,最终才能达成终态。