编程人生 重构爬虫项目的反思

jinhuisheng · March 06, 2021 · Last by jinhuisheng replied at March 14, 2021 · 204 hits
Topic has been selected as the excellent topic by the admin.

由来

从 2020 年上半年开始,工作项目中已经在尝试用 tdd 的方式开发了,虽然刚开始写得很不好,但是也在不断变好的路上,也给自己带来了很多的好处,认识到了代码质量的重要性。

年前业余时间写了一个爬虫项目,想着快速写出来,但当想要快的时候,还是很自然地就凭本能去写代码了,就没有想着写测试和重构,代码也就逐渐混乱下去了。

过年前后这一段时间,在小波老师的直播视频号上,和不同的小伙伴一起对这个项目进行了重构,直到现在整洁了很多。

因为是自己写的,所以对重构的过程也有更深的认识,这个项目很像我们工作中的真实项目,如果不写测试,重构,时间长了,代码就一步一步烂掉,所以总结一下这个过程,对自己也做个总结,也让其他朋友看下,引以为戒!

开发过程

爬取第一个平台

刚开始,对很多代码,包括使用的爬取框架 webmagic,文件下载,上传,selenium 使用等,都会参考一些代码并进行修改,快速使用,也没什么问题,先让它跑起来

爬取第二个平台

为了快速实现,也是复制之前的逻辑改改实现,比如有些方法,为了快速,复制一下,改下名字,其中有几行不一样,就改一下,来完成第二个平台的爬取。

但这个时候,和爬取第一个平台时就不一样了,这个时候是对业务逻辑代码的复制粘贴,而不是像第一次那样对一些框架、组件代码的使用,业务代码中就有了很多的重复。

完成爬取完第二个平台功能后,代码就变成了下边这个样:

1、重复代码,违背开闭原则,没有封装

2、不同的爬虫逻辑重复

3、一看就是粘贴复制的代码

4、明显的重复代码没有处理,都在

重构爬虫

当想开始爬第三个平台的时候,看了下代码,感觉无从下手,代码看上去也混乱了

如果再接着往下写,因为没有测试,就不敢改动之前的逻辑,那就只能粘贴复制,类似的逻辑又会有不同,有 upload1,upload2,就会有 upload3,只会带来更多的重复。

和小波老师沟通,也非常有幸和小波老师一起结对对这个项目进行重构,经过前两次结对重构之后,就以线上结对直播写代码的方式进行重构,中间不断有新加入的小伙伴一起也对这个项目进行了重构。

经过了多期的重构之后,现在也变得比较整洁了。

重构过程

从外到内,逐层优化,不断调整

构建安全网

service

拆分爬取文章和同步文章,职责分离

提取方法到 createCrawlCommand 参数对象,方法和行为封装

crawler

提取 crawler 父类,消除重复代码

service 中消除不同 crawler 调用逻辑重复

Processor

提取父类 platformProcessor 消除重复

增加不同平台 Processor 对页面提取元素测试,重构提取元素逻辑

Pipeline

对不同平台 pipeline 增加测试

重构文章图片修改和替换逻辑

重构文章样式修改逻辑

提取 pipleline 父类,消除重复

重构文件下载上传代码逻辑

currentYear 统一处理

文件名称统一生成方式 FileNameGenerator

增加图片类型提取测试

SeleniumDownloader 消除重复

调整设计

从 platformpipeline 提取 ArticleContentModifier,职责分离

重构后代码地址

生产代码:

https://github.com/codingstyle-cn/forum-spider/tree/main/src/main/java/cn/codingstyle/spider

测试代码:

https://github.com/codingstyle-cn/forum-spider/tree/main/src/test/java/cn/codingstyle/spider

每次重构都会进行总结复盘,也学到了很多:

爬取第三个平台

重构后的代码方便了新功能的开发,逻辑清晰,新增功能可以快速理解代码,也能快速定位问题。

