爬虫实战:基于 HtmlParser 实现网页链接的提取

爬虫程序的第三步,是提取页面链接。

页面链接的提取,是爬虫程序中非常关键的一部分。一个完整的爬虫程序,要能从种子 URL 出发,逐步遍历子节点中的所有页面。就比如我们想采集微博内容时,不能仅仅采集第一页的内容,而要实现从第一页开始一直采集到尾页。

本篇主要介绍一款能提取网页链接的强大类库,HtmlParser。


HtmlParser

HtmlParser 是一个通过线性和嵌套两种方式来解析网页的 Java 开源类库,主要用于网页元素的转换以及网页内容的抽取。

HtmlParser 具备如下特性:

  • 过滤器
  • 访问者模式
  • 自定义标签
  • 易于使用的 Java 组件

正如官网简介里所述,HtmlParser 是一个快速的、健壮的、经过严格测试的工具包。


NodeFilter

HtmlParser 具备过滤器的特性,我们可以通过这个特性过滤并提取网页中的链接。

HtmlParser 中与过滤相关的基本接口是 NodeFilter,接口中只定义了一个方法。

package org.htmlparser;

import java.io.Serializable;
import org.htmlparser.Node;

public interface NodeFilter extends Serializable, Cloneable {
    boolean accept(Node var1);
}

该方法的作用是,对于想要保留的节点,返回 true;对于满足过滤条件、需要过滤掉的节点,返回 false。

HtmlParser 本身就提供了多种实现 NodeFilter 接口的过滤器,如下表所示:

类别 类名
逻辑运算类 AndFilter
NotFilter
OrFilter
判断类 HasAttributeFilter
HasChildFilter
HasParentFilter
HasSiblingFilter
IsEqualFilter
TagNameFilter
其他 CssSelectorNodeFilter
LinkRegexFilter
LinkStringFilter
NodeClassFilter
RegexFilter
StringFilter


当然,开发人员也可以自定义 Filter,用于实现一些特殊情况的过滤。


简易链接提取器

使用 HtmlParser 实现链接提取,需要以下步骤:

  • 使用 url 或者网页源码创建一个 Parser 对象;
  • 构建满足需求的过滤器对象;
  • 通过 Parser 的 extractAllNodesThatMatch(NodeFilter filter) 方法提取过滤后的节点;
  • 通过节点获取链接信息。

以下是 HtmlParser 提取网页链接的具体示例:

package filter;

import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.tags.FrameTag;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * 链接提取器
 *
 * @author panda
 * @date 2017/10/28
 */
public class LinkExtractor {

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

    public static List<String> extractLinks(String body, LinkFilter filter) {

        List<String> linkList = new ArrayList<String>();

        try {

            Parser parser = new Parser(body);

            OrFilter linkFilter = new OrFilter(
                    new NodeClassFilter[]{
                            new NodeClassFilter(LinkTag.class),
                            new NodeClassFilter(FrameTag.class)
                    }
            );
            NodeList nodeList = parser.extractAllNodesThatMatch(linkFilter);

            if (nodeList != null) {
                logger.info("发现链接个数:" + nodeList.size());
            }

            for (int i = 0; i < nodeList.size(); i++) {

                Node node = nodeList.elementAt(i);
                String linkUrl;

                if (node instanceof LinkTag) {
                    LinkTag link = (LinkTag) node;
                    linkUrl = link.getAttribute("HREF");
                } else {
                    FrameTag frame = (FrameTag) node;
                    linkUrl = frame.getFrameLocation();
                }

                // 如果有自定义过滤器,则增加自定义过滤条件
                if (filter != null && linkUrl != null) {
                    if (!filter.accept(linkUrl)) {
                        linkUrl = null;
                    }
                }

                if (linkUrl == null || "".equals(linkUrl) || "#".equals(linkUrl) || linkUrl.startsWith("javascript")) {
                    continue;
                }

                // 防止链接重复
                if (!linkList.contains(linkUrl)) {
                    linkList.add(linkUrl);
                }
            }

            if (linkList != null) {
                logger.info("提取链接个数:" + linkList.size());
            }

        } catch (Exception e) {
            logger.error("提取链接异常:", e);
        }

        return linkList;
    }

}

