设计爬虫Hawk背后的故事

 更新时间: 2017-01-05 17:09:30   来源: 科技头条

2016年,能记入个人年终总结的事情没几件,其中一个便是开源了Hawk。我花不少时间优化和推广它,得到的评价还算比较正面,因为负面评价也没什么渠道进我耳朵。不过你知道我...

2016年,能记入个人年终总结的事情没几件,其中一个便是开源了Hawk。我花不少时间优化和推广它,得到的评价还算比较正面,因为负面评价也没什么渠道进我耳朵。

不过你知道我写这个东西花了多久吗? 掐头去尾,这是第五个年头了。

读研究生伊始,实验室开始做数据挖掘,但我发现大家做研究,都是一段段的代码,遇到新问题,就不得不再拷贝一份修改,很少想过复用。于是我便花了一年的时间,开发了一款现在看起来配色丧心病狂的“数据挖掘软件”:

它居然能在上面刷微博,能把任何一个学姐学妹在微博的蛛丝马迹全扒出来,渲染出配色更丧病的绚丽图表(这里有更详细的细节)。实验室老师最喜欢拿这套软件给参观的领导演示了。

我多少有点偏执,想用纯图形化的方式构建数据挖掘的全部流程:从获取清洗数据,建模,训练到最后可视化。但以我当时的算法和软件水平,这样的东西根本就不可能做完善,我还意淫着能把它商业化。最终结果,是它跟着我一起毕业,除了我和几个学弟之外,没人用过。

工作后,靠业余时间维护它有很大的困难。数不清的bug和时间花销,让它成了拖后腿的包袱。一些考虑不周的接口,设计于几年之前,后来想修改却花费巨大。更重要的是,它的未来在哪里?

时值2015年,桌面软件已死,web都已过时,移动端才是兵家必争之地。这种大杂烩和复杂度,普通人不可能会用,程序员没有文档不爱用,大神不屑于用。

我也不想让它扔在历史的故纸堆里无人问津,索性就开源吧!

在开源之前,我做了一件事,将它改名为Hawk,寓意为驰骋天空的鹰;把我自己用的次数少于10次的组件全部删掉,连3D可视化,统计和预测模块也不幸免:只保留爬虫和数据清洗,因为分析和挖掘已经有其他工具做得更好了,比如Python那无比强大的工具链。我认识到,只做一件事,并将其做好是多么重要。

老司机送它上路了,那一天,它正式出现在GitHub上。

我对Hawk的评价

我就是用程序员的思路去设计Hawk的,好玩有趣是最重要的。

先说优点:起码它能用,几乎没接触过编程的人,也能在看过教程后,做出他自己的设计,熟练后效率会很高。

我最喜欢的是手气不错,升级Hawk2.0之后,绝大多数网页都可以通过一键提取数据。其实原理很简单,就是树结构的模板匹配。

其次便是那个所见即所得的数据清洗,在调试模式下快速设计任务,在执行模式下并行执行。恕我孤陋寡闻,这种基于流式管线思路,在其他软件上用的并不多。

最后便是子任务,再复杂的需求,都可以通过子任务分而治之,比如多次定向跳转。这东西让八爪鱼和火车头去试试看?

Hawk最大的价值,是将复杂的逻辑链条化和可视化了,你可以将代码变得不那么耦合,像搭积木一样方便地组合。当然这些都拜于函数式编程的思想所赐。

再说缺点:Hawk有很多的bug,没单元测试,功能不完善。但这都不是最根本的缺点,最根本是难于实现条件判断和循环,你很难写出if,switch和while。 这也是函数式语言的共同问题,你不得不逼迫自己用另外的思路去解决。虽然通过引入子任务,能“不够优雅”地解决它,但依旧要花费不少脑筋。

有的人觉得Hawk不够强,这是因为它是图形化软件。为什么具备UI的软件很难和命令行相比?因为设计UI的新功能时,不仅需要考虑它的算法,更要考虑它如何在UI上呈现。有些功能非常有用,比如爬虫常见的BFS和DFS(深度和广度优先遍历),但在UI上配置会异常繁琐,我不得不将它们抛弃掉,只保留最常用的功能。