爬取第三个平台时,只用了 1 个半小时就搞定了,很惊讶,这也说明了代码有测试、并不断重构后,可以更快、更有质量的添加新功能。

总结/反思

这个过程中,对我来说是悲喜交加

喜的是,通过这个过程学到了很多写测试、重构的技巧,以及更深的理解。

悲的是,这个代码是我写的,所以,每次看到很明显的问题时,都感觉有些羞愧,为什么不思考呢,不动脑呢。 怎么之前就写成了这样呢。我不是在练 tdd 吗?为什么没有这样去做呢,为什么又回到本能无脑地去开发了呢?

当然,并不是每次都是这样,项目中也在去写测试,做重构,但越是工期紧张、比较赶的时候,为什么就自然地这样去做了呢?

错误的认知

我也想了很久,我想到的可能是安全感或者责任感?

按期上线,或者提前上线,让同事、老板满意,得到认可,以后就能升职加薪了?

但感觉不对呢。

你这次加班加点做完了,你可能给人的感觉是速度挺快的。那你就真的得到别人的信任和认可了吗?

由于赶工出现了很多 bug,那你给人的感觉就是速度快,质量差,你也得不到别人的认可,你可能就适合做那种只开发一次上线的那种项目,长期参与的,对代码质量要求高的,你也做不了,也不敢让你去做!

如果你这次做的快,后边需求迭代可能更快,因为普遍的认知就是一回生二回熟,那你就进了死胡同,后边技术债让你根本快不起来,进退不是。。。

这样看,你的安全感在哪呢?这个好像也体现不出你的责任感呢,不管是哪种情况,于人于己都不好!

小波老师的启发

跟小波老师结对,几件事情,让我印象深刻:

(1)、消除潜在的时间浪费

爬虫的集成测试,由于爬取需要时间,就等待了 15 秒之后,对结果进行断言验证,由于不同的电脑由于性能问题,有的不到 15s 就跑完了,有的到 15s 还没跑完,所以不同的伙伴来做的时候,经常会有失败,而且不知道的,又要花很长时间来排查到底是什么原因,这就导致了潜在的时间浪费,而且是之后不间断就会出现的,这个时间的浪费就会相当长。

小波老师就发现了这点,2 个小时的结对中,就解决了这个潜在时间浪费的问题,虽然感觉花了很长时间做这个,但它节省的潜在时间可是远远大于这 2 个小时的。

我也认识到 潜在的时间浪费的消除非常有意义,工程师就是要发现这些并把这些处理掉。

(2)、按正确的方式做事

一次是修改部署脚本,一次是通过配置流水线的方式来更新数据库中某个数据。

最初我感觉,咦,为什么不直接在服务器上改了试下呢,而是要在 ansible 配置脚本,再跑脚本来验证。

为什么不直接去数据库修改一下呢,要新建一个流水线中做这个修改,感觉好着急呢

现在我才体会到,他是在用正确的方式去做事,正确的方式可以避免很多错误的发生, 比如:数据库表误删了怎么办?直接在服务器上改,那 ansible 脚本是不是就不能验证它的有效性了呢?以后怎么还会信任 ansible 做服务器的初始化、常用工具的配置呢?

(3)、加班赶项目的沟通

最近一段时间,新项目工作量比较大,要赶工快速把东西做出来,很苦恼,跟小波老师聊了聊之后恍然大悟

小波老师问我,你想成为什么样的人?你的原则是什么?

你觉得工作量大,月底要上线,工作量做不完,需要赶工,但赶工能不能做完不管,就去埋头苦干,快速粘贴复制、牺牲代码质量去堆项目,那如果明天上线呢?

工作量是多少,那就是多少,做不完就是做不完。

你一次没有坚守原则、牺牲质量带来的快,下次就会觉得你可以更快,然后再一次放弃原则,到最后你就没有了原则,但牺牲质量带来的技术债让你后边又快不起来。

