为了给ripro
主题资源站填充数据,我用golang
开发了并发版爬虫和分布式爬虫两个版本,已经顺利跑通!代码总量还是比较多了,具体课程中再详细讲解,这篇文章主要分享一下自己的技术选型思路,以及我觉得做这个项目过程中自己的一些感悟!包括对于系统架构的理解以及对于golang
语言本身的理解。
先说自己的业务选型思路,由于自己几套系统都已经上线,所以填充数据又成了棘手的问题,我以前的文章中也分享过我使用爬虫的经历,这次准备不走老路了,也不用golang
系的口碑不错的colly
了,而是自己来实现一套爬虫。这样做的目的有两个,一是我现在上线的站点确实需要填充数据,因为用了wordpress
的ripro
主题,资源类站点嘛,口碑和业务闭环实现不错的主题,可选的并不多,目的还是要放我自己的课程上线,但是我自己的课程暂时也就几个,数量不太够,网站空空的,感受总是不好的,所以还是找些数据来填充。二是爬虫其实是一个对于综合能力考察比较全面的项目,并且如果自己不使用现有框架,而完全从零来实现,会考察你各方面的能力。往基础点说就是你对golang
语言的掌握,http
协议的理解(ip
代理池、隧道代理、防爬、反爬等),往业务层面上靠,可以把方方面面的知识都揉进来,比如说数据存储、高并发架构、分布式架构、各种业务中间件的使用,总之,它是特别考验一个人综合能力的,也正因如此,我准备出的课程,就是教大家具有这方面实践的能力,那既然要录课,自己不亲手实现出来也说不过去,基于这两个原因,并发版和分布式版本的爬虫就诞生了。
这过程中解决了很多意想不到的问题,比如说匹配页面时候,golang
中正则表达式的理解程度,很大程度上决定了你爬虫fetcher
层的业务执行效率。还是拿我使用过的python
系的scrapy
来做对比,因为scrapy
使用过程中基本都是聚集式爬虫的开发,针对特定站点,所以我基本都用xpath
,直接chromeF12
模式获取xpath
地址,直接填上去就可以,这样虽然简单,但是xpath
的执行效率很低,如果遇到比较复杂的dom
结构显然这是拖后腿的环节,还有更早期使用比较多的BeautifulSoup
之类的,那个执行效率就更加低。css
选择器也是类似,另外一个原因是这些工具的使用,其实不需要太多思考,如果长此以往用工具重复每天做相同的事,那么对于个人能力的成长想必也是没有助益的。因此,从效率和拓展知识边界两方面思考,我都决定使用xpath
作为自己的dom
解析器,当然这其间趟了很多坑,举几个例子:
golang
的regexp
这个正则package
是不支持perl
syntax
(pcre
)正则表达式的
如果正则用的少,或者没有匹配过很复杂的页面结构,那么前向断言、后向断言这些东西就应该没有用过。而有些页面内容的获取,用这种方式就效率其高。那怎么让golang
支持这些功能呢?肯定也不需要重复造轮子了,已经有了一个regexp2
,它可以支持你在golang
代码中使用前向断言、后向断言。
- 修饰符的使用
在大块源代码中,如果以于修饰符的理解不深刻,很难更高效的做内容提取。比如sm
就是我使用最多的两个修饰符(?sm
)加到正则表达式的前面,代表着匹配内容时允许`n行以及进行多行匹配。而对于修饰符
i,做英文处理时可能出镜率更高,我这次业务中没有遇到,但是
U,也就是非贪婪匹配,在你把抓取数据框定到一个源码范围内,进一步做抓取,而抓取的有效内容有重复结构出现的时候,你要指定这个修改符,才可以把源码块中重复的部分全部匹配到,再通过
findAllSubmatch中分组的定义,可以把你想拿的图片地址呀,
url地址呀等有效信息遍历取到,方便回放到调度器中,交由多
channel组成的
worker`进一步处理。
- 页面编码的问题
这个有些站点可能会遇到,有些遇不到,要实现编码检测,再进一步做数据提取。
关于golang
也有很多新的理解,如果框架用久了,或者在公司天天写业务代码,你可能不会太关注一些golang
设计精妙的地方,比如说golang
对于interface
的定义,比如说golang
中那句经典的“不要通过共享内存去通信,而要通过通信去共享内存”。展开说一下这两点,golang
的接口是由使用者定义,如果你写过php
或者java
的话,接口就是用来规范业务实现的,接口定义什么,你就实现什么,定义了几个接口,并且你业务中有涉及,那你几个接口中的方法你就都需要实现出来,而golang
中的接口有点不一样,比如说a
接口有方法bcd
,x
接口有方法y
,如果你在某个文件中把这两个接口的4
个方法(bcdy
)都实现了,你实际就已经实现了ax
两个接口,而所提的这某个文件,你搞些其他逻辑进来也是可以了。再来说另一点就是经典的这句话,如果你对于channel
不怎么使用的话,那你可能也不理解这句话的深层含义,因为在golang
中函数和channel
都是一等公民,什么叫一等公民,就是你可以为所欲为,其它不强调,但强调一点函数和channel
都可以作为struct
的参数、返回值,这就极大扩展了你写程序的灵活性,顺着这个思路来想,你只要参数或者返回值是函数的时候,由于有闭包函数的存在,其实在函数里套无限多的业务逻辑都是可以的,而channel
同样作为一等公民,channel
中的数据结构也函盖了golang
支持的所有类型,就包括了函数,普通的业务,如果只需要业务类型抽象成队列,那么channel
就可以搞定,利用channel
的写入和消费属性,你基本上不用第三方业务中间件(rabbitmq
、rocketmq
、redis
),你也可以实现这些中间件可以做的事(话虽如此,术业有专攻,该选什么还选什么),这种灵活性就有点变态了。而channel
结合着函数作为一等公民,又怎么理解呢?毕竟两个一等公民就是强强联合了,在做分布式系统中很多业务相互之间的衔接可能参数比较复杂,那么,普通的数据类型可能就无法支持一些功能,而channel
加上函数共同作用,就可以传递非常复杂的rpc
参数,让分布式业务之间的调度和融合显得游刃有余,总之,golang
的高级技巧如果你不做复杂的项目,不做深度使用函数一等公民特性,不做channel
一等公民特性的项目,应该是没啥深刻体会的,特别是天天写业务代码。
我还记得我在写公司的建店项目的时候,毕竟业务体量放那里业务复杂度自然跟自己之前做的业务不是一个量级,由此可以欣赏很多大拿的“精湛”技艺,那个时候对于golang
的dao
扩展包大家代码写的飞起的样子记忆犹新,有人用golang
的mysql
基础库,有人有gorm
,有人用其它的数据库组件,拼mysql
的方式神乎其神、各有千秋,当时觉得这个牛逼,现在来看,其实这些技巧还是术层面的,具有可习得性,类似于你看我的抖音,今天学习了一个泡妞技巧,明天学习了一个泡妞技巧,但是跟妹子一聊天一约会,你发现你撩不动她,甚至几句话聊死冷场,女生当场黑脸,一个手起刀落,你就进了黑名单!本质原因就是你没有一套成型的思想体系,没有一套真正落地实践有效的架构,而架构的习得是有门槛的,不是说不能学会,但是就算全部给你讲了,还是要看人悟性的,悟性高了,可能很快掌握,悟性不高,可能你反反复复学习也不得门径。就比如报我两性情感课的学员,我把聊天的架构节奏步骤都告诉他们了,一样有人三个月现在泡妞风生水起,而有人依旧默默无名,原地踏步。但是凡是就怕用心二字,但是只用心,只在大脑中模拟是没个卵用的,你必须身体力行,疾速实践,不然天赋的愚钝加上后天的不够努力,将让你跟老天爷赏饭吃型选手、或者能迅速抓住问题本质然后持之以恒的学习实践选手间的差距越来越大!
扯远了,扯回来,总之golang
的很多高级特征,如果你自己不实际做几个项目,不做完项目好好复盘,其实你对我讲了些啥想必一定是糊涂的,这大概也是通过报名我的课程,来实现技术层面和跟女生互动层面双丰收的一大理由吧!哈哈!
圆回来,继续说回我开篇埋的坑,最后一个,就是对于系统架构的理解。很多业务现在看来是不需要架构的,就比如说你做的流量站,就是靠联盟广告收入来赚钱,你也没啥野心,现在短视频时代,留给web
的红利越来越少了,所以能端好手上的饭碗就不错了,还要啥自行车,所以这种网站甚至云原生那一套都不需要用,就cvm
或者轻量级服务系统上直接编译安装程序、业务中间件、逻辑中间件,然后狠劲搞流量就完了。架构对于什么类型的企业作用最大呢?我觉得就是提高了每个人的效能,便能使整体效能达到指数级的提高,类似于下面这张图:
这也就是为什么现在都搞云原生的原因,一个分布式系统的部署,我在传统机器上也可以搞,我之前scrapy
引入scrapy-redis
作为数据中间件,我就可以把各种机器的闲置性能全部利用起来,达到最大化榨干服务器性能的目的。而这个东西你传统企业也可以搞呀,我做多机房部署,搞几个运维,在那源码安装也一样可以完成呀,为啥现在都要上云原生呢?就是因为云原生相关的组成部分,可以解放很多人力的投入,并且可以让scrum
真正的得到落地。至于技术架构,我觉得没有最好的,只有更适于当前情况的架构,所以无所谓最好的架构,最牛逼的架构。
但是基于k8s
的云原生开发的技能,我倒觉得是现在大厂程度员,或者有志于进大厂的程序员都应该具备的一项技能。