Selenium PHP爬虫
陆续开发维护了几个爬虫以后终于在web端爬虫这一块有了登堂入室的感觉。中间踩了许多坑,也对爬虫的许多细节有了自己的认识,所以今天希望能分享一些爬虫经验。虽然爬虫的很多东西不好说太细,因为说太细了别人马上有针对性的反爬虫了,而且很多技巧业界没用通用的解决方案(别人就算做出来了也不太愿意分享),都是我自己慢慢摸索出来的。但是我认为适当的业界/友商之间的技术交流是必要的,不能闭门造车,我也渴望能和业界/友商有更多私下的深入交流,大家多切磋才能进步嘛。最近我在研究app反编译爬虫相关的,所以对这块特别感兴趣。我博客:qsalg.com
为什么是PHP
其实就目前业界来说,python下的爬虫轮子是最多的,我厂大多数同学都用python搞爬虫。我由于原来搞web后端用PHP比较多,对PHP下的生态和第三方库啥的如数家珍,厂里对使用的语言也不做强制要求,所以我就用最拿手的PHP开搞了。有同学可能会觉得PHP下爬虫轮子似乎不多,甚至有部分做惯了PHP后台的同学在需要完成爬虫任务时也拿起了python,难道PHP就不适合搞爬虫么?我认为恰恰相反,PHP在web领域积累了大量成熟的第三方库,而且其强大的内容处理能力使之在需要琐碎处理的爬虫任务中如鱼得水。爬虫从运行时间上大致可以分为两种:1、实时的爬虫:一个请求来了我就开一个爬虫去爬取结果,一般情况下这种爬虫直接对外提供API; 2、长期爬虫:这种爬虫一般会一直运行或者定期运行,把数据更新入库。一般来说这2种爬虫都需要比较频繁的维护更新,PHP作为一门部署简单的脚本语言,可以实施热更新爬虫代码,非常方便。
使用第三方库
用PHP搞爬虫请利用好composer下的第三方库。PHP在web领域积累了大量成熟的第三方库,基本上你想得到的库都能在github上都能找到,如果你不用第三方库的话,那么你就等于放弃了PHP在web领域的巨大优势。爬虫相关的PHP第三方库我用的比较多的有:
1、Guzzle:功能很完善的httpclient,带异步并发功能,别的脚本语言找不到这么好的httpclient
2、Goutte:对symfony的dom-crawler和css-selector的简单封装,你也可以直接用symfony的css-selector来抽取html的dom元素
3、symfony/process:symfony出品的php开进程的库(封装的proc_open),兼容windows,要知道pcntl扩展不支持windows的
4、php-webdriver:Facebook官方维护的selenium的php客户端
前段时间有一个《我用爬虫一天时间“偷了”知乎一百万用户,只为证明PHP是世界上最好的语言》,这个repo很受关注也一直在维护。我也研究了一下他的代码,质量很高,但是有一个缺点就是没有使用现有的第三方库而选择自己封装。我们应该把精力花在爬虫业务上而不是去从新造轮子,我平时直接无脑的使用现有的composer下的各种第三方库。我从今年4月份入职到现在8个月时间只写了3个爬虫(除了爬虫业务外,基于redis的分布式爬虫调度、单机多爬虫并发、报警+监控+参数控制、selenium多浏览器匹配+特性定制、代理策略定制and so on)一套下来,所有代码都加起来只有6000行PHP代码。已经有现成的成熟稳定的第三方库不用,自己造轮子是得不偿失的。
多线程、多进程和异步
爬虫不能不说到并发,爬虫作为一个IO密集型而不是CPU密集型的任务,一个好的并发的爬虫应该满足:1、尽量可能高的下载带宽(下载带宽越高,爬的数据越多);2、尽可能小的CPU消耗和尽可能小的内存消耗。
多线程似乎是实现并发的不错的方式,经常有人说“PHP没有多线程”让广大PHPer直不起腰。作为web后端的时候PHP没法使用多线程,但是作为命令行运行的话PHP是支持多线程的。我们知道PHP分为线程安全(ZTS)和非线程安全版本(NTS),后者其实是为了兼容win下IIS的ISAPI,这也就逼着PHP下的扩展基本上都提供的线程安全和非线程安全版本。也就是说从理论上来说命令行的PHP多线程是真的多线程,没有像py或者ruby那样的全局锁(实际上同一时刻只有一个线程在跑),但是实际上PHP命令行多线程不太稳定(毕竟它的多线程不是为php-cli设计的),所以我建议命令行应用还是使用多进程来做并发。
而异步也是实现并发的重要方法,爬虫需要并发的大多数情况是我想是同时去爬多个url,这种情况无须使用多进程/多线程,直接在单进程中使用异步就可以了。比如PHP的Guzzle异步支持非常好用,Guzzle默认异步是包装的curl的curl_multi的几个函数来做的,如果你想用性能更好的异步事件库可以设置Guzzle的adapter为react-guzzle-psr7(当然了你得安装Event之类的异步pecl扩展)。我个人试用下来觉得Guzzle默认的异步就够用了,单进程并发几十上百的http请求跑满小水管那是不成问题的,cpu和内存消耗还很小。总之,把php的多进程和异步合起来用,实现良好的并发不是问题。
关于爬虫框架
开箱即用封装好的爬虫框架不是银弹。我一开始也研究了java和py下的一些比较著名的框架,企图先把这些框架学会然后把自己的爬虫任务整合进去,后来发现这么做很困难。诚然用爬虫框架基本上改两行就可以跑起来了,对简单的爬虫任务来说很不错。但是用别人封装好的框架会导致爬虫的定制性变差(要知道爬虫是需要灵活处理各种情况的),而我们知道爬虫的本质就是开着httpclient取回html然后dom抽取数据就完了(并发的话再加个多进程管理),就这么简单的任务为了尽可能满足所有人需要被封装成了一个复杂系统的框架,并不一定适合所有的情况。有一次v2ex上也有人出来质疑说我直接用requests也很简单啊,scrapy的优势在哪里呢?我的理解是爬虫框架的优势就在于把爬虫的并发调度都做了,而我们直接单进程来写爬虫的话只能是一个单进程爬虫没有并发调度。其实爬虫的多进程并发调度没那么复杂,也不需要搞太复杂,我说说我的php爬虫是怎么做并发调度的(python下一回事)。
爬虫多进程调度
我的PHP爬虫多进程调度比较简单粗暴,爬虫分为管理爬虫进程的Master进程和负责具体爬取业务的worker进程,而redis负责对爬虫进行控制以及显示爬虫的状态。
比如我有一个爬取A站点的爬虫任务,我开发好爬虫Worker A以后,我可以在redis中设置在服务器Node1上我开2个Worker A来爬,而Node1上的master1进程会定期去redis中读取控制参数,如果发现Node1上的Worker A进程不足2个的话就会新开Worker A进程补充。当然了,控制参数需要包含哪些你可以自己定制,比如我就定制了每个节点的Worker上限、使用的代理策略、是否禁止加载图片、浏览器特性定制等等。Master进程新开Worker进程有2种方式,一种是通过类exec(比如在Master进程中proc_open(‘php Worker.php balabala’, $descriptorspec, $pipes)这样)调用来开一个新的命令行php的Worker进程,另外就是通过fork机制来做。我采用了类exec调用的方法(其实是symfony/process库,它封装的proc_open函数来开的进程)来开Worker进程(如果要传命令行参数给Worker进程注意使用base64编码一下,因为命令行可能会过滤某些参数),这么做的好处就是解耦。需要注意的是,现在Worker进程都是Master进程的子进程,所以Master进程退出的话所有Worker进程也会退出,所以Master进程注意异常的catch,尤其是redis、数据库和别的有网络io的地方。如果你希望Worker进程damonize的话请按这篇文章的方法来(php下也是一样的,不过不兼容windows)。
我不建议Master进程通过IPC机制对Worker进程进行控制,因为这么做一下子就让Master进程和Worker进程耦合起来了,Master进程应该只是简单的负责开Worker进程而已。对Worker进程的控制可以通过Redis来完成,也就是说Worker进程每隔一段时间(可以是完成了一次http请求,或者每隔几秒)可以去Redis读一次控制参数(如果需要的话,也可以到汇报一下自己状态,参数比较多的话用好redis的pipeline),在实践中这种方法工作的很好。
我的PHP爬虫中都采用了这个简单粗暴的方案,我认为它的好处有4个:
1、支持分布式且依赖简单,参数控制+状态汇报直接通过单一的redis节点。我推荐你用一个好的redis的GUI工具来管理redis,redis的5种数据结构用来做爬虫参数控制+爬虫状态显示非常方便
2、Master进程和Worker进程解耦,而且可以解决爬虫较多发生的内存泄漏问题(Worker进程跑完直接退出),也可以热更新代码
3、实时爬虫可以通过Master进程抢占push到redis list中的请求来做,而长期任务的爬虫在Worker进程意外退出后Master进程立刻补充,能适应各种爬虫任务
4、开发爬虫只用去写Worker进程就ok了,开发方便,不用关心调度问题
缺点当然就是这一套机制都需要你自己写,高度可定制性的代价就是自己动手。
为什么是 Selenium
在简单的爬虫中直接用 httpclient 就可以爬了,但是反爬虫比较厉害的情况下,有很多反爬虫的机制,比如:各种 302 跳转、 js 检测、种 cookie 、 iframe 、 captcha 等等。去逻辑分析这些机制成本太高了,就算分析出来了用 httpclient 模拟也会写一大堆,代码也很难重用,于是不得已只能增加成本上浏览器了。浏览器也得分有界面浏览器和无界面浏览器,无界面浏览器不用渲染自然 CPU 和内存消耗低适合爬虫,也有人在 linux 下用 Xvfb 来把有界面浏览器运行在虚拟屏幕上来降低的消耗。
Selenium 作为事实上的前端测试标准,其完整的 API 是为大量的前端测试需求而成熟,这是前端给我们爬虫工程师的馈赠。 Selenium 和 phantomjs 、 HtmlUnit 、 ghost.py 之类的 headless 浏览器(这些 headless 浏览器一般都提供了原生的 API )不是一类东西,你可以把它理解成可以驱动包括 phantomjs 、 HtmlUnit 、 Chrome 、 IE 等主流浏览器的统一接口。至于原生的 headless 的 API 方案,比如你直接用原生的 phantomjs 完成稍微复杂一点的操作会很困难,因为你看不见也很难 debug 。实际上 phantomjs 、 ghost.py 、 HtmlUnit 之类的比较小众的原生 API 的 headless 方案在大多数场景下,是无法和 Selenium 相提并论的。由于 Selenium 统一了和浏览器交互的标准,客户端基本上包含了主流语言。也就是说你可以在你喜欢的编程语言下用 Selenium 在 Chrome 上开发好了爬虫,然后在生产环境直接把浏览器换成 phantomjs 就 ok 了, API 提供统一的 dom 、 js 注入、 cookie 管理、事件等待、浏览器控制和输入等操作,完整且成熟。而且由于我用的是最好的语言,实际上用 PHP 的话选择似乎就只有 Selenium 没有别的选项了,所以 Selenium 还有个优势就是 PHPer 除了它没得选。
PHP 下 Selenium 开发环境
虽然说 Selenium 本身用啥语言都行,考虑到我们的主题是用 PHP 来搞爬虫,所以这里把我的 PHP 下的 Selenium 开发环境的一些基本点介绍一下,方便刚入门的同学。
我以开发环境 win7+XAMPP+eclipse(with PDT)举例,大家可以用自己喜欢的 PHP 开发套件,我只说一下其中一些注意点。新的项目我建议都用 PHP7.0 ,基本上所有的第三方库和扩展都支持 PHP7.0 了。另外 windows 下安装扩展的时候,去 pecl 下载 dll 的时候注意区分线程安全和非线程安全版本,而且扩展之间依赖关系,比如你要安装 php_event.dll 扩展,你会发现它依赖 php_sockets.dll 扩展,而且你在 php.ini 中必须把 php_event.dll 放在 php_sockets.dll 的后面。
PHP 下的 Selenium 的客户端php-webdriver由 Facebook 维护,在 composer 添加依赖安装即可,需要注意的是 PHP 下需要一个Selenium 官方的 java 的命令行的应用(负责管理浏览器和分发来自 Selenium 客户端的命令,也就是所谓的 Selenium RC )。 Selenium 在今年 8 月推出了 Selenium3.x 大版本,我依然在使用 2.x 版本也暂时无升级打算,所以如果你需要一些 3.x 支持的新特性的话可以试试 3.x 版本。如果使用 2.x 版本的话,安装好 java 运行环境,然后下载官方 build 好的最新 selenium-server-standalone-2.53.1.jar ,启动参数请读官方文档(目前我试过 chrome 、 firefox 和 phantomjs 都是没问题的),需要注意的是官方的 2.x 版本的代码(官方 repo 称之为 leg-rc 分支)有个比较重要的 bug 没有修导致 selenium-server-standalone-2.53.1.jar 存在 bug ,后面我会讲这个 bug 。
这一切都搞定后,就可以按照 php-webdriver 官方 github 的 example.php 例子跑个 hello world 了,然而 php-webdriver 并没有那种一步一步教初学者入门的文档(它的 API 也只是类自动生成的),很多特性需要使用者看源码或者去读 Selenium 官方文档。我个人觉得 php-webdriver 虽然文档缺乏,但写得很漂亮,大量使用类来克服脚本语言弱类型的缺点,写起来像用 java 那样鲁棒又不失脚本语言的速度, Facebook 不愧是 PHP 的大厂。
开发 Selenium 的模式
我不会给大家长篇累牍的介绍 Selenium 的 API 或者贴上几块爬 xxx 页面的 example 代码,因为这些东西官方文档( Selenium 官网被墙了)/源码里面就有贴出来真的没啥意思。所以这里主要讲 Selenium 的运用模式,大家熟悉了 Selenium 的 API 了觉得如果爬虫干不下去了,可以试试直接转 web 前端测试(笑)。
一旦采用了 Selenium 就意味着必须开个浏览器,而速度就成了一个很大的问题,这也是很多人比较关心的。一个解决思路就是并发,我可以开很多爬虫进程来驱动很多浏览器,然而这种模式有个缺点是对 CPU 、内存和带宽的消耗特别大,毕竟用 Selenium 就意味着开始拼成本了。为了降低对 CPU 和内存的消耗, phantomjs 等似乎是一个很不错的选择,我在 windows 下的经验是每个 phantomjs 进程内存消耗 90M 左右, i7 的 CPU 单机并发可以跑到 100 个 phantomjs 进程,所以说内存和 CPU 都是可以接受的。另外请务必打开 phantomjs 的静态资源缓存(缓存图片、 js 和 css 等静态资源,参数看 phantomjs 官网文档),或者干脆禁止加载图片,做完这些以后每次 http 请求基本只会下载 html 和一些 ajax 动态请求而已,加快速度的同时非常节省带宽。
当然了 phantomjs 并发有一个神坑,如果你给 phantomjs 设置了--disk-cache=true 并且有并发,由于所有的 phantomjs 进程实例会使用同一个系统默认的缓存目录,所以时间久了以后会导致缓存文件会被破坏(并发越多重现越容易)。此时 phantomjs 的表现很诡异:访问别的 url 可以正常访问,但是访问一直在爬的站点 url 就会在 GET 的时候卡住(我猜测静态缓存文件是根据 url 的 hash 来存储的),此时 CPU 占用 100%,你知道当初发现这个状况后很难怀疑是 phantomjs 自己的问题,觉得肯定是目标网站用了黑科技。用 fiddler 抓包发现 tcp 没有建立连接,后来用 wireshark 抓包发现 phantomjs 连 tcp 的连接请求都没发,最后才发现是 phantomjs 的缓存问题,也就是说你一旦指定了--disk-cache=true 并且有并发,请一定给不同的 phantomjs 实例指定--disk-cache-path 为不同的缓存目录。
另一个加速的方法自然就是在拿数据的时候,把 cookie 从 phantomjs 中取出来,然后用 httpclient 带上 cookie 去嗖嗖的取就 ok 了,不过很多时候请求参数的构造很复杂导致这种办法比较困难。然而 Selenium 强大的 API 提供了一个在浏览器中同步 /异步注入 js 代码的功能,这个功能如果发挥想象力的话在很多时候可以克服请求参数构造复杂的问题并且速度还很快。
Selenium 的一个神坑
大家直接看这个 PR : Fix SessionCleaner dying off due to uncaught exception. by zmokhtar · Pull Request #2031 · SeleniumHQ/selenium ,关于这个 bug 我还需要给大家讲一个故事。由于我之前花了很长时间在搞一个私人的兴趣项目,到今年 4 月份的时候发现还有接近 2W 的学杂费欠着学校,我们可爱的辅导员经常很和蔼的关心我聊些答辩啥啥啥之类的,我没办法就找到我厂打算打点杂在毕业前挣点学杂费。打了一个星期杂以后,有一个比较难的爬虫没人搞我就接手了,当时也没怎么正式搞过爬虫,于是凭着一点技术直觉选定了 Selenium+phantomjs 的技术栈,花了 20 多天把并发爬虫调度+爬虫业务这些东西打通了(其实后来队友直接用 httpclient 模拟也能搞定的样子)。结果发现 phantomjs 进程存在无法回收的问题,并发多了以后跑着跑着内存就炸了,这个问题没法解决一切无从谈起。我还尝试了 C++写了个 daemon 检测无法回收的 phantomjs 进程来着,然而 Selenium 不对客户端暴露浏览器的进程号,导致做起来效果不太好。当时算是实习,干了 1 个月啥成果都没有我也比较郁闷。 4 月干完后就是五一,当时觉得干不下去了,然后没事逛 github 看到了 4 月 29 号开的那个 PR ,这尼玛不就是我遇到的 bug 么,而且这个 bug 在 google code 那边几年了前就被提出了,这么巧刚好在我卡住的时候被解决了?于是觉得要不把这个 bug 解决试试,于是用 Selenium 的 repo 自己 build 了一份已经 fix 好了的驱动,然后问题就解决了,之后什么都比较顺利了。磨合久了后比我小三岁的 Leader 可以在很短时间做出正确的判断给了我很深的印象,所以之后毁了杭州蘑菇街的三方留在冰鉴科技继续搞爬虫也是后话了。
然后这个 bug fix 官方很不负责的只是 merge 进了 master (对应后来的 3.x ),这个 bug fix 没有 cherry-pick 到 2.x 版本,于是官方发布的 2.x 版本的驱动依然有这个 bug 。这个 repo 很大下载下来要花很久,用 2.x 版本的同学觉得 build 麻烦的话可以用我 build 好的 2.x 版本的: pan.baidu.com/s/1kUQsBA ,你可以把它当做 selenium-server-standalone-2.53.1.jar 修复 bug 之后的版本。然后就是无限感叹,这个 bug 在 selenium rc 中才存在,如果你用来做爬虫时挂上代理时由于基本上代理不靠谱肯定会经常 timeout 然后触发这个 bug ,因为 Selenium 主要为前端测试存在所以这种 bug 没人关注也不奇怪,在 php 下用 selenium 做爬虫的并发场景下,这个 bug 没有解决前是不是就没人打通过呢。
一些可注意的地方
如果大家在 php 下用 Selenium 驱动 phantomjs 没有并发的话,其实可以完全抛弃 selenium-server-standalone-2.53.1.jar 这个服务端,因为 phantomjs 自带的 ghostdriver 自己就是个 Selenium 服务器端,调用方法见: MergEye/phpSelenium 。其实如果可以这么玩的话,那我 php 的并发爬虫进程中开一个 phantomjs 的子进程绑定一个该进程独有的端口,然后进程内部再用 Selenium 客户端去连接 phantomjs 子进程的端口,这样不仅可以绕开烦人的 selenium-server-standalone.jar ,还可以保证 php 进程退出后 phantomjs 子进程一定会退出。我这么测试了一下发现是可行的,但是有个问题就是 i7 下 phantomjs 的并发从 100 个降到了 15 个,所以这个方案适合无并发且不想单开一个 java 命令行应用的同学,因为它并发性能太差了。
由于 phantomjs 的依赖被静态编译进了二进制中(这一点做的非常好),在 win/linux 下使用时就是一个绿色版的二进制,非常方便。另外 phantomjs 的某些 Selenium API 存在一些 bug ,以及存在崩溃问题(实际上在 CPU 比较高的时候,别的浏览器也存在崩溃),这些问题我只能在业务上容错考虑进去,毕竟没有银弹。需要注意的一个 Selenium 的 session 对应一个浏览器实例,这些实例不是线程安全的,所以任何时候都只能有一个 Selenium 客户端在控制一个浏览器。
生产环境我实践过 Windows 和 Linux ( docker 下), Windows 下没啥好说的, docker 下由于爬虫需要经常更新所以我建议通过 git 来做(更新一点源码就重新 build docker 镜像然后分发是不值得的)。我们在 docker 的系统中安装好 git ,然后每次更新代码 exec/attach 后进入源码目录 git 手动更新代码就好了( build 镜像的时候把.git 目录 copy 进去,因为.git 目录下的东西是跨平台的,不过请务必注意autocrlf 问题),当然你也可以开 cron 定期更新。由于我的爬虫代码需要对源码目录有较多的写操作,而 docker 镜像的文件系统写消耗比较大,所以我采取了比较 dirty 的办法就是把源码挂载到主机目录上了(没用专门的数据卷纯粹觉得数据卷比较坑)。对了,用 compose 在生产环境 build 镜像是不对的。
我已经把 php 下 selenium 驱动 phantomjs 的并发方案都分享给大家了,更深入的东西实在是不好再说了,毕竟厂里养着我搞出来技巧也不能都分享了。如果有友商 /同好有 app 反编译爬虫相关的经验的话,非常愿意私下里多交流互补一下(我博客下联系),我们目前这块打算积累起来。
提高生产效率的几点
还有几个我认为对提高效率比较重要的点:
1 、把 xdebug 环境配置好。我把配置好 xdebug 环境放到了第一位是觉得这个可以大幅度提高开发效率,我们知道在 php-fpm 中的 php 进程是不能做长期爬虫的(就算使用 ignore_user_abort(true);set_time_limit(0);之类的也是没法保证稳定的),所以我们要用 php-cli 来开发爬虫。然而就我接触的大部分同学都知道在 web 开发中使用 xdebug 调试功能来开发,但是不知道在 php-cli 中也是可以使用 xdebug 的,具体可以参考: How can I debug a PHP CLI script with xdebug? 。因为 Selenium 操作是一步接一步的,很多时候我们只有通过单步调试就可以非常愉快的找出 bug ,以及继续往下写逻辑。
2 、开发时设置 fiddler 抓包。和 python 不同, PHP 的默认 http 不走系统默认的代理(至少 windows 下是这样的),所以如果你在开发爬虫的时候就算开了 fiddler 也是没法抓到包的,所以你需要在你使用的 httpclient 中显式设置代理为本地 fiddler 的代理端口。这么做的好处就是开发的时候每一个 http 请求都在掌控之中,如果可以的话可以把浏览器也设置为 fiddler 代理抓包,可以大幅提高开发效率。
3 、确保 IDE 的 typehint 可以用,使用第三方库的时候没 typehint 完全没法干活。另外许多 php 的 web 框架都带有为 php-cli 写的 console 应用脚手架,由于 web 框架本身把配置、路由和很多组件都包含进去了,如果在这些 web 框架的 console 应用脚手架上开发应该会省力很多。
最后谈谈我对爬虫工程师的理解
爬虫是任何内容提供商都需要面临的问题,据说在 Web 流量中有 60%是由爬虫贡献的,当然了如果是搜索引擎爬虫的流量那肯定是收到欢迎的。然而非搜索引擎的爬虫带来的流量让网站的内容流失,而且消耗服务器带宽和 CPU 甚至影响正常访问,简直百害无一利,所以反爬虫基本上大家都在做。作为一个爬虫工程师(我回去翻了一下我当初 offer 的 title 的确是“高级爬虫开发工程师”,虽然有时候我也打打杂干点别的),我相信入这一行的同好们很快就可以体会到,我们和网站搞反爬虫的后端们是谁也离不开谁得关系。后端不反爬虫的话,那扒东西随便找个刚毕业的菜鸟三下五除二就搞完了,我要感谢辛苦反爬虫的后端们。没有我们的话,后端的 KPI 和竞争力不是也会减少么,其实我老本行就是 PHP 后端来着,自己和自己相互理解了(笑)
然而爬虫工程师比较悲催的是,这是一个市场需求比较小的业界,技术很难积淀(比如两个要价 25K 的简历,一个写着 5 年 iOS 经验,一个写着 5 年爬虫经验,你觉得后者是不是比较搞笑),也很难拿出来和人交流,大部分工作也是体力活没啥技术含量,给人比较 low 的印象。业界搞得很出名的梁斌,你能找出比梁博还出名的搞爬虫的么?我基本上一和人谈起来梁博马上就有人跳出来说梁博水(梁博自己也在微博自嘲过),这其实也反映了我前面所说的爬虫工程师的悲催。
还有朋友总结说爬虫很难处于一个核心的业务,而且爬虫需要人长期维护(爬和反爬此消彼长,最后拼成本,对抗技术更新也比较快),比较累,而且觉得写爬虫好玩靠这个入门的新人一大堆,这块感觉很饱和。这些我觉得说得还是很有道理的,不过有一点我想补充的是简单的爬虫的确是体力活没啥技术含量,但是比较困难任务还是很难做的,因为业界没有通用的解决方案,你得自己摸索。我也很难把自己局限在“爬虫工程师”这么一个 title 上,只是码业务的话用别人做的基础组件经常觉得这么漂亮的工作别人都做好了,我去做的话肯定做不了这么好,所以缺乏去重复别人工作的动力也讨厌造轮子。不过转念一想,基础组件的存在就是为业务为需求服务的,所以在业务中沉淀技术,寻找有可能性的需求,说不定哪天能做出很漂亮的工作?