我们编写一个简单的单元测试进行测试。

@Test
public void testExtractLinksWithoutFilter() {
    String body = HttpUtil.executeGetRequest("http://sm.xmu.edu.cn/");
    List<String> linkList = LinkExtractor.extractLinks(body, null);
    for (int i = 0; i < linkList.size(); i++) {
        System.out.println("linkUrl:" + linkList.get(i));
    }
}

输出结果如下:

INFO - 发现链接个数:148
INFO - 提取链接个数:131
linkUrl:http://sm.xmu.edu.cn/html/mp/
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=lists&catid=432
linkUrl:http://sm.xmu.edu.cn/html/english/
linkUrl:http://sm2.xmu.edu.cn/default2.asp
linkUrl:http://sm.xmu.edu.cn/
linkUrl:http://sm.xmu.edu.cn/html/about/overview/overview/
linkUrl:http://sm.xmu.edu.cn/html/about/message/
linkUrl:http://sm.xmu.edu.cn/html/about/leaders/
linkUrl:http://sm.xmu.edu.cn/html/about/department/
linkUrl:http://sm.xmu.edu.cn/html/about/structure/
linkUrl:http://sm.xmu.edu.cn/html/about/service/
linkUrl:http://sm.xmu.edu.cn/html/about/contact/
linkUrl:http://sm.xmu.edu.cn/html/current_students/
linkUrl:http://sm.xmu.edu.cn/keyan/TeacherWeb/Teacher_Special.aspx
linkUrl:http://sm.xmu.edu.cn/html/research/research_news/
linkUrl:http://sm.xmu.edu.cn/html/research/academic/
linkUrl:http://sm.xmu.edu.cn/html/research/research_center/
linkUrl:http://sm.xmu.edu.cn/html/intl/
linkUrl:http://sm.xmu.edu.cn/html/intl/overview/
linkUrl:http://sm.xmu.edu.cn/html/intl/authentication/
linkUrl:http://sm.xmu.edu.cn/html/intl/news/
linkUrl:http://sm.xmu.edu.cn/html/intl/student/
linkUrl:http://sm.xmu.edu.cn/html/intl/2_2/
linkUrl:http://sm.xmu.edu.cn/html/intl/International_students/
linkUrl:http://sm.xmu.edu.cn/html/intl/guide/
linkUrl:http://sm.xmu.edu.cn/html/intl/contact/
linkUrl:http://smcareer.xmu.edu.cn/
linkUrl:http://sm-alumni.xmu.edu.cn/
linkUrl:http://sm.xmu.edu.cn
linkUrl:https://xmu.higheredtalent.org/Login
linkUrl:http://pme.xmu.edu.cn/
linkUrl:http://sm.xmu.edu.cn/html/programs/
linkUrl:http://sm.xmu.edu.cn/html/programs/ung/
linkUrl:http://sm.xmu.edu.cn/html/programs/master/
linkUrl:http://sm.xmu.edu.cn/html/programs/phd/
linkUrl:http://mba.xmu.edu.cn/
linkUrl:http://emba.xmu.edu.cn/
linkUrl:http://www.xmuedp.com/
linkUrl:http://sm.xmu.edu.cn/html/about/department/MPAcc/
linkUrl:http://meem.xmu.edu.cn/
linkUrl:http://sm.xmu.edu.cn/html/about/department/mta/
linkUrl:http://smice.xmu.edu.cn/
linkUrl:http://sm.xmu.edu.cn/html/programs/bsh/
linkUrl:http://sm.xmu.edu.cn/html/about/department/bm/class/
linkUrl:http://femba.xmu.edu.cn
linkUrl:http://ifas.xmu.edu.cn/cms/Channel.aspx?ID=147
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=3103
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2982
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2975
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2923
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2914
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2896
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2873
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2872
linkUrl:http://sm.xmu.edu.cn/html/jwxx/
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3278
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3267
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3246

