作者:鲍沛泽

下课的时间难免会跟同学开开玩笑,有时候我就会用爬虫爬点资源给同学看,可是写了一些就会渐渐发现网上的很多教程和现有的成熟框架都是基于Python或Java的,比如Scrapy等。

为什么与之相比,很少有人会用C++写爬虫呢?

后来有幸在调用一些REST API时发现了一些C++的网络库,发现他们其实封装十分完善,调用方便,如果代码编写合理,爬取数据还是能和Python、Java一样简单的。

但最终C++爬虫没有流行起来

主要是因为以下几点原因

1. 缺乏优秀框架。虽然C++本身的特性使得其代码运行的效率很高,但它对于内存的管控要求同样很高。如果没有良好的编程风格和编程习惯,经常出现爬虫运行不了太久,程序就因为各种原因崩溃了。而且C++不原生支持网络库以及C++11之前语法的繁杂也导致了大家懒得去给C++开发相关的框架。


2. C++终究不是一门脚本语言。有时候仅仅是更改一个简单的功能就要重新编译整个项目,而脚本语言直接改掉局部语句,就可以再次飞快的进入调试运行阶段。虽然运行效率稍低,但即使是Java在编译速度上也是可以甩C++几条街的,爬虫终究不是要在服务器上7×24小时运行的大工程,相比较而言编译的比重仍比运行要大。

3. C++的程序太长。C++的程序废话,相信大家在大一的“C++程序设计”课程中已经有了深刻的体会。以对于字符串的处理为例,如果不依赖于其他库的话,仅用std::string,Python的1句话C++至少就要写3句。遵循简洁而不高效一直是当代程序员们所奉行的生活准则,毕竟编写的时间是自己的,运行的时间是用户的。

4. 对于大部分爬虫来说,爬虫的瓶颈在于网络I/O。除非在高并发环境下C++的效率会比Python高,否则耗费你程序大部分时间的永远不是对数据的清洗与归类。


尺有所短,寸有所长。即使是这样多的麻烦,也不影响在某些特定情况下C++的优势。比如对于大型企业,他们的网络爬虫对于性能的极其强调,而且有能力造一个不错的轮子,这导致了他们有时也愿意使用C++来开发爬虫。 更重要的是,C++对于爬虫底层的探索仍是我们需要借鉴的重点,也是本文接下来的重心所在。

原理

爬虫从原理上来,简单来讲就是发送http/https请求+解析html页面,获取页面上的目标数据。无论采用何种语言,都需要和server建立若干个TCP连接,再将http请求写入TCP socket中,等待server返回数据。

它本质上就是一个事件循环(event loop),用伪代码来描述基本上就是这样:

1. 初始化epoll,与server建立TCP连接。

2. 从请求队列(如你要爬取的网站列表)中取出url和http请求。

3. 将http请求写入到这个TCP socket中,并把这个socket加入epoll中。

4. 检查活动事件(epoll_wait)。

5. 分析处理事件(如html页面),然后把剩余未处理过的url和http继续放回队列中供下一次爬取调用(如果你写的爬虫只是简单的单线程串行可以不用放回)。

6. 回到第2步。

当程序从请求队列里拿到一个url后,需要去下载与这个url对应的页面,进而解析出真正需要的数据,然后才能把它的下一层url加入队列中,这是一种典型的阻塞程序。但由于爬虫是网络I/O密集型程序,将代码写成非阻塞式的多线程程序并不会使运行效率产生飞跃式的提升,相反甚至可能会造成请求队列的调用混乱,这也为什么爬虫要用基于事件的方法来写的原因。


分析

为了使大家更好的理解C++爬虫的底层机制,我们不采用目前比较方便的整套socket爬虫解决方案,转向微软给我们内置的库来实现目标。

作为一个优秀的爬虫,它首先需要实现http的访问,主要流程分为以下几步

1. 首先我们需要打开一个Session来获得一个HINTERNET session句柄。

2. 然后我们需要使用这个session句柄与服务器连接来得到一个HINTERNET connect句柄。

3. 再往后我们又要使用这个connect句柄来打开Http请求,以得到一个HINTERNET request句柄。

4. 于是再后我们就可以使用这个request句柄来发送数据和读取从服务器返回的数据。

5. 最后存储我们得到的数据以待进一步分析,依次关闭request,connect,session句柄,收工。

为了实现这五个步骤,微软给我们提供了两套用于http访问的接口:WinHttp和WinINet。相比较而言WinHttp比WinINet更加安全和健壮,因此也可以认为WinHttp是WinINet的升级版本。这两套API都包含了很多相似的函数与宏定义,且访问的流程也是完全类似的。因此我们主要通过WinHTTP实现post请求方法,并按照上述5个步骤进行逐步分析。


为什么不用java开发上位机 为什么不用java爬虫_java爬虫框架

总结

通过对这五个步骤的具体实现,我们可以详细的看到C++爬取网页的底层运作机制——虽然复杂但又井然有序。

正如上文所说,当爬取数据的活动事件(epoll_wait)结束以后,紧接着的就是对数据的清洗与分析。 但事实上除了对原理本身的格物致知以外,更多时候的我们仅仅只希望简单快速的上手,浮光掠影,流于表面。不得不承认时代的发展与变迁使得底层被越来越尘封于目光不可及之处,有时简简单单的几步就能完成前人绞尽脑汁几个月的思考。在我看来,无论未来的代码发展方向是何处,都是一种美。

为什么不用java开发上位机 为什么不用java爬虫_为什么不用java开发上位机_02