我刚开始使用Ruby On Rails,想创建一个简单的网站爬虫,它是:
- 浏览所有雪狗战士的个人资料
- 获取裁判员的姓名
- 将名称与旧名称进行比较(在站点分析期间和从文件中)
- 打印所有唯一名称并将其保存到文件中
一个示例URL是:http://www.sherdog.com/fighter/Fedor-Emelianenko-1500
我正在搜索像<span class="sub_line">Dan Miragliotta</span>
这样的标签条目,不幸的是,除了我需要的正确裁判名称外,还使用了相同类型的类:
- 日期
- "不适用",当裁判姓名未知时
我需要丢弃所有带有"N/a"字符串的结果以及任何包含数字的字符串。我设法完成了第一部分,但不知道如何完成第二部分。我试着搜索、思考和实验,但在实验和重写之后,我设法破坏了整个程序,不知道如何(正确)修复它:
require 'rubygems'
require 'hpricot'
require 'simplecrawler'
# Set up a new crawler
sc = SimpleCrawler::Crawler.new("http://www.sherdog.com/fighter/Fedor-Emelianenko-1500")
sc.maxcount = 1
sc.include_patterns = [".*/fighter/.*$", ".*/events/.*$", ".*/organizations/.*$", ".*/stats/fightfinder?association/.*$"]
# The crawler yields a Document object for each visited page.
sc.crawl { |document|
# Parse page title with Hpricot and print it
hdoc = Hpricot(document.data)
(hdoc/"td/span[@class='sub_line']").each do |span|
if span.inner_html == 'N/A' || Regexp.new(".*/d.*$").match(span.inner_html)
# puts "Test"
else
puts span.inner_html
#File.open("File_name.txt", 'a') {|f| f.puts(hdoc.span.inner_html) }
end
end
}
我也希望能为程序的其他部分提供一些想法:如果程序运行多次,我如何正确地从文件中读取当前名称,以及如何对唯一名称进行比较?
编辑:
经过一些提议的改进,以下是我得到的:
require 'rubygems'
require 'simplecrawler'
require 'nokogiri'
#require 'open-uri'
sc = SimpleCrawler::Crawler.new("http://www.sherdog.com/fighter/Fedor-Emelianenko-1500")
sc.maxcount = 1
sc.crawl { |document|
doc = Nokogiri::HTML(document.data)
names = doc.css('td:nth-child(4) .sub-line').map(&:content).uniq.reject { |c| c == 'N/A' }
puts names
}
不幸的是,代码仍然不起作用——它返回一个空白。
如果我写doc = Nokogiri::HTML(open(document.data))
而不是doc = Nokogiri::HTML(document.data)
,那么它会给我整个页面,但是解析仍然不起作用。
hpricot
。不如改为使用nokogiri?
names = document.css('td:nth-child(4) .sub-line').map(&:content).uniq.reject { |c| c == 'N/A' }
=> ["Yuji Shimada", "Herb Dean", "Dan Miragliotta", "John McCarthy"]
不同部分的分解:
document.css('td:nth-child(4) .sub-line')
这将返回位于第四个表列中的类名为sub-line
的html元素数组。
.map(&:content)
对于上一个数组中的每个元素,返回element.content
(内部html)。这相当于map({ |element| element.content })
。
.uniq
从数组中删除重复的值。
.reject { |c| c == 'N/A' }
删除值为"N/A"的元素
您可以使用数组数学(-)来比较它们:
从当前页面获取推荐人
current_referees = doc.search('td[4] .sub_line').map(&:inner_text).uniq - ['N/A']
从文件中读取旧的推荐人
old_referees = File.read('old_referees.txt').split("n")
使用数组#-来比较它们
new_referees = current_referees - old_referees
写入新文件
File.open('new_referees.txt','w'){|f| f << new_referees * "n"}
这将返回所有名称,忽略日期和"N/A":
puts doc.css('td span.sub_line').map(&:content).reject{ |s| s['/'] }.uniq
结果是:
Yuji Shimada
Herb Dean
Dan Miragliotta
John McCarthy
将这些添加到文件中并删除重复项留给您练习,但我会使用File.readlines
、sort
和uniq
的神奇组合,然后再加上一点File.open
来编写结果。
这是的最终答案
require 'rubygems'
require 'simplecrawler'
require 'nokogiri'
require 'open-uri'
# Mute log messages
module SimpleCrawler
class Crawler
def log(message)
end
end
end
n = 0 # Counters how many pages/profiles processed
sc = SimpleCrawler::Crawler.new("http://www.sherdog.com/fighter/Fedor-Emelianenko-1500")
sc.maxcount = 150000
sc.include_patterns = [".*/fighter/.*$", ".*/events/.*$", ".*/organizations/.*$", ".*/stats/fightfinder?association/.*$"]
old_referees = File.read('referees.txt').split("n")
sc.crawl { |document|
doc = Nokogiri::HTML(document.data)
current_referees = doc.search('td[4] .sub_line').map(&:text).uniq - ['N/A']
new_referees = current_referees - old_referees
n +=1
# If new referees found, print statistics
if !new_referees.empty? then
puts n.to_s + ". " + new_referees.length.to_s + " new : " + new_referees.to_s + "n"
end
new_referees = new_referees + old_referees
old_referees = new_referees.uniq
old_referees.reject!(&:empty?)
# Performance optimization. Saves only every 10th profile.
if n%10 == 0 then
File.open('referees.txt','w'){|f| f << old_referees * "n" }
end
}
File.open('referees.txt','w'){|f| f << old_referees * "n" }