为什么呢?轻松写BFS的人,会想用Hawk吗?应该不会,他们也许会直接敲代码。

Python版本的Hawk: etlpy

因为图形界面和C#本身的诸多不足,我开始发展了Python的Hawk,称为etlpy (Extract-Transform-Load in Python)

最初的想法,是将Hawk生成的配置文件(xml)交给etlpy去执行,初看很酷,但这种思路被证明不靠谱,两种语言,使用不同类库,对同一个任务流,就因为在底层的细微偏差,会导致结果的完全不同。我花了大量的时间在解决兼容性问题上,发现难度不小,两者互相制约牵绊。

最终,我放弃了所谓的兼容性,之后etlpy功能日新月异,甩了Hawk有10条街不止。Hawk的5分钟拖拽,在etlpy上只要一句话:

url='www.cnblogs.com/p{0}'t=task().let('p').range('1:20').format(url).detect().let('dignum').split('_')[0].num().write('cnblogs.json')

介绍etlpy的语法超过了本文的范畴,但它拥有Hawk的一切优点,同时能够分布式并行抓取,任务队列,超级代理,定时更新,与任意函数库集成。执行引擎(etlpy)的全部源代码,也就1000行出头。

也许你都没有听过Lisp这门语言,不过没有关系:Hawk是用C#写的,手敲的代码大概有1万行,用Python大概是1000行左右,用Lisp只要300行就能实现它的全部功能!当然,没有图形界面。由此可见编程语言在表现力上的巨大差别。Hawk本质上是个可视化的Lisp设计器。

我喜欢语言的纯粹和精妙,用简洁的语法就能代表复杂的逻辑。Hawk其实定义了一种爬虫语言,对诸多常用操作进行了模块抽象,在拖拖拽拽中,你构建了一张图(Graph),数据在图上流动,被生成,清洗和消费。最近大热的Tensorflow不也是一样的思路吗? 这张图,既是数据,也是代码。

当然,不论是Hawk和etlpy,你都不能把它们当成厨子,给它指令,就能做出一道菜来。而应当成一套顺手的厨具,而真正做菜的那个人还是你。自动化只是解决了部分问题,而巧妙设计的源泉还是来自于操作者本人。最强大的工具,是自己。

我们用语言表达概念,模式和流程,在此之上构建抽象。我假定每个用Hawk的人都能触类旁通,因此通过一些通用的介绍,他应该就能理解绝大多数的功能。那如果还是不懂呢?那不好意思,请用八爪鱼,否则Hawk的很多设计,对他来说就变得没有意义。我自己从来不愿意把Hawk和etlpy定义为爬虫,它们提供的是一组环境,语言和工具,爬虫只是它的一个不错的应用场景而已。

为什么要开源?

有人肯定会问,为什么不把Hawk商业化,去赚一笔钱呢?

理想状态下,成立公司去完善推广和销售软件,那么应该行得通,否则单枪匹马是不可能的。那我是不是值得为这套软件成立公司呢?应该不会,因为它只是个工具,还不值得我去开一家公司去完善它。

我相信,这也是很多程序员面临的类似困境,想靠卖软件盈利,就像路上的煎饼摊一样简单朴素,问题是放在10年前还有可能(记不记得30元一套的金山词霸?)而服务化盛行的今天,可能很低。到处推广,最终只可能沦为某天我在咖啡厅里的几句谈资,不可能有什么大气候。

有朋友说,为什么你要开源,对自己来说是不是有点像代码外泄?谷歌的TensorFlow都开源了。前面的路还有很远,大家都要忙着赶路呢。保护代码最好的方式就是开源,没开源时有人还想着破解研究,开源了大家fork一下然后就没有然后了,“得不到的才是最好的”。

不得不说,开源才是最贵的。大家看到开源,欢欣雀跃,以为在路上捡到了钱,其实不是的,如果你真想用起来,一定会花更多的时间来学习它,因为开源者没有义务像商业软件那样,提供完整的支持。时间和钱总是矛盾的,既想省钱还想省时间,天下没有这样的好事。

我以前天真地以为,开源的全部就是把源代码公开在网上。之后难道不是理所应当的声望,一帮牛人帮你优化功能修复bug吗?