这个时候你会让别人看到你是什么样的人呢?没有原则,做事应付

你会给别人什么好的印象吗?不会

最后

明显可以感觉到小波老师表现出来的专业性,以一个工程师的思想去看问题,我不是也想要给别人这种感觉吗?

作为程序员,写代码,符合简单设计原则,用正确的方式做事,以工程师思想去要求自己,这不就是程序员的职业修养吗

如果我这样去做了,我也不会有因为赶工,着急,不写测试,不注重代码质量的困扰,我那样做,是因为我降低了自己的原则,底线,表现出了自己的不专业!

这才是我以后应该坚守的,做一个更职业的程序员!

坚守原则,你就不会衡量利弊。

每当你开始衡量利弊的时候,你都有可能抛开了原则的底线。如果你坚守了原则,你更多的考虑的是对错,而不是得失。

「软件匠艺社区」旨在传播匠艺精神,通过分享好的「工作方式」,让帮助程序员更加快乐高效地编程!

文章逻辑清晰,之前也有这样的疑惑,用 tdd 会不会拖慢项目的进度,或者一个工期很紧张的项目(大部分)不会考虑使用 tdd,但是就像文章里说的,先实现功能反而是会欠下技术债,到最后发现代码臃肿到无法维护,所以 tdd 要成为一种编码的习惯。

牛,为慧生的热情和坚持点赞!

seabornlee mark as excellent topic. 08 Mar 13:13
Reply to chen_1992

一期加油哈,😀

Reply to flykingstar

今天还是又一次犯了这个错误😓 ,发现了问题,就想快速修复它,着急想着上线,就想着在出错的地方进行快速修复,没有仔细考虑,没有想着分析问题根源并加测试修复它,如果这个地方改了,可能其他的地方也会有问题。。。

还好被拉住了😹

很多时候,事情都没有那么着急,都是自己在吓自己,催自己。

想了想,应该是心理层面的压力(线上出 bug,谁写的代码?怎么测的?出事故拉,怎么写的代码。。。),让我放弃了思考。

当然,毕竟好几年的这种无脑的应急反应,一时间还是很难改正,还得一次次的暂停自己,一次次的反思,来转变这种思维方式或者说是心理压力!

继续努力👬

如何识别自己是否坚守这些原则呢

写了这篇文章之后,对如何判断自己是否坚守了原则,还是有一些疑惑。。。

然后就去看了 Bob 大叔的《代码整洁之道 - 程序员的职业修养》这本书,在其中找到了压力这个章节其中 - 危机中的纪律,给了我很大的启示,也算是对这篇文章的进一步思考:

危机中观察自己的反应

观察自己在危机时刻中的反应,就可以了解自己的信念。如果在危机中依然遵循着你守持的纪律,就说明你确实相信那些纪律。反过来说,如果在危机中改变行为,就说明你并不真正相信常规行为中的原则。

我们也可以依据它来验证我们是否坚守那些纪律:

TDD

如果在非危急时刻你会遵循测试驱动开发的纪律,但是在危机时刻你放弃了这种做法,就说明你并不真正相信 TDD 是有帮助的。

整洁代码

如果你平常时候你会注意保持代码整洁,但在危机时刻你却会产出混乱的代码,就说明你并不真正相信混乱会导致速度下降。

结对编程

如果在危机时刻你会结对编程,但平时却不结对,就说明你相信结对工作比不结对更有效率。

时刻践行

TDD、整洁代码、简单设计、结对编程、用正确的方式做事、以工程师思想去要求自己

在所有工作中都遵守这些纪律。遵守这些纪律原则是避免陷入危机的最好途径。

当困难降临时,也不要改变行动。如果你遵守的纪律原则是工作的最佳方式,那么即使是在深度危机中,也要坚决秉持这些纪律原则。

You need to Sign in before reply, if you don't have an account, please Sign up first.