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

来源:互联网 发布:贝克汉姆 帅 知乎 编辑:程序博客网 时间:2024/06/07 03:13

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

页面链接的提取,是爬虫程序中非常关键的一部分。一个完整的爬虫程序,要能从种子 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;    }}

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

@Testpublic 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 - 发现链接个数:148INFO - 提取链接个数:131linkUrl:http://sm.xmu.edu.cn/html/mp/linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=lists&catid=432linkUrl:http://sm.xmu.edu.cn/html/english/linkUrl:http://sm2.xmu.edu.cn/default2.asplinkUrl: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.aspxlinkUrl: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.cnlinkUrl:https://xmu.higheredtalent.org/LoginlinkUrl: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.cnlinkUrl:http://ifas.xmu.edu.cn/cms/Channel.aspx?ID=147linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=3103linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2982linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2975linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2923linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2914linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2896linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2873linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2872linkUrl:http://sm.xmu.edu.cn/html/jwxx/linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3278linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3267linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3246……

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

@Testpublic 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 - 发现链接个数:148INFO - 提取链接个数:107linkUrl:http://sm.xmu.edu.cn/html/mp/linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=lists&catid=432linkUrl: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.aspxlinkUrl: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.cnlinkUrl: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=3103linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2982linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2975linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2923linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2914linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2896linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2873linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=149&id=2872linkUrl:http://sm.xmu.edu.cn/html/jwxx/linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3278linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3267linkUrl:http://sm.xmu.edu.cn/index.php?m=content&c=index&a=show&catid=150&id=3246……

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

阅读全文
0 0
原创粉丝点击