网页爬虫系统的设计

2,618 阅读7分钟
原文链接: miketech.it

网络爬虫,是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。当你需要大量的网络数据的时候,比如需要做一些数据分析,需要学习一些基于内容处理的算法的时候,爬虫程序就可以来为你抓取网站上的数据,人工一个页面一个页面的查找复制肯定不是办法,这个时候就需要编写爬虫来自动的为你去抓取网页数据。这篇博客将会讲述网页爬虫的设计。

前些天有一个需求就是从大众点评网站上抓取一点店铺数据作为推荐算法学习的数据,需要设计一个爬虫来为我获取这些店铺数据。根据要求,这个爬虫要在一个大众点评的根据地标进行店铺分类的页面获取地标的url,之后根据这些url抓取店铺的列表,之后根据列表来获取店铺的详情。

task

一般网页爬虫系统架构主要需要考虑以下方面

structure

  • 调度端:用来启动爬虫或者监控爬虫状态
  • URL管理器:用来存放和管理需要获取信息的链接,为网页下载器提供这些信息
  • 网页下载器:下载网页的源代码以供分析
  • 网页解析器:解析下载过后的源代码,分析出来相应的信息
  • 日志系统:网页解析器拿到相关数据后即为这个链接解析成功,存放到日志系统中,日志系统与URL管理器进行通讯来剔除不需要的URL

以上就是一个爬虫需要考虑的最简单的部分。接下来会谈论一下实现的细节,大众点评的爬虫我是用Java编写的所以在这里我将主要使用Java语言表述。

  • 调度端
    首先就是调度端啦,用来启动爬虫,为调度端传送不同的命令来控制爬虫,比如控制爬虫抓取一个店铺列表,或者抓取店铺详情,都是通过调度端运行的。
  • URL管理器
    URL管理器是必不可少的,他就像一个队列,用来存放需要抓取的URL,我一般会把需要抓取的URL存放在记事本里,URL管理器通过读取文本即可分析出来需要抓取的URL。
  • 网页下载器和网页解析器
    这两个部分可以放在一块,因为Java中可以直接请求道网页的源代码而不用下载到本地的磁盘中,关于解析器:我推荐使用Jsoup。Jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。Jsoup的主要功能如下:

    1. 从一个URL,文件或字符串中解析HTML;
    2. 使用DOM或CSS选择器来查找、取出数据;
    3. 可操作HTML元素、属性、文本;

    比如我想解析下面这个网页的店铺名称

    可以打开浏览器的开发者工具,分析出来店铺名称在main标签下有一个id为basic-info的标签,这个标签中有一个shop-name标签,这个标签的内容就是店铺名称。当然有的地方不好分析的也要用到正则表达式来分析字段。

inspect

在Jsoup的帮助下只要几行代码就能解决:

Document doc =
        Jsoup.connect(requestUrl).
                header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0").get();


Element basicBody = doc.getElementById("basic-info");
String shopName = basicBody.getElementsByClass("shop-name").get(0).childNode(0).toString().trim();
System.out.println(shopName);

注意header一定要设置,不然的话有的网站是会因为没有完整的头信息直接403拒绝访问爬虫的请求的。

  • 日志系统日志系统和URL管理器同样可以储存在记事本中,通过比对日志文本和URL管理器的文本的不同就能得出哪些URL已经被抓去过而哪些没有。还可以单独在写一段代码来存放无效的URL,可以用来人工抓取。

以上就是一些细节,接下来来讨论下可能会遇到的问题:

  • 网站403拒绝访问:
    现在的网站肯定都为了防止爬虫系统抓取他们的数据而做了处理,如果爬虫在短时间内访问了网站的大量连接,对方的服务器会发现,并且封掉你的IP,这时候你只会收到403拒绝访问,可能在30分钟或一段时间之后爬虫才能恢复对网站的访问。这个时候可以让爬虫休息一段时间在进行工作,Java的异常处理机制为我们提供了便利,我们可以通过异常捕获的方法来得知403错误,通过捕获一个HttpStatusException来分析是否是被对方服务器发现而拒绝访问。从而暂停爬虫的工作,当然也可以在捕获这个异常之后通过一段代码来更换代理IP,如果有大量IP的话。
try{
    //连接并解析Html
}catch (HttpStatusException e){//异常捕获
    if (e.getStatusCode()==403){
        try {
            logUtil.writeToHttpStatesException(); //日志写入
            System.out.println("HttpStatusException! Program will sleep for "+configuration.forbiddenSleepTime/1000/60+"min.");
            Thread.sleep(configuration.forbiddenSleepTime); //进程休眠
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }
    
}

这个时候日志系统的用途就体现出来了,日志系统无形中提升了爬虫的稳定性,在抓取的时候可能会遇到各种异常,Socket超时,Http状态错误,数组越界,空指针等等,如果爬虫因为未知的错误而停止了运行,因为短期开发的过程中不可能想到所有可能出现的错误情况,第二次爬虫重新启动之后可以读取日志,这样就不会因为一个崩溃错误而导致爬虫把抓取过的信息再抓取一遍了。

  • 并发需求
    刚在说了,快速的访问会导致403拒绝访问,我抓取大众点评的时候,每抓取一次休息5秒钟才不会被服务器拒绝访问,所以说这样算下来,爬虫的效率是很慢的,一分钟最多十几个店铺,但是如果需要抓取很多店铺,这就需要好几天了。所以说,编写的爬虫系统必须具备横向扩展能力,用更多的机器同时抓取来节省宝贵的时间。比如有10台空闲机器,我们可以将爬虫分别部署在10台机器上进行抓取,这个时候就需要更改URL管理器让其变得更加智能,可以增加URL任务分配相关的代码,比如我有1000个网页需要抓取,有5台电脑,URL管理器应该为第一台电脑分配抓取1-200个URL,第二个机器201-400,以此类推,这样的代码也很好写,小学数学问题就可以解决。
  • 工具类设计
    爬虫设计完之后还需要设计一些小工具类来完成一些基本操作,比如文件合并,用来合并多台机器上抓取的信息,条目去重复,用来删掉重复抓取的店铺(可能的话),过滤器:用来过滤不要的店铺之类的。这样的工具类也是必不可少的。

 

以下是我设计的爬虫系统的结构:

manager

因为我的爬虫需要抓取三种不同的网页,所以我设计了一个CrawlerManager类来管理调度不同类型的爬虫,这个Manager使得整个爬虫系统更加易于维护和调整,并且使得整个程序的逻辑结构更加清楚;

之后就可以开始抓取啦~

terminal

data

以上店铺数据仅为程序测试用,并未作为商业用途使用,如构成侵权,请直接留言,我会删除文章。

 

我的博客MikeTech app现已登陆iPhone和Android

iPhone版下载
Android版下载