带有SOLR的NCrawler

时间:2013-03-20 07:12:30

标签: solr web-crawler

我设法使用NCrawler抓取网站。是否可以将该数据导入SOLR,以便我可以使用SOLR中的索引数据进行搜索?

如果可能,那么如何将已爬网数据推送到SOLR?任何帮助都会非常感激。

提前致谢。

1 个答案:

答案 0 :(得分:4)

是的,可以将已爬网的数据编入索引。我以前做过这个。您需要创建一个实现IPipelineStep的自定义管道步骤,并将其添加到您的NCrawler实现中。我使用SolrNet作为连接Solr的客户端。

以下是一些有助于您入门的代码。

 SolrNet.Startup.Init<IndexItem>("http://localhost:8983/solr");

 using(Crawler c = new Crawler("http://ncrawler.codeplex.com/", 
    new HtmlDocumentProcessor(), new AddCrawledItemToSolrIndex()))
 {
     c.ThreadCount = 3;
     c.MaxCrawlDepth = 2;
     c.ExcludeFilter = new[] { new RegexFilter(
        new Regex(@"(\.jpg|\.css|\.js|\.gif|\.jpeg|\.png|\.ico)",
            RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)) },
     c.Crawl();
 }

自定义IPipelineStep

using System;
using System.Collections.ObjectModel;
using Microsoft.Practices.ServiceLocation;
using MyCrawler.Index;
using NCrawler;
using NCrawler.Interfaces;
using SolrNet;

namespace MyCrawler.Crawler
{
    public class AddCrawledItemToSolrIndex : IPipelineStep
    {
        public void Process(NCrawler.Crawler crawler, PropertyBag propertyBag)
        {
            if (string.IsNullOrWhiteSpace(propertyBag.Text))
                return;

            var indexItem = new IndexItem
            {
                Id = propertyBag.Step.Uri.ToString(),
                Url = propertyBag.Step.Uri.ToString(),
                Host = propertyBag.Step.Uri.Host,
                Content = propertyBag.Text,
                Title = propertyBag.Title,
                LastModified = Convert.ToInt64(DateTimeToUnixTimestamp(propertyBag.LastModified)),
                Date = propertyBag.LastModified.ToString("yyyyMMdd"),
                Keywords = ExtractKeywords(propertyBag.Headers),
                Type = SplitString(propertyBag.ContentType, ';'),
                Digest = CreateMD5Hash(propertyBag.Text),
            };
            var solr = ServiceLocator.Current.GetInstance<ISolrOperations<IndexItem>>();
            solr.Add(indexItem, new AddParameters {CommitWithin = 10000});
        }

        private Collection<string> SplitString(string input, char splitOn)
        {
            var values = input.Split(splitOn);
            var valueCollection = new Collection<string>();
            if (values.Length == 0) return valueCollection;
            foreach (var value in values)
            {
                valueCollection.Add(value.Trim());
            }

            return valueCollection;

        } 

        private double DateTimeToUnixTimestamp(DateTime dateTime)
        {
            return (dateTime - new DateTime(1970, 1, 1).ToLocalTime()).TotalSeconds;
        }

        private string CreateMD5Hash(string input)
        {
            // Use input string to calculate MD5 hash
            var md5 = MD5.Create();
            var inputBytes = Encoding.ASCII.GetBytes(input);
            var hashBytes = md5.ComputeHash(inputBytes);

            // Convert the byte array to hexadecimal string
            var sb = new StringBuilder();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                //sb.Append(hashBytes[i].ToString("X2"));
                // To force the hex string to lower-case letters instead of
                // upper-case, use he following line instead:
                sb.Append(hashBytes[i].ToString("x2")); 
            }
            return sb.ToString();
        }


        private Collection<string> ExtractKeywords(System.Net.WebHeaderCollection headers)
        {
            var keywords = headers["keywords"];
            if (string.IsNullOrWhiteSpace(keywords))
            {
                return new Collection<string>();
            }

            return SplitString(keywords, ',');
        }
    }
}

这是使用以下IndexItem.cs类映射到Solr索引字段。

using System.Collections.ObjectModel;
using SolrNet.Attributes;

namespace MyCrawler.Index
{
    public class IndexItem
    {
        [SolrField("id")]
        public string Id { get; set; }
        [SolrField("url")]
        public string Url { get; set; }
        [SolrField("host")]
        public string Host { get; set; }
        [SolrField("content")]
        public string Content { get; set; }
        [SolrField("title")]
        public string Title { get; set; }
        [SolrField("description")]
        public string Description { get; set; }
        [SolrField("digest")]
        public string Digest { get; set; }
        [SolrField("keywords")]
        public Collection<string> Keywords { get; set; }
        [SolrField("date")]
        public string Date { get; set; }
        [SolrField("contentLength")]
        public long ContentLength { get; set; }
        [SolrField("lastModified")]
        public long LastModified { get; set; }
        [SolrField("type")]
        public Collection<string> Type { get; set; }
    }
}

从Nutch代码库中获取的Solr字段定义(schema.xml)。

      

  <!-- core fields -->
  <field name="segment" type="string" stored="true" indexed="false"/>
  <field name="digest" type="string" stored="true" indexed="false"/>
  <field name="boost" type="float" stored="true" indexed="false"/>

  <!-- meta-tag fields -->
  <field name="keywords" type="text_general" stored="true" indexed="true" multiValued="true"/>
  <field name="description" type="text_general" stored="true" indexed="true"/>

  <!-- fields for index-basic plugin -->
  <field name="host" type="url" stored="false" indexed="true"/>
  <field name="site" type="string" stored="true" indexed="true"/>
  <field name="url" type="url" stored="true" indexed="true"
      required="true"/>
  <field name="content" type="text_general" stored="true" indexed="true"/>
  <field name="title" type="text_general" stored="true" indexed="true"/>
  <field name="cache" type="string" stored="true" indexed="false"/>
  <field name="tstamp" type="long" stored="true" indexed="true"/>

  <!-- fields for index-anchor plugin -->
  <field name="anchor" type="string" stored="true" indexed="true"
      multiValued="true"/>

  <!-- fields for index-more plugin -->
  <field name="type" type="string" stored="true" indexed="true"
      multiValued="true"/>
  <field name="contentLength" type="long" stored="true"
      indexed="false"/>
  <field name="lastModified" type="long" stored="true"
      indexed="true"/>
  <field name="date" type="string" stored="true" indexed="true"/>

  <!-- fields for languageidentifier plugin -->
  <field name="lang" type="string" stored="true" indexed="true"/>

  <!-- fields for subcollection plugin -->
  <field name="subcollection" type="string" stored="true"
      indexed="true"/>

  <!-- fields for feed plugin -->
  <field name="author" type="string" stored="true" indexed="true"/>
  <field name="tag" type="string" stored="true" indexed="true"/>
  <field name="feed" type="string" stored="true" indexed="true"/>
  <field name="publishedDate" type="string" stored="true"
      indexed="true"/>
  <field name="updatedDate" type="string" stored="true"
      indexed="true"/>

  <!-- catchall field, containing all other searchable text fields (implemented
  via copyField further on in this schema  -->
  <field name="text" type="text_general" indexed="true" stored="false" multiValued="true"/>

  <field name="_version_" type="long" indexed="true" stored="true"/>

  <field name="timestamp" type="date" indexed="true" stored="true" default="NOW" multiValued="false"/>
</fields>

显然,您需要修改它以满足您的需求,并且可能会使用一些性能改进。但应该是一个很好的参考点。