……

如果我们想添加自定义的过滤规则,比如只保留当前域名下的链接,可通过以下测试示例实现:

@Test
public void testExtractLinksWithFilter() {
    String body = HttpUtil.executeGetRequest("http://sm.xmu.edu.cn/");
    LinkFilter filter = new LinkFilter() {
        public boolean accept(String link) {
            return link.contains("sm.xmu.edu.cn");
        }
    };
    List<String> linkList = LinkExtractor.extractLinks(body, filter);
    for (int i = 0; i < linkList.size(); i++) {
        System.out.println("linkUrl:" + linkList.get(i));
    }
}

输出结果如下:

INFO - 发现链接个数:148
INFO - 提取链接个数:107
linkUrl:http://sm.xmu.edu.cn/html/mp/
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=lists&catid=432
linkUrl:http://sm.xmu.edu.cn/html/english/
linkUrl:http://sm.xmu.edu.cn/
linkUrl:http://sm.xmu.edu.cn/html/about/overview/overview/
linkUrl:http://sm.xmu.edu.cn/html/about/message/
linkUrl:http://sm.xmu.edu.cn/html/about/leaders/
linkUrl:http://sm.xmu.edu.cn/html/about/department/
linkUrl:http://sm.xmu.edu.cn/html/about/structure/
linkUrl:http://sm.xmu.edu.cn/html/about/service/
linkUrl:http://sm.xmu.edu.cn/html/about/contact/
linkUrl:http://sm.xmu.edu.cn/html/current_students/
linkUrl:http://sm.xmu.edu.cn/keyan/TeacherWeb/Teacher_Special.aspx
linkUrl:http://sm.xmu.edu.cn/html/research/research_news/
linkUrl:http://sm.xmu.edu.cn/html/research/academic/
linkUrl:http://sm.xmu.edu.cn/html/research/research_center/
linkUrl:http://sm.xmu.edu.cn/html/intl/
linkUrl:http://sm.xmu.edu.cn/html/intl/overview/
linkUrl:http://sm.xmu.edu.cn/html/intl/authentication/
linkUrl:http://sm.xmu.edu.cn/html/intl/news/
linkUrl:http://sm.xmu.edu.cn/html/intl/student/
linkUrl:http://sm.xmu.edu.cn/html/intl/2_2/
linkUrl:http://sm.xmu.edu.cn/html/intl/International_students/
linkUrl:http://sm.xmu.edu.cn/html/intl/guide/
linkUrl:http://sm.xmu.edu.cn/html/intl/contact/
linkUrl:http://sm.xmu.edu.cn
linkUrl:http://sm.xmu.edu.cn/html/programs/
linkUrl:http://sm.xmu.edu.cn/html/programs/ung/
linkUrl:http://sm.xmu.edu.cn/html/programs/master/
linkUrl:http://sm.xmu.edu.cn/html/programs/phd/
linkUrl:http://sm.xmu.edu.cn/html/about/department/MPAcc/
linkUrl:http://sm.xmu.edu.cn/html/about/department/mta/
linkUrl:http://sm.xmu.edu.cn/html/programs/bsh/
linkUrl:http://sm.xmu.edu.cn/html/about/department/bm/class/
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=3103
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2982
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2975
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2923
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2914
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2896
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2873
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2872
linkUrl:http://sm.xmu.edu.cn/html/jwxx/
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3278
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3267
linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3246

……

我们发现,自定义的过滤器起到了作用,程序只提取了当前域名下的链接。

猜你喜欢

转载自blog.csdn.net/magicpenta/article/details/78680008