哈哈,开源怎么可能那么简单。

如果软件写得不错,但没有宣传,那挂在网上完全无人问津,没人知道那是什么鬼;写得足够好,宣传也到位,那也很难有人帮忙贡献开发: 往上看,大神们都忙着造轮子;往下看一大帮实用主义者,抄起轮子就上路,谁顾得上看轮子是不是圆的呢?

开源不意味着坐视不管,依旧需要大量的精力去培养完善它和它的社区环境,通过邮件和论坛方式提供一定的服务。这件事情如果不用心,则一事无成。

如果pandas和ipython不开源,我现在也可能无法进入数据分析的殿堂,没有Linux和众多开源工具,世界也不会发展成这个样子。我明白了为什么开源是一种哲学,它是一种“共赢”。对程序员来说,名誉和成长比卖软件带来的价值更大,那是认可感和成就感。对其他人来说,开源带来了时间的节省和实际的财富。这样做必然是有价值的。

你肯定会问,那为什么etlpy后来不更新了?

第一它不稳定,现在释放出来可能砸招牌。其次,如果释放出etlpy,能量和Hawk完全不是一个数量级,它可能会让网站变本加厉地防爬虫。

请理性使用爬虫

开发完Hawk的那段时间,我也和很多人一样,看到一个网站就想去试试。以我的熟练程度,20分钟数据就都被下载下来了。然而数据保存在硬盘上,几乎不产生价值。

Google的工程师,可以下载它的全部源代码。可是为什么没有另外一家Google,甚至另外一家百度(又黑了百度)出现呢?因为更重要的是人才,资源和商业模式,代码在这件事情中最不重要了。拿到源代码都没用,更遑论挂在网上供大众阅读的数据呢?

爬虫是灰色区域,所以大公司从来不公布任何爬虫框架,但内部用的比谁都多。两年以来,网络发生了巨大的变化,ajax化,SEO变成了关键词广告位,大量的网站开始强硬地反爬虫,之前能随便抓取的网站,现在都变得很困难。我相信这里有相当一部分是Hawk的功劳。让人郁闷的是,不少人抓数据仅仅用来玩。

可以想象,一小部分标榜大数据和AI的公司,把Hawk的代码弄下来改改,添一点数据报表功能,就可以盗走别人网站数据,自立门户,去到处忽悠。虽然Hawk开源协议是GPL(使用开源代码的必须开源),但这完全限制不了,毕竟是中国嘛。可能他们不知道,数据,工具和算法根本不值钱,理解,洞见和执行才是最重要的,可是这部分却需要真刀真枪的本事。

总之,稍微理性一些,适可而止吧。

为什么不继续优化?

很简单,因为没有时间。爬虫本身没太多技术含量,再深入就进入了异构Web数据集成的领域,大部分人根本用不到。这是个拥有数不清trick和dirty技巧的领域,是与网站设计者的攻防战争,玩到最后,你的能力曲线就像log函数一样,被压制在一个确定的上界。

现在是人工智能和深度学习的天下,以我所在的团队为例,身边的大神们都在努力地优化Tensorflow,改造算法的底层来提升效率,在有趣的数据集上做大规模并行训练。

几年之前,如果我没有去做爬虫和Hawk,没有去设计正则解析器tnpy,而是紧跟潮流去做大规模机器学习的话,眼界和身价可能就完全不一样了呢?

当然,我不后悔去做Hawk,它是实实在在的能让大众用起来东西。或许在万里之外,有个一样戴着眼镜充满激情的和我一样的nerd,看到我的代码开心地拍了桌子,大喊“f**king nice work!”到那个时候,其他事情还重要么?

如果说我后悔没做什么,是我没有把软件做成英文,曾经有一段时间我个人非常崇尚中文编程,从而酿成了现在软件国际化异常困难的后果;再者,没有直接开发Python或者js这种能跨平台语言的版本,Hawk只能局限于Windows桌面应用,而不同的社群就风气又不一样了。

它的设计有不少问题,不过更多来自于多种因素的妥协。它注定是不完美的。因为职业生涯的关系,我无法像前几年那样,花大把的时间去优化它。既然开源了,我希望有人能把它做得更好。