All pastes #739279 Raw Edit

Anonymous

public text v1 · immutable
·published 2007-10-17 00:29 UTC
rendered paste body
#!/usr/bin/env ruby

#--
# Copyright (c) 2006 Jens Kraemer
# 
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
#

RDIGVERSION = '0.3.4'


require 'thread'
require 'thwait'
require 'singleton'
require 'monitor'
require 'ostruct'
require 'uri'
require 'cgi'
require 'set'
require 'net/http'
require 'getoptlong'
require 'tempfile'
require 'open-uri'

begin
  require 'ferret'
rescue LoadError
  require 'rubygems'
  require 'ferret'
end

require 'htmlentities/htmlentities'


$KCODE = 'u'
require 'jcode'

# See README for basic usage information
module RDig

  class << self

    # the filter chains are for limiting the set of indexed documents.
    # there are two chain types - one for http, and one for file system
    # crawling.
    # a document has to survive all filters in the chain to get indexed.
    def filter_chain
      @filter_chain ||= {
        # filter chain for http crawling
        :http => [
          :scheme_filter_http,
          :fix_relative_uri,
          :normalize_uri,
          { :hostname_filter => :include_hosts },
          { RDig::UrlFilters::UrlInclusionFilter => :include_documents },
          { RDig::UrlFilters::UrlExclusionFilter => :exclude_documents },
          RDig::UrlFilters::VisitedUrlFilter 
        ],
        # filter chain for file system crawling
        :file => [
          :scheme_filter_file,
          { RDig::UrlFilters::PathInclusionFilter => :include_documents },
          { RDig::UrlFilters::PathExclusionFilter => :exclude_documents }
        ]
      }
         
    end

    def application
      @application ||= Application.new
    end

    def searcher
      @searcher ||= Search::Searcher.new(config.index)
    end

    # RDig configuration
    #
    # may be used with a block:
    #   RDig.configuration do |config| ...
    #
    # see doc/examples/config.rb for a commented example configuration
    def configuration
      if block_given?
        yield configuration
      else
        @config ||= OpenStruct.new(
          :crawler           => OpenStruct.new(
            :start_urls        => [ "http://localhost:3000/" ],
            :include_hosts     => [ "localhost" ],
            :include_documents => nil,
            :exclude_documents => nil,
            :index_document    => nil,
            :num_threads       => 2,
            :max_redirects     => 5,
            :wait_before_leave => 10
          ),
          :content_extraction  => OpenStruct.new(
            # settings for html content extraction (hpricot)
            :hpricot      => OpenStruct.new(
              # css selector for the element containing the page title
              :title_tag_selector => 'title', 
              # might also be a proc returning either an element or a string:
              # :title_tag_selector => lambda { |hpricot_doc| ... }
              :content_tag_selector => 'body'
              # might also be a proc returning either an element or a string:
              # :content_tag_selector => lambda { |hpricot_doc| ... }
            )
            #,
            # # settings for html content extraction (RubyfulSoup)
            # :rubyful_soup => OpenStruct.new(
            #  # select the html element that contains the content to index
            #  # by default, we index all inside the body tag:
            #  :content_tag_selector => lambda { |tagsoup|
            #    tagsoup.html.body
            #  },
            #  # select the html element containing the title 
            #  :title_tag_selector         => lambda { |tagsoup|
            #    tagsoup.html.head.title
            #  }
            # )
          ),
          :index                 => OpenStruct.new( 
            :path                => "index/", 
            :create              => true,
            :handle_parse_errors => true,
            :analyzer            => Ferret::Analysis::StandardAnalyzer.new,
            :occur_default       => :must,
            :default_field       => '*'
          )
        )
      end
    end
    alias config configuration
    
  end

  class Application

    OPTIONS = [
      ['--config',   '-c', GetoptLong::REQUIRED_ARGUMENT,
        "Read aplication configuration from CONFIG."],
      ['--help',     '-h', GetoptLong::NO_ARGUMENT,
        "Display this help message."],
      ['--query',   '-q', GetoptLong::REQUIRED_ARGUMENT,
        "Execute QUERY."],
      ['--version',  '-v', GetoptLong::NO_ARGUMENT,
       	"Display the program version."],
    ]

    # Application options from the command line
    def options
      @options ||= OpenStruct.new
    end
    
    # Display the program usage line.
    def usage
      puts "rdig -c configfile {options}"
    end
    
    # Display the rake command line help.
    def help
      usage
      puts
      puts "Options are ..."
      puts
      OPTIONS.sort.each do |long, short, mode, desc|
        if mode == GetoptLong::REQUIRED_ARGUMENT
          if desc =~ /\b([A-Z]{2,})\b/
            long = long + "=#{$1}"
          end
        end
        printf "  %-20s (%s)\n", long, short
        printf "      %s\n", desc
      end
    end

    # Return a list of the command line options supported by the
    # program.
    def command_line_options
      OPTIONS.collect { |lst| lst[0..-2] }
    end

    # Do the option defined by +opt+ and +value+.
    def do_option(opt, value)
      case opt
      when '--help'
        help
        exit
      when '--config'
        options.config_file = value
      when '--query'
        options.query = value
      when '--version'
        puts "rdig, version #{RDIGVERSION}"
        exit
      else
        fail "Unknown option: #{opt}"
      end 
    end

    # Read and handle the command line options.
    def handle_options
      opts = GetoptLong.new(*command_line_options)
      opts.each { |opt, value| do_option(opt, value) }
    end

    # Load the configuration
    def load_configfile
      load File.expand_path(options.config_file)
    end

    # Run the +rdig+ application.
    def run
      puts "RDig version #{RDIGVERSION}"
      handle_options
      begin
        load_configfile
      rescue
        puts $!.backtrace
        fail "No Configfile found!\n#{$!}"
        
      end    

      puts "using Ferret #{Ferret::VERSION}"

      if options.query
        # query the index
        puts "executing query >#{options.query}<"
        results = RDig.searcher.search(options.query)
        puts "total results: #{results[:hitcount]}"
        results[:list].each { |result|
          puts <<-EOF
#{result[:url]}
  #{result[:title]}
  #{result[:extract]}

          EOF
        }
      else
        # rebuild index
        @crawler = Crawler.new
        @crawler.run
      end
    end
  end
end

require 'rdig/content_extractors'
require 'rdig/url_filters'
require 'rdig/search'
require 'rdig/index'
require 'rdig/file'
require 'rdig/documents'
require 'rdig/crawler'