`

ruby网站抓取(图片,网页,css,javascript)

    博客分类:
  • ruby
阅读更多

这几天没事研究了一下ruby,写了一个网站抓取的小程序,希望能给初学者带来帮助

# @date:2011/2/26
# @author:wuzhenzhong

require 'net/http'
require 'thread'
require "open-uri"
require "fileutils"

module Log
  def Log.i(msg)
    print "[INFO]#{msg}\n"
  end
  def Log.e(msg)
    print "[ERROR]#{msg}\n"
  end
end

class UrlObj
  attr_accessor :url,:depth,:save_name
  def initialize(url,depth,save_name)
    @url=url
    @save_name=save_name
    @depth=depth
  end

  def info
    return @info if @info
    begin
      @info=URI.parse(@url)
      return @info
    rescue Exception=>e
      Log.e e
      return nil
    end
  end
  #Get url's domain
  def domain
    if @domain
      return @domain
    elsif !info
      Log.e("#{@url}'s host is nil")
      return nil
    elsif !info.host
      Log.e("#{@url}'s host is nil")
      return nil
    else
      return info.host[/\w+\.\w+$/]
    end
  end
end
  
class WebSnatcher
  THREAD_SLEEP_TIME=0.5
  HTTP_RETRY_TIMES=3


  def initialize(opt={},&block)
    @url_check=block;
    @url_to_download=[]
    @url_to_download_count=0
    @html_name_counter=0
    @file_name_counter=0
    @bad_urls=[]
    @url_history={}
    @file_history={}
    @thread_count=opt[:thread_count]||=10
    @mutex=Mutex.new#Initialise the mutex for threads
    @dig_depth_limit=opt[:depth]||=2
    @work_path=opt[:work_path]||File.dirname(__FILE__)+"/output/"
    @other_file_path=@work_path+"others/"
    FileUtils.mkdir_p @other_file_path unless File.directory?(@other_file_path)
    Log.i "Downloaded files will save to #{@work_path}"
    @max_file_size=opt[:max_file_size]||nil
    @min_file_size=opt[:min_file_size]||nil
    @include_url_matcher=opt[:url_regexp]||[]
    @exclude_url_matcher=[]
    @need_type={}    
    if opt[:file_limit]
      opt[:file_limit].each { |filetype|
        @need_type[filetype]=true
      }
    else
      @need_all=true
    end
  end

  def append_include_url_matcher(regexp)
    @include_url_matcher<<regexp if regexp.instance_of?(Regexp)
    return self
  end
  def append_exclude_url_matcher(regexp)
    @exclude_url_matcher<<regexp if regexp.instance_of?(Regexp)
    return self
  end
  def get_url_to_download
    @mutex.synchronize do
      if @url_to_download_count>0
        url=@url_to_download.shift
        @url_to_download_count-=1
        return url
      else
        return nil
      end
    end
  end
  
  def add_url_to_download(url)
    @mutex.synchronize do
      @url_to_download.push(url)
      @url_to_download_count+=1
    end
  end

  def add_url_to_history(url,save_name)
    @url_history[url]=save_name
  end

  def amend_url(url,ref)
    return nil unless url
    return url if url=~/^http:\/\/.+/
    url=url.sub(/#\w+$/, "")
    return amend_url($1,ref) if url=~/^javascript:window\.open\(['"]{0,1}(.+)['"]{0,1}\).*/i#<a href="javascript:window.open("xxxx")">
    return nil if url=~/javascript:.+/i
    return "http://"+url if url=~/^www\..+/#www.xxxxx.com/dddd
    return "http://"+ref.info.host+url if url=~/^\/.+/#root path url
    return ref.url.sub(/\/[^\/]*$/,"/#{url}") if url=~/^[^\/^\.].+/  #simple filename like 123.html
    if url=~/^\.\/(.+)/ #./pmmsdfe.jpg
      return ref.url.sub(/\/[^\/]+$/,"/#{$1}")
    end
    if url=~/^\.\.\/(.+)/ #../somthing.jpg
      return ref.url.sub(/\/\w+\/[^\/]*$/, "/#{$1}")
    end
    nil
  end


  def get_html_save_name
    @hnm||=Mutex.new
    @hnm.synchronize {
      @html_name_counter+=1
      return "#{@html_name_counter}.html"
    }
  end

  def get_file_save_counter
    @fnl||=Mutex.new
    @fnl.synchronize{
      return @file_name_counter+=1
    }
  end


  def match_condition?(url_obj)
    @include_url_matcher.each{|rep|
      return true if url_obj.url=~rep
    }
    @exclude_url_matcher.each{|rep|
      return false if url_obj.url=~rep
    }
    return false if @bad_urls.include?(url_obj.url)
    if !(@base_url_obj.domain)||@base_url_obj.domain!=url_obj.domain
      return false
    else
      return true
    end
  end

  def write_text_to_file(path,content)
    File.open(path, 'w'){|f|
      f.puts(content)
    }
    Log.i("HTML File have saved to #{path}")
  end

  def download_to_file(_url,save_path)
    return unless _url
    begin
      open(_url) { |bin|
        size=bin.size
        return if @max_file_size&&size>@max_file_size||@min_file_size&&size<@min_file_size
        Log.i("Downloading: #{_url}|sze:#{size}")
        File.open(save_path,'wb') { |f|
          while buf = bin.read(1024)
            f.write buf
            STDOUT.flush
          end
        }
      }
    rescue Exception=>e
      Log.e("#{_url} Download Failed!"+e)
      return
    end
    Log.i "File has save to #{save_path}!!"
  end

  def deal_with_url(url_obj)
    Log.i "Deal with url:#{url_obj.url};depth=#{url_obj.depth}"
    return unless url_obj.instance_of?(UrlObj)
    retry_times=HTTP_RETRY_TIMES
    content=nil
    0.upto(HTTP_RETRY_TIMES) { |i|
      begin
        return unless url_obj.info
        Net::HTTP.start(url_obj.info.host,url_obj.info.port)
        content=Net::HTTP.get(url_obj.info)
        retry_times-=1
        break
      rescue
        next if i<HTTP_RETRY_TIMES#stop trying until has retry for 5 times
        Log.i "Url:#{url_obj.url} Open Failed!"
        return
      end
    }
    Log.i "Url:#{url_obj.url} page has been read in!"
    return unless content
    if url_obj.depth<@dig_depth_limit||@dig_depth_limit==-1
      urls = content.scan(/<a[^<^{^(]+href="([^>^\s]*)"[^>]*>/im)
      urls.concat content.scan(/<i{0,1}frame[^<^{^(]+src="([^>^\s]*)"[^>]*>/im)
      urls.each { |item|
        anchor=item[0][/#\w+/]#deal with the anchor
        anchor="" unless anchor
        full_url=amend_url(item[0],url_obj)
        next unless full_url
        if @url_history.has_key?(full_url)#if already have downloades this url replace the links
          content.gsub!(item[0],@url_history.fetch(full_url)+anchor)
        else#add to url tasks
          #if match download condition,add to download task
          save_name=get_html_save_name
          new_url_obj=UrlObj.new(full_url, url_obj.depth+1,save_name)
          if match_condition?(new_url_obj)
            Log.i "Add url:#{new_url_obj.url}"
            add_url_to_download new_url_obj
            content.gsub!(item[0], save_name+anchor)
            add_url_to_history(full_url,save_name)
          end
        end
      }
    end
    #Downloadd Other files
    files=[]
    #search for image
    files.concat content.scan(/<img[^<^{^(]+src=['"]{0,1}([^>^\s^"]*)['"]{0,1}[^>]*>/im) if @need_type[:image]||@need_all
    #search for css
    files.concat content.scan(/<link[^<^{^(]+href=['"]{0,1}([^>^\s^"]*)['"]{0,1}[^>]*>/im) if @need_type[:css]||@need_all
    #search for javascript
    files.concat content.scan(/<script[^<^{^(]+src=['"]{0,1}([^>^\s^"]*)['"]{0,1}[^>]*>/im) if @need_type[:js]||@need_all
    
    files.each {|f|
      full_url=amend_url(f[0],url_obj)
      
      next unless full_url
      base_name=File.basename(f[0])#get filename
      base_name.sub!(/\?.*/,"")
      full_url.sub!(/\?.*/,"")
      #      unless base_name=~/[\.css|\.js]$/
      base_name="#{get_file_save_counter}"+base_name
      #      end
      if @file_history.has_key?(full_url)
        filename=@file_history[full_url]
        content.gsub!(f[0],"others/#{filename}")
      else
        download_to_file full_url,@other_file_path+base_name
        content.gsub!(f[0],"others/#{base_name}")
        @file_history[full_url]=base_name
        #        add_url_to_history(full_url,base_name)
      end
      files.delete f
    }
    #save content
    if @need_type[:html]||@need_all
      write_text_to_file(@work_path+url_obj.save_name, content)
      Log.i "Finish dealing with url:#{url_obj.url};depth=#{url_obj.depth}"
    end
  end

  def run(*base_url)
    @base_url=base_url[0] if @base_url==nil
    Log.i "<---------START--------->"
    @base_url_obj=UrlObj.new(@base_url,0,"index.html")
    m=Mutex.new
    threads=[]
    working_thread_count=0
    @thread_count.times{|i|
      threads<<Thread.start() {
        Log.i "Create id:#{i} thread"
        loop do
          url_to_download=get_url_to_download
          if url_to_download
            m.synchronize {
              working_thread_count+=1
            }
            begin
              deal_with_url url_to_download
            rescue Exception=>e
              Log.e "Error: " +e
              @bad_urls.push(url_to_download.url)
            end
            m.synchronize {
              working_thread_count-=1
            }
          else
            sleep THREAD_SLEEP_TIME
          end
        end
      }
    }
    #Create a monitor thread
    wait_for_ending=false
    monitor=Thread.start() {
      loop do
        sleep 2.0
        Log.i "Working threads:#{working_thread_count}|Ramain Url Count:#{@url_to_download_count}"
        next unless wait_for_ending
        if @url_to_download_count==0 and working_thread_count==0
          Log.i "Finish downloading,Stoping threads..."
          threads.each { |item|
            item.terminate
          }
          Log.i("All Task Has Finished")
          break
        end
      end
      Log.i "<---------END--------->"
    }
    deal_with_url @base_url_obj
    wait_for_ending=true
    Log.i "main thread wait until task finished!"
    #main thread wait until task finished!
    monitor.join
  end
end


#Linux c函数参考
#snatcher=WebSnatcher.new(:work_path=>"E:/temp/",:depth=>2)
#snatcher.append_exclude_url_matcher(/http:\/\/man\.chinaunix\.net\/{0,1}$/i)
#snatcher.run("http://man.chinaunix.net/develop/c&c++/linux_c/default.htm")
#http://www.kuqin.com/rubycndocument/man/index.html

snatcher=WebSnatcher.new(:work_path=>"E:/temp/",:depth=>2)
snatcher.run("http://www.ifeng.com/")

#snatcher=WebSnatcher.new(:file_limit=>[:image],:min_file_size=>50000,:depth=>-1,:thread_count=>20,:work_path=>"E:/mm/")
#snatcher.run("http://www.tu11.cc/")#snatcher.run("http://www.sw48.com/")#snatcher.run("http://www.siwameinv.net/")

 

2
0
分享到:
评论

相关推荐

    ruby写的网络蜘蛛

    Ruby编写的网络蜘蛛,也称为Web爬虫,是一种自动化程序,用于遍历互联网并抓取网页内容。在编程世界中,Ruby以其简洁、易读的语法而受到许多开发者的青睐,尤其是在构建网络爬虫这样的任务上。Ruby拥有丰富的库和...

    Ruby-InstagramCrawler下载Instagram照片帖子和视频的最简单方法

    Nokogiri支持XPath和CSS选择器,使得定位网页元素变得简单。对于动态加载的内容,可以借助像Selenium或Capybara这样的库,它们可以驱动真实或模拟的浏览器执行JavaScript。 对于Instagram Crawler,它可能利用了...

    用于网络抓取和数据处理的库、工具和API列表。_Makefile_下载.zip

    在这个压缩包中,你可能找到各种编程语言(如Python、JavaScript、Ruby等)的库,这些库能够帮助编写爬虫来高效地导航网页、解析HTML、CSS和JavaScript,以及处理反爬虫策略。 1. **Python库**:Python是网络抓取...

    Crawler:用于网站抓取的Web搜寻器

    Web搜寻器通过模拟用户行为,访问网页,抓取HTML内容,并可能进一步解析出所需的数据,如文本、图片链接、结构化信息等。这种技术广泛应用于搜索引擎优化、市场分析、数据挖掘等领域。 【标签】:“Ruby” Ruby是...

    SinatraWebScraper:一个 sinatra 应用程序,它将抓取特定项目的网页并将该内容作为短信发送

    SinatraWebScraper 是一个基于 Sinatra 框架...通过以上分析,我们可以看出 SinatraWebScraper 是一个结合了网页抓取、短信服务集成以及前端交互的多功能应用,展示了如何利用 Ruby 和 JavaScript 实现数据获取和传递。

    爬虫20220803pm

    网络爬虫,也称为网页抓取或数据抓取,是互联网上的一种自动化程序,用于收集大量网页信息并进行处理。在2022年,随着大数据和人工智能的崛起,爬虫技术在数据分析、市场研究、信息监控等领域的重要性日益凸显。本篇...

    Tutorialspoint QC Sass Scala Scrapy Rust Python Ruby Redis R React 教程

    这些教程涵盖了多个编程语言和技术,包括质量控制(QC)、Sass、Scala、Scrapy、Rust、Python、Ruby、Redis、R和React。让我们逐一深入探讨这些知识点。 1. **质量控制(QC)**:质量控制是软件开发过程中的一个...

    Atom-urss,ultra-rss是另一个ruby提要rss解析器。通过在github上创建一个帐户来帮助zedtux/urss开发。.zip

    Atom是一个由GitHub开发的开源文本编辑器,它利用了Web技术如HTML、CSS和JavaScript,为开发者提供了一个现代化的工作环境。这款编辑器以其高度可定制性、丰富的插件生态系统和跨平台支持而闻名,适用于多种操作系统...

    WebTransformer:以各种方式为 XProgramming.com -> ronjeffries.com 转换旧网页

    总的来说,WebTransformer 展示了如何利用 Ruby 的强大功能进行网页抓取、数据转换以及可能的前端重构工作,将旧版网站的内容迁移至新的平台。这不仅涉及到了基本的编程技巧,还涵盖了Web开发中的多个重要环节,对...

    大数据分析公司网站模板

    同时,考虑到数据安全和性能,网站可能采用了先进的后端框架,如Node.js、Django或Ruby on Rails,与数据库(如MySQL、MongoDB)进行高效交互。 对于股票证券类数据公司,网站的核心在于数据处理和分析能力。因此,...

    bike_scraper:Web App 从 Craigslist 抓取数据

    Bootstrap 3是其第三个主要版本,提供了预定义的CSS样式、JavaScript组件和HTML模板,帮助开发者快速创建美观且响应式的网站。在bike_scraper中,Bootstrap可能被用于设计用户界面,确保在不同设备上都能呈现良好的...

    wichita-bridges:Nokogiri抓取JSON Goole映射

    通过Nokogiri,开发者可以利用XPath或CSS选择器找到文档中的特定元素,这对于Web抓取和自动化处理网页内容非常有用。 在描述中提到的“威奇托桥梁”项目中,开发者首先可能访问了一个包含威奇托市桥梁信息的网页,...

    zhizhu.rar_news crawler_网络爬虫 获取

    1. **JAVA开发**:网络爬虫可以使用多种编程语言实现,如Python、JavaScript、Ruby等,但本项目选择了JAVA。JAVA因其跨平台性和丰富的库支持,常被用于大型和复杂的爬虫项目。开发者可能使用了JAVA的HTTP客户端库,...

    horseman:无头 HTTP 爬虫

    4. 图片和文件下载:下载网页上的图片和其他资源。 5. 响应处理:解析和处理HTTP响应,包括状态码、头部和正文内容。 6. 路由和重定向:管理请求的路由和自动处理重定向。 在实际应用中,horseman可以用于各种场景...

    phantomjs-2.1.1-linux-ppc64le.tar.gz

    通过使用Webkit渲染引擎,PhantomJS能够精确地模拟真实浏览器的行为,这对于网页抓取、网页自动化测试和性能分析来说非常有用。 在描述中提到,官方PhantomJS仓库并未提供ppc64le架构的二进制版本,这可能是因为该...

    NokogiriWebScrapTest1

    Nokogiri是一个在Ruby编程语言中广泛使用的库,用于解析HTML...在实践中,你可能会遇到各种挑战,如动态加载的内容、JavaScript渲染、登录验证等,这些都是网页抓取中常见的问题,需要你利用Nokogiri和其他工具来解决。

    rentomatic:一个小型网络应用程序,用于按通勤时间和交通类型抓取和分类伦敦租金优惠

    在网页应用程序中,通常会结合CSS(Cascading Style Sheets)和JavaScript来增强用户体验。虽然这里没有明确提及CSS和JavaScript,但它们对于实现Rentomatic的功能至关重要。CSS用于美化和布局页面,使信息呈现得更...

    rental_site_scraper

    【标题】"rental_site_scraper" 是一个基于 Ruby 编程语言的网络抓取工具。这个工具的主要目的是为了从特定的租赁网站上提取数据,如房源信息、价格、地理位置等,帮助用户快速获取和分析大量租房信息。 【描述】...

    spidey-web-crawlers:Ruby 中的网络爬虫

    综上所述,"spidey-web-crawlers"项目是一个使用Ruby编写的网络爬虫,它可能利用Nokogiri、HTTParty和Mechanize等库来抓取和处理网页数据。实际的代码实现、特定功能以及爬取策略将取决于项目内的具体文件内容。

    html5自适应个人博客html静态模板.zip

    相比之下,动态网站利用PHP、Python、Ruby等后端语言,能实时生成页面,便于数据管理和交互功能的实现。 总结来说,HTML5自适应个人博客HTML静态模板是一个利用HTML5特性和响应式设计原理,为个人博主提供的网页...

Global site tag (gtag.js) - Google Analytics