爬虫之网页分析
在编写爬虫的时候,我们需要对网页进行分析。这需要前端和后端知识的结合。
随着前端框架的进一步发展,为了安全性和代码的可维护性,很多前端使用js技术动态返回需要描写的页面,这就不是简单的urllib.request.urlopen能够访问的了。
于是有了selenium, marionette, puppeteer等浏览器外部接口的调用库。
众所周知,虽然selenium是一款常见的强大工具,但并不是无敌的。所谓“一招鲜吃遍天”的时代已经过去了。譬如有些网站设置一个简单的验证码门槛,你就要停下快速采集的脚步,处理这个令人头疼的问题——而生成复杂点的第一代验证码只需要几行代码——当前我使用过tesseract-ocr等识别技术,识别率均随网站作者的心情而浮动;有些网站限制了IP访问的频率,然而当你买了价值198元/月的代理借口想要一鼓作气拿下这个小网站的时候,骤然发现——开发人员觉得反正不会有很多人看这网站就破罐子破摔限制了所有IP访问的频率,于是你写的多线程高并发分布式统统不管用就是一潭死水。。。
但以上只是对现有普遍状况的一个客观描述。大数据时代,最有价值的就是数据。我们要接受爬虫的存在,也就要接受反爬虫的存在——如果我们能制定出反反爬虫的策略。
以网站为例,我们简单得使用Chromedriver模拟点击,会发现弹出了一个新的浏览器——这就有点为难了,我们还要进行主体的切换。继续手动测试发现,新浏览器的网址给出了详细的请求信息。于是考虑直接根据这个请求信息,改动最后一个p=
后面的参数。
难度稍微升级。
以网站为例。进入某一个项目发现,点击下一页后上面的请求URL没有发生变化。于是检查页面上元素的信息。一看发现,就是调用了一个js的回调函数。再仔细想想,结合js的知识,我们可以直接从这个函数的表达式中截取了有价值的部分,整合URL,直接发送请求。最后结果可以直接请求table中的数据,甚至都不用对上部的数据进行二次请求,大大降低了资源的需求。
稍微改变研究方向。
以网站为例。搜索某个关键字,调出了结果后,我们可以看到很多条。于是我们对这个题目信息进行采集时候发现,当然我们可以模拟点击下一页一直下去。但有没有更好的方法呢?观察到上方的URL在点击下一页的时候没有发生变化,因为是以.do封装的一个方法。这里稍微结合一点后端知识,或者直接百度可以发现,所谓的.do只是对发送的请求进行了一个封装。于是打开开发者工具后,我们点击下一次,就可以看到发出的网络资源的变化。对请求头的formatdata进行分析,整合到URL中,我们可以直接进行请求。而对参数的稍微修改,我们不仅可以直接请求某一页,我们还可以请求每页的个数——当然挑个大的来啦,这样可以少请求很多次,少了很多的开销。
再稍稍增加难度。我们来谈谈jsonpcallback。
以网站为例。其实这个网站抓取并不难,直接调用浏览器就可以采集数据。但是我最初犯了一个低级的错误:Chrome打开URL后直接就进行了抓取,没有发现这里的渲染需要较长的时间。于是在那里纠结了一会儿为什么总是定位不到元素后,我决定试试别的方法——这主要也是前端知识不够体系化的原因。接着我对网页的请求进行了分析。很显然,由于是调用js进行前端页面上数据的变化,所以可以预见的URL没有发生变化。老一套,打开开发者工具,点击下一页,我们可以看到网络上发起了一个新请求。直接找到这个资源的headers,拼接URL,我们就构成了一个完整的请求体。直接请求这个URL,我们可以发现返回一个函数形式的数据。直接解析就可以了。——讲讲我的曲折的探索史:首先我先到本地的sources中去寻找这个search这个脚本,然后分析其中的函数,一路曲曲折折找到了jsonpcallback的这部分,才发现这个完整的请求方式。最后发现,我们根本不需要关心代码是如何实现的,而只要看请求是如何发出的就好了——这部分开发者工具做的很人性化,很好地帮助我们直接定位。
以上。
author@cheng