Раздел «Язык Ruby».RubySchoolSourceL07:
<<Программирование на Ruby

Занятие 7: Итоговый код качалки сайтов

Следующее занятие
Предыдущее занятие

Пример кода

Код по-прежнему нуждается в большом числе доработок.

require 'net/http'
require 'uri'
require 'iconv'
require 'fileutils'

url = 'http://acm.mipt.ru/twiki/bin/view/Ruby/RubySchoolSource'
LOAD_DIR = '/home/artem/web_sites'

FileUtils.mkdir_p(LOAD_DIR)

# очень черновой вариант метода вычисления имени файла из URL
def url_to_filename(url, target_dir)
  uri = URI.parse(url)
  # нужно добавить защиту от '../../../etc/passwd' и др. подобных вещей
  path = File.join(target_dir, uri.path.gsub(/\W/, '_') + '.html')
  # Если вы хотите сохранять структуру сайта, то не нужно
  # заменять слеши на подчеркивание.
  # Для создания пути используйте FileUtils.mkdir_p()
end

# load_url('http://yandex.ru', :level => 2, :pages_limit => 5000)

def load_url(initial_url, options = {})
  
  http = options.fetch(:http, nil) 
  initial_level = options.fetch(:level, 5)
  target_subdir = options.fetch(:target_dir, nil)
  pages_limit = options.fetch(:pages_limit, 1_000_000)
  
  queue = [ [initial_url, initial_level] ]
  initial_uri = URI.parse(initial_url)
  
  # Hash {loaded_url => 1}
  loaded_url = {}
  
  # Hash {queued_url => 1}
  queued_url = {}
  
  target_subdir ||= initial_uri.host
  
  target_dir = File.join(LOAD_DIR, target_subdir)
  
  FileUtils.mkdir_p(target_dir)

  http = Net::HTTP.start(initial_uri.host, initial_uri.port) unless http && http.active?
    
  while !queue.empty? && loaded_url.size < pages_limit
    
    url,level = queue.shift
    
    next if loaded_url[url]
    uri = URI.parse(url)
    
    if options[:debug_output]
      puts <<-END_INFO
        Downloading #{url}
        Current queue: #{queue.size}
        Downloaded: #{loaded_url.size}
        Queued: #{queued_url.size}
        Path = #{uri.path}
      END_INFO
    end
       
    result = http.get( (uri.path != '') ? uri.path : '/' )
    # TODO treat statuses
    html = result.body
    
    # Кодировку страницы нужно получать из result
    # и перекодировать в utf-8, если она отличается от utf-8
    #begin 
    #  html = Iconv.iconv('koi8r//ignore', 'utf-8', html).join
    #rescue =>e
    #  puts e.to_s
    #end
    
    # Нужна умная функция вычисляющая имя файла по URL
    file_name = url_to_filename(url, target_dir)
    
    File.open(file_name, 'w+') do |file|
      puts "Saving '#{url}' to #{file_name}"
      file.write(html)
      loaded_url[url] = 1
    end

    if level > 0
      html.scan(%r{http://[^\s""''><]+}).each do |sub_url|
        # puts sub_url
        next if queued_url[sub_url]
        sub_uri = URI.parse(sub_url) rescue next
        # TODO: sub domains
        next if sub_uri.host != uri.host
        queue.push( [sub_url, level - 1] )
        queued_url[sub_url] = 1
      end
    end
  end
end

load_url(url, :debug_output => true)