爬虫实战:一个简易 Java 爬虫程序的实现

前面,我们分别介绍了爬虫程序的 3 个部分:源码下载、内容解析、页面链接提取。现在,让我们把这些部分串起来,构建一个完整的简易爬虫程序。

一、爬虫流程

构建程序前,我们首先需要了解爬虫的具体流程。

一个简易的爬虫程序,具备以下流程:

image

若以文字表述,就是:

  1. 从任务库(可以是 MySQL 等关系型数据库)选取种子 URL;

  2. 在程序中初始化一个 URL 队列,将种子 URL 加入到队列中;

  3. 若 URL 队列不为空,则位于队头的 URL 出队;若 URL 队列为空,则退出程序;

  4. 程序根据出列的 URL,反射出对应的解析类,同时新建线程,开始解析任务;

  5. 程序将下载 URL 指向的网页,并判断该页面是详情页还是列表页(如博客中的博客详情与博文列表),若为详情页,则解析出页面内容并入库,若为列表页,则提取出页面链接,加入到 URL 队列中;

  6. 解析任务完成后,重复第 3 步。


二、程序结构

我们已经清楚爬虫的具体流程,现在,我们需要一个合理的程序结构来实现它。

首先,介绍一下该简易爬虫程序的主要结构组成:

类名 作用
SpiderApplication.java 程序入口,负责任务调度,初始化 URL 队列,线程调度
DownloadService.java 下载服务类,负责下载 URL 指向的页面
PluginFactory.java 插件工厂,根据 URL 反射对应的插件类(解析类)
Plugin.java 插件注解类,用于插件(解析类)的注解
AbstractPlugin.java 抽象插件类,所有插件(解析类)的父类,包含公用方法
XmuPlugin.java 具体插件类,负责指定 URL 的解析任务

然后,再了解一下程序中的工具类与实体类。

类名 作用
LinkFilter.java 链接过滤接口,规范过滤方法
LinkExtractor.java 基于 htmlparser 的链接提取类
HttpUtil.java 封装 HttpClient 的工具类
CommonUtil.java 程序通用工具类
Task.java 任务对象
HttpParams.java http 请求对象
Proxy.java 代理配置类
StructData.java 结构化数据

最后,我们根据类的作用,将其放置到上述流程图中对应的位置中。具体的示意图如下所示:

image

现在,我们已经完成了实际流程到程序逻辑的转换。接下来,我们将通过源码的介绍,深入到程序中的各个细节。


三、任务调度、初始化队列

在简易爬虫程序中,任务调度、初始化队列都在 SpiderApplication 类中完成。

package main;

import entity.Task;
import factory.PluginFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import plugins.AbstractPlugin;

import java.util.*;
import java.util.zip.CRC32;

/**
 * 应用入口
 *
 * @author panda
 * @date 2017/10/28
 */
public class SpiderApplication {
   

    private static final Logger logger = LoggerFactory.getLogger(SpiderApplication.class);

    // URL 队列
    private static Queue<String> urlQueue = new LinkedList<String>();

    // URL 排重
    private static Map<Long, Integer> urlPool = new HashMap<Long, Integer>();


    public static void main(String[] args) {
   

        // 实例化任务对象
        Task task = Task.getBuilder()
                .setUrl("http://sm.xmu.edu.cn/")
                .build();

        // 初始化 URL 队列
        urlQueue.add(task.getUrl());
        addUrlPool(task.getUrl(), 1);

        // 循环调度
        String taskUrl;
        while ((taskUrl = urlQueue.poll()) != null) {
   
            logger.info("当前任务URL:" + taskUrl + ",当前层深:" + getDepth(taskUrl));

            try {
   
                task.setUrl(taskUrl);
                AbstractPlugin plugin = PluginFactory.getInstance().getPlugin(task);
                plugin.run();

                if (plugin.getUrlList() != null) {
   
                    int depth = getDepth(taskUrl) + 1;
                    for (String url : plugin.getUrlList()) {
   
                        if (!isUrlExist(url)) {
   
                            urlQueue.add(url);
                            addUrlPool(url, depth);
                        }
                    }
                }

                Thread.sleep(300);
            } catch (Exception e) {
   
                continue;
            }
        }

    }

    /**
     * 添加链接到 url 池
     *
     * @param url
     * @param depth
     */
    private static void addUrlPool(String url, int depth) {
   
        CRC32 c = new CRC32();
        c.update(url.getBytes());

        urlPool.put(c
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值