这几天没事研究了一下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/")
分享到:
相关推荐
Ruby编写的网络蜘蛛,也称为Web爬虫,是一种自动化程序,用于遍历互联网并抓取网页内容。在编程世界中,Ruby以其简洁、易读的语法而受到许多开发者的青睐,尤其是在构建网络爬虫这样的任务上。Ruby拥有丰富的库和...
Nokogiri支持XPath和CSS选择器,使得定位网页元素变得简单。对于动态加载的内容,可以借助像Selenium或Capybara这样的库,它们可以驱动真实或模拟的浏览器执行JavaScript。 对于Instagram Crawler,它可能利用了...
在这个压缩包中,你可能找到各种编程语言(如Python、JavaScript、Ruby等)的库,这些库能够帮助编写爬虫来高效地导航网页、解析HTML、CSS和JavaScript,以及处理反爬虫策略。 1. **Python库**:Python是网络抓取...
Web搜寻器通过模拟用户行为,访问网页,抓取HTML内容,并可能进一步解析出所需的数据,如文本、图片链接、结构化信息等。这种技术广泛应用于搜索引擎优化、市场分析、数据挖掘等领域。 【标签】:“Ruby” Ruby是...
SinatraWebScraper 是一个基于 Sinatra 框架...通过以上分析,我们可以看出 SinatraWebScraper 是一个结合了网页抓取、短信服务集成以及前端交互的多功能应用,展示了如何利用 Ruby 和 JavaScript 实现数据获取和传递。
网络爬虫,也称为网页抓取或数据抓取,是互联网上的一种自动化程序,用于收集大量网页信息并进行处理。在2022年,随着大数据和人工智能的崛起,爬虫技术在数据分析、市场研究、信息监控等领域的重要性日益凸显。本篇...
这些教程涵盖了多个编程语言和技术,包括质量控制(QC)、Sass、Scala、Scrapy、Rust、Python、Ruby、Redis、R和React。让我们逐一深入探讨这些知识点。 1. **质量控制(QC)**:质量控制是软件开发过程中的一个...
Atom是一个由GitHub开发的开源文本编辑器,它利用了Web技术如HTML、CSS和JavaScript,为开发者提供了一个现代化的工作环境。这款编辑器以其高度可定制性、丰富的插件生态系统和跨平台支持而闻名,适用于多种操作系统...
总的来说,WebTransformer 展示了如何利用 Ruby 的强大功能进行网页抓取、数据转换以及可能的前端重构工作,将旧版网站的内容迁移至新的平台。这不仅涉及到了基本的编程技巧,还涵盖了Web开发中的多个重要环节,对...
同时,考虑到数据安全和性能,网站可能采用了先进的后端框架,如Node.js、Django或Ruby on Rails,与数据库(如MySQL、MongoDB)进行高效交互。 对于股票证券类数据公司,网站的核心在于数据处理和分析能力。因此,...
Bootstrap 3是其第三个主要版本,提供了预定义的CSS样式、JavaScript组件和HTML模板,帮助开发者快速创建美观且响应式的网站。在bike_scraper中,Bootstrap可能被用于设计用户界面,确保在不同设备上都能呈现良好的...
通过Nokogiri,开发者可以利用XPath或CSS选择器找到文档中的特定元素,这对于Web抓取和自动化处理网页内容非常有用。 在描述中提到的“威奇托桥梁”项目中,开发者首先可能访问了一个包含威奇托市桥梁信息的网页,...
1. **JAVA开发**:网络爬虫可以使用多种编程语言实现,如Python、JavaScript、Ruby等,但本项目选择了JAVA。JAVA因其跨平台性和丰富的库支持,常被用于大型和复杂的爬虫项目。开发者可能使用了JAVA的HTTP客户端库,...
4. 图片和文件下载:下载网页上的图片和其他资源。 5. 响应处理:解析和处理HTTP响应,包括状态码、头部和正文内容。 6. 路由和重定向:管理请求的路由和自动处理重定向。 在实际应用中,horseman可以用于各种场景...
通过使用Webkit渲染引擎,PhantomJS能够精确地模拟真实浏览器的行为,这对于网页抓取、网页自动化测试和性能分析来说非常有用。 在描述中提到,官方PhantomJS仓库并未提供ppc64le架构的二进制版本,这可能是因为该...
Nokogiri是一个在Ruby编程语言中广泛使用的库,用于解析HTML...在实践中,你可能会遇到各种挑战,如动态加载的内容、JavaScript渲染、登录验证等,这些都是网页抓取中常见的问题,需要你利用Nokogiri和其他工具来解决。
在网页应用程序中,通常会结合CSS(Cascading Style Sheets)和JavaScript来增强用户体验。虽然这里没有明确提及CSS和JavaScript,但它们对于实现Rentomatic的功能至关重要。CSS用于美化和布局页面,使信息呈现得更...
【标题】"rental_site_scraper" 是一个基于 Ruby 编程语言的网络抓取工具。这个工具的主要目的是为了从特定的租赁网站上提取数据,如房源信息、价格、地理位置等,帮助用户快速获取和分析大量租房信息。 【描述】...
综上所述,"spidey-web-crawlers"项目是一个使用Ruby编写的网络爬虫,它可能利用Nokogiri、HTTParty和Mechanize等库来抓取和处理网页数据。实际的代码实现、特定功能以及爬取策略将取决于项目内的具体文件内容。
相比之下,动态网站利用PHP、Python、Ruby等后端语言,能实时生成页面,便于数据管理和交互功能的实现。 总结来说,HTML5自适应个人博客HTML静态模板是一个利用HTML5特性和响应式设计原理,为个人博主提供的网页...