`

搜索引擎Nutch源代码研究之一 网页抓取(4)

阅读更多
今天来看看Nutch如何Parse网页的:
Nutch使用了两种Html parser工具(NekoHTML和TagSoup)来实现html的提取,这两种工具是可通过配置来选择的。
当然你要自己实现Parser你还可以选择HTMLParser[基于visitor访问者模式同时也提供了Event driver的接口]来
提取网页。如果你用惯了XML一套处理方法,使用NekoHTML和TagSoup应该会比较顺手的。
我们来看看类public class HtmlParser implements Parser的实现:
首先为了更好的理解下面的代码先看看成员变量:
 private static final int CHUNK_SIZE = 2000;
  private static Pattern metaPattern =
    Pattern.compile("<meta\\s+([^>]*http-equiv=\"?content-type\"?[^>]*)>",
                    Pattern.CASE_INSENSITIVE);
  private static Pattern charsetPattern =
    Pattern.compile("charset=\\s*([a-z][_\\-0-9a-z]*)",
                    Pattern.CASE_INSENSITIVE);
  
  private String parserImpl;

CHUNK_SIZE提取html meta tag部分的html片断的长度,一般meta tag没有超过2000bytes的,所以只需要从这部分
提取就行了
metaPattern为meta tag匹的正则模式
charsetPattern为字符集编码的正则模式
parserImpl是具体使用的是NekoHTML还是TagSoup来parser html.如果parserImpl为"tagsoup"就使用TagSoup,否则就使用NekoHTML。
用来从html在meta tag里面提取出charset或Content-Type中指定的编码:
length限定在meta tag部分提取,通过正则表达式很容易提取出编码
private static String sniffCharacterEncoding(byte[] content) {
    int length = content.length < CHUNK_SIZE ? 
                 content.length : CHUNK_SIZE;

    // We don't care about non-ASCII parts so that it's sufficient
    // to just inflate each byte to a 16-bit value by padding. 
    // For instance, the sequence {0x41, 0x82, 0xb7} will be turned into 
    // {U+0041, U+0082, U+00B7}. 
    String str = new String(content, 0, 0, length); 

    Matcher metaMatcher = metaPattern.matcher(str);
    String encoding = null;
    if (metaMatcher.find()) {
      Matcher charsetMatcher = charsetPattern.matcher(metaMatcher.group(1));
      if (charsetMatcher.find()) 
        encoding = new String(charsetMatcher.group(1));
    }

    return encoding;
  }

最重要的一个方法是:
public Parse getParse(Content content)
这个方法返回了包含了提取所有结果Parse对象:
这个方法写的比较长,近100行,其实整个方法可以分解成几个小方法:
提取base url,提取encoding,根据提取出的编码提取content,提取meta tags,提取outlinks,最后根据提取得到的
text和parseDate构造Parse对象
下面我们一个一个看:
提取base url
URL base;
    try {
      base = new URL(content.getBaseUrl());
    } catch (MalformedURLException e) {
      return new ParseStatus(e).getEmptyParse(getConf());
    }

提取encoding:
      //直接从content中的metadata中提取
      byte[] contentInOctets = content.getContent();
      InputSource input = new InputSource(new ByteArrayInputStream(contentInOctets));
      String contentType = content.getMetadata().get(Response.CONTENT_TYPE);
      String encoding = StringUtil.parseCharacterEncoding(contentType);
      if ((encoding != null) && !("".equals(encoding))) {
        metadata.set(Metadata.ORIGINAL_CHAR_ENCODING, encoding);
        if ((encoding = StringUtil.resolveEncodingAlias(encoding)) != null) {
          metadata.set(Metadata.CHAR_ENCODING_FOR_CONVERSION, encoding);
          if (LOG.isTraceEnabled()) {
            LOG.trace(base + ": setting encoding to " + encoding);
          }
        }
      }
     //如果从metadata中没有提取到,使用前面sniffCharacterEncoding从meta tag提取
      // sniff out 'charset' value from the beginning of a document
      if ((encoding == null) || ("".equals(encoding))) {
        encoding = sniffCharacterEncoding(contentInOctets);
        if (encoding!=null) {
          metadata.set(Metadata.ORIGINAL_CHAR_ENCODING, encoding);
          if ((encoding = StringUtil.resolveEncodingAlias(encoding)) != null) {
            metadata.set(Metadata.CHAR_ENCODING_FOR_CONVERSION, encoding);
            if (LOG.isTraceEnabled()) {
              LOG.trace(base + ": setting encoding to " + encoding);
            }
          }
        }
      }
      //如果还没有提取到,使用默认的编码
      if (encoding == null) {
        // fallback encoding.
        // FIXME : In addition to the global fallback value,
        // we should make it possible to specify fallback encodings for each ccTLD.
        // (e.g. se: windows-1252, kr: x-windows-949, cn: gb18030, tw: big5
        // doesn't work for jp because euc-jp and shift_jis have about the
        // same share)
        encoding = defaultCharEncoding;
        metadata.set(Metadata.CHAR_ENCODING_FOR_CONVERSION, defaultCharEncoding);
        if (LOG.isTraceEnabled()) {
          LOG.trace(base + ": falling back to " + defaultCharEncoding);
        }
      }

设置好编码方式,从content中提取DocumentFragment
 input.setEncoding(encoding);
      if (LOG.isTraceEnabled()) { LOG.trace("Parsing..."); }
      root = parse(input);
    } catch (IOException e) {
      return new ParseStatus(e).getEmptyParse(getConf());
    } catch (DOMException e) {
      return new ParseStatus(e).getEmptyParse(getConf());
    } catch (SAXException e) {
      return new ParseStatus(e).getEmptyParse(getConf());
    } catch (Exception e) {
      e.printStackTrace(LogUtil.getWarnStream(LOG));
      return new ParseStatus(e).getEmptyParse(getConf());
    }

提取meta tag,并检查meta指令
   HTMLMetaProcessor.getMetaTags(metaTags, root, base);
    if (LOG.isTraceEnabled()) {
      LOG.trace("Meta tags for " + base + ": " + metaTags.toString());
    }
    // check meta directives
    if (!metaTags.getNoIndex()) {               // okay to index
      StringBuffer sb = new StringBuffer();
      if (LOG.isTraceEnabled()) { LOG.trace("Getting text..."); }
      utils.getText(sb, root);          // extract text
      text = sb.toString();
      sb.setLength(0);
      if (LOG.isTraceEnabled()) { LOG.trace("Getting title..."); }
      utils.getTitle(sb, root);         // extract title
      title = sb.toString().trim();
    }

提取出outlinks:
if (!metaTags.getNoFollow()) {              // okay to follow links
      ArrayList l = new ArrayList();              // extract outlinks
      URL baseTag = utils.getBase(root);
      if (LOG.isTraceEnabled()) { LOG.trace("Getting links..."); }
      utils.getOutlinks(baseTag!=null?baseTag:base, l, root);
      outlinks = (Outlink[])l.toArray(new Outlink[l.size()]);
      if (LOG.isTraceEnabled()) {
        LOG.trace("found "+outlinks.length+" outlinks in "+content.getUrl());
      }
    }

构建parse对象:
ParseStatus status = new ParseStatus(ParseStatus.SUCCESS);
    if (metaTags.getRefresh()) {
      status.setMinorCode(ParseStatus.SUCCESS_REDIRECT);
      status.setMessage(metaTags.getRefreshHref().toString());
    }
    ParseData parseData = new ParseData(status, title, outlinks,
                                        content.getMetadata(), metadata);
    parseData.setConf(this.conf);
    Parse parse = new ParseImpl(text, parseData);

    // run filters on parse
    parse = this.htmlParseFilters.filter(content, parse, metaTags, root);
    if (metaTags.getNoCache()) {             // not okay to cache
      parse.getData().getParseMeta().set(Nutch.CACHING_FORBIDDEN_KEY, cachingPolicy);
    }

下面这个方法根据parserImpl字段,使用NekoHTML或TagSoup来提取content得到DocumentFragment对象
 private DocumentFragment parse(InputSource input) throws Exception {
    if (parserImpl.equalsIgnoreCase("tagsoup"))
      return parseTagSoup(input);
    else return parseNeko(input);
  }

网页抓取部分到此基本结束,必要的部分相应再作补充。等研究好google的map-reduce再继续其他部分。
分享到:
评论
3 楼 xieyijiejie 2014-04-17  
你好,我的nutch2.2.1项目在调试的时候怎么没走htmlparser呀,但是存储到数据库的数据中text是有的,觉得很奇怪呀,我还在htmlparser中设置了断点
2 楼 fuliang 2013-08-26  
koubi1986 写道
你好!请教一些问题:
请问一下
1。你是如何把nutch抓取到的二进制内容,在项目中读取的。
2。nutch抓取时候,发现有很多url没有被成功抓取过来,请问你做的时候,有什么   办法是提高成功率。
3。对抓取过来内容你们是如何进行关键词搜索

希望群主可以帮忙解答一下。万分感激!
email:83132614@qq.com


1、可以通过bin/nutch readseg查看,或者通过nutch提供的API,具体格式可以参考:http://wiki.apache.org/nutch/NutchFileFormats
2、主要是防止被封,多设置一些代理的ip
3、可以直接用Lucene或者Solr。
1 楼 koubi1986 2013-08-20  
你好!请教一些问题:
请问一下
1。你是如何把nutch抓取到的二进制内容,在项目中读取的。
2。nutch抓取时候,发现有很多url没有被成功抓取过来,请问你做的时候,有什么   办法是提高成功率。
3。对抓取过来内容你们是如何进行关键词搜索

希望群主可以帮忙解答一下。万分感激!
email:83132614@qq.com

相关推荐

    搜索引擎nutch配置

    从Apache官方网站下载Nutch的最新源代码,通常通过Git克隆仓库。解压后,进入Nutch的工作目录。 3. **配置Nutch** 打开`conf/nutch-site.xml`文件,这是Nutch的主要配置文件。以下是一些关键配置项: - `...

    Lucene+nutch搜索引擎开发(源代码)

    2. **Nutch源代码**:包括Nutch的爬虫模块、索引模块和搜索模块,可以帮助开发者学习如何配置和运行一个完整的网络爬虫,以及如何与Lucene集成进行全文检索。 3. **示例项目**:可能包含了一些示例应用,展示如何...

    nutch-2.1源代码

    Nutch不仅仅是一个搜索引擎,它还包含了一个Web爬虫,能够抓取互联网上的网页,并对抓取的数据进行索引和处理。 Nutch的源代码包含了整个项目的完整实现,包括爬虫、索引器、搜索器以及相关的配置和文档。这对于...

    nutch的源代码解析

    Nutch 的源代码解析对于深入理解搜索引擎的工作原理以及自定义搜索引擎的实现非常有帮助。下面我们将详细探讨 Nutch 的注入(Injector)过程,这是整个爬取流程的第一步。 Injector 类在 Nutch 中的作用是将输入的 ...

    Lucene+Nutch搜索引擎开发.王学松源代码

    总的来说,王学松的“Lucene+Nutch搜索引擎开发实例代码”是一份宝贵的教育资源,它可以帮助开发者快速入门搜索引擎开发,并深入了解这两个项目的内部工作机制。通过实践这些代码,不仅可以提升技术能力,还能为构建...

    分布式搜索引擎nutch开发

    分布式搜索引擎Nutch开发详解 Nutch是一款开源的、基于Java实现的全文搜索引擎,它主要用于构建大规模的网络爬虫系统,并提供了对抓取的网页进行索引和搜索的功能。Nutch与Hadoop紧密集成,能够充分利用分布式计算...

    lucene+nutch搜索引擎开发源码1

    《lucene+nutch搜索引擎开发源码1》是一个包含开源搜索引擎项目Lucene和Nutch源代码的压缩包,主要针对搜索引擎开发的学习和实践。这个压缩包是书籍《lucene+nutch搜索引擎开发》的一部分,由于源码量较大,因此分为...

    lucene+nutch开发自己的搜索引擎一书源代码

    《lucene+nutch开发自己的搜索引擎一书源代码》是一份专为初学者设计的资源,旨在教授如何利用Apache Lucene和Nutch构建自定义搜索引擎。Lucene是Java编写的一个高性能全文检索库,而Nutch则是一个开源的网络爬虫...

    nutch 1.5的源代码

    Nutch 1.5 是一个基于Java开发的开源搜索引擎项目,它主要负责网络抓取、索引和搜索等功能。这个源代码包包含了实现这些功能的所有模块和组件,为开发者提供了深入理解搜索引擎工作原理以及定制化搜索引擎的机会。接...

    nutch开发资料 搜索引擎

    Nutch是Apache软件基金会的一个开源项目,主要用于构建网络搜索引擎。这个开发资料压缩包包含了与Nutch相关的源代码和可能的配置文件,可以帮助开发者深入了解和学习Nutch的工作原理以及如何进行定制化开发。以下是...

    开发自己的搜索引擎 lunenc nutch

    Lucene 是一个全文搜索引擎库,而 Nutch 是一个完整的网络爬虫项目,两者结合可以提供从网页抓取到索引、搜索的一站式解决方案。 在开发自定义搜索引擎的过程中,首先我们需要了解 **Lucene** 的基本原理。Lucene ...

    Nutch 是一个开源Java 实现的搜索引擎(学习资料)----下载不扣分,回帖加1分,童叟无欺,欢迎下载 。不下也来看看啊!!

    1. **Java编程基础**:由于Nutch是用Java编写的,因此理解和修改Nutch源代码需要扎实的Java基础知识。 2. **搜索引擎原理**:理解搜索引擎的基本工作流程,包括爬虫、预处理、索引和查询处理。 3. **Hadoop和...

    Lucene.Nutch搜索引擎开发

    2. **Nutch**: Nutch是一个开放源代码的网络爬虫,主要用于抓取和索引网页内容。它基于Lucene,提供了完整的爬虫解决方案,包括网页抓取、预处理(如HTML解析、链接分析、去重等)、索引和搜索功能。Nutch的主要优势...

    Lucene+Nutch搜索引擎开发(配套光盘资源)

    《Lucene+Nutch搜索引擎开发》是一本专注于搜索引擎技术的书籍,配套光盘资源为学习者提供了丰富的实践材料,尤其对于想要深入理解Nutch搜索引擎开发的读者来说,这是一份不可多得的学习资料。Nutch是基于Apache ...

    nutch解决搜索结果高亮和网页快照链接无效及网页变形

    Nutch 是一个开源的搜索引擎项目,它提供了网络爬虫、索引和搜索的功能。在构建一个自定义的搜索引擎时,可能会遇到几个常见的问题,如搜索结果的关键词高亮、快照链接无效以及网页在预览时的变形。下面将详细讨论...

    Nutch:一个灵活可扩展的开源web搜索引擎

    总之,Nutch是一个强大的开源搜索引擎工具,它不仅适用于构建自己的搜索引擎,也是研究和学习搜索引擎技术的理想平台。通过熟悉其工作流程、分析源代码以及查阅相关文档,你将能够掌握搜索引擎的核心技术和实现细节...

    apache-nutch-1.4-src.tar.gz_nutch_搜索引擎

    Apache Nutch 是一款高度可扩展的开源全文搜索引擎框架,它为构建自定义的网络爬虫和搜索引擎提供了强大的工具集。Nutch 的设计目标是处理大量网页数据,进行高效的抓取、索引和搜索操作。在“apache-nutch-1.4-src....

    nutch网页爬取总结

    - **配置 Nutch 创建索引**:下载 Nutch 的源代码并解压,然后通过 Maven 进行编译。配置 Nutch 的 `conf/nutch-site.xml` 文件以设置存储路径、抓取策略等参数。 - **安装 Tomcat**:Tomcat 用于运行 Nutch 的 UI...

    nutch使用&Nutch;入门教程

    在使用Nutch之前,你需要配置Nutch的运行环境,包括安装Java、设置Hadoop(如果需要分布式爬取)、下载和编译Nutch源代码。还需要配置Nutch的`conf/nutch-site.xml`文件,指定抓取策略、存储路径、爬虫范围等参数。 ...

Global site tag (gtag.js) - Google Analytics