Ruby 脚本实现数据爬取

本贴最后更新于 3148 天前,其中的信息可能已经时移世异

工作上使用Redis,为了测试其性能,需要大量的测试数据,所以利用周末的时间用ruby写了个脚本,跑了一天,从搜搜问问百度知道爬了大量的数据下来,分成问题和答案,总共有50万条数据,大小也就50M左右;周一去上班,写了一个ruby脚本连接Redis数据库,再循环分别导入问题和答案数据,这样测试数据就有了,由于测试机器内存的限制,导入的测试数据总共100万条,占内存1G。

下面想说说ruby脚本的结构,很简单,还有需要改进的地方,但是脚本就是一次性的工具,如没有通用性可能,有没有必要再改进另当其说。

建立http连接功能使用了内部包含的gem包open-uri,解析获取到的页面对象,并抓取特定的document元素,使用的gem是nokogiri,脚本分几个功能部分,各负其责,分别介绍如下:

  • 递归抓取页面所有超级链接(spider_url.rb)
#!/usr/bin/ruby -w

require 'rubygems'
require 'nokogiri'
require 'open-uri'

load 'spider_document.rb'

class URL
attr_accessor :available_url, :pre_url, :visited_url, :error_url

def initialize @available_url = {} @visited_url = {} @error_url = {} @pre_url = "http://wenwen.soso.com" end # 抓取页面上的所有超链接,形如 'href...' def crawl_url (target_url) puts '获取超级链接页面地址 -> ' + target_url temp_available_url = {} begin open(target_url) do |uri| doc_content = uri.read doc_content.scan(/href=["|'](.+?)["|']/) do |href_item| url = href_item[0] # TODO pattern is not exactely url.match(/.z./) do |m| # build hash {url=>real url} if !@visited_url.has_key?(url) temp_available_url["#{url}"] = url @visited_url["#{url}"] = url end # puts '新增访问url:' + url end # url=@target_url.match(/(http:\/\/([^\/]+))\//)[1] << url if url =~ /^\// end end rescue puts $! @error_url["#{target_url}"] = target_url puts 'error' end temp_available_url end def crawl_content (target_url) doc = Document.new doc.crawl_content(target_url) end

end

url_spider = URL.new
puts 'url spider begining ...'

url_spider.available_url = url_spider.crawl_url("http://wenwen.soso.com")
while(!url_spider.available_url.empty?)
url_spider.available_url.each do |key,value|
url_spider.crawl_content(url_spider.pre_url + value)
url_spider.available_url = url_spider.available_url.merge url_spider.crawl_url(url_spider.pre_url + value)
url_spider.available_url.delete(key)
puts 'current available_url size : ' + url_spider.available_url.size.to_s
end
end

puts 'Total available_url size : ' + url_spider.available_url.size.to_s
puts 'Total visited_url size : ' + url_spider.visited_url.size.to_s
puts 'Total error_url size : ' + url_spider.error_url.size.to_s

  • 解析页面文档(spider_document.rb)
#!/usr/bin/ruby -w

require 'rubygems'
require 'nokogiri'
require 'open-uri'

class Document
@@quesion_count = 0
@@answer_count = 0

def get_question (page) questionArray = page.css('div.qa_title') questionArray.each do |question| #puts question.text File.open("question.txt",'a') { |f| f.puts question.text.strip.delete "快速回答".strip } end @@quesion_count = @@quesion_count + questionArray.size puts "current question count is : [" + @@quesion_count.to_s + "]" end def get_answer (page) answerArray = page.css('div.answer_con') answerArray.each do |answer| #puts answer.text File.open("answer.txt",'a') { |f| f.puts answer.text.strip.delete "快速回答".strip } end @@answer_count = @@answer_count + answerArray.size puts "current answer count is : [" + @@answer_count.to_s + "]" end def crawl_content (target_url) puts '抓取页面内容地址 -> ' + target_url begin page = Nokogiri::HTML(open(target_url)) get_question (page) get_answer (page) rescue Exception => e puts $! end end

end

  • 批量导入Redis(spider_persistence.rb)
#!/usr/bin/ruby -w

require 'rubygems'
require 'nest'
require 'redis'

class Persistence

attr_accessor :redis, :question_count, :answer_count def initialize @redis = Redis.new # @redis = Redis.new(:host=>"192.168.1.67",:port=>6379) @question_count = 0 @answer_count = 0 end # 批量生产账号 def batch_account account_namespace = Nest.new("account",@redis) File.open("account_email_local.txt") do |f| f.each_line do |line| pre_str = line.chomp.split('@')[0] account_namespace[line.chomp].hset("nickName",pre_str) account_namespace[line.chomp].hset("email",pre_str) account_namespace[line.chomp].hset("passWordHash","49") # 密码为1 account_namespace[line.chomp].hset("answerCount","0") account_namespace[line.chomp].hset("selfDescription","非理性人类一枚") account_namespace[line.chomp].hset("followCount","0") account_namespace[line.chomp].hset("followerCount","0") account_namespace[line.chomp].hset("questionCount","0") puts line.chomp + " is builded." end end end # 批量生成问题集合 def batch_question account_namespace = Nest.new("account",@redis) question_namespace = Nest.new("question",@redis) pre_email = "rayootech" # 默认的账号 rayootech@163.com begin File.open("question.txt","r") do |file| file.each_line do |line| # 生成随机的20位问题id id = random_id(20) if (!line.strip.empty? && line.strip.length>3) puts "#{file.lineno} : #{line}" question_namespace[id].hset("id",id) question_namespace[id].hset("content",line) question_namespace[id].hset("author",pre_email+"@163.com") question_namespace[id].hset("createTime","2014-01-14") question_namespace[id].hset("followerCount","0") question_namespace[id].hset("browseCount","1") # 用户和提出的问题关系集合 account:[id]:question account_namespace["#{pre_email}@163.com"]["questions"].zadd(1401141645,id) @question_count = @question_count + 1 File.open("question_id_local.txt", "a") { |f| f.puts id } end # 生成随机email地址前缀,并保存,后期生成account账号导入redis,一个email账户提500个问题 if (@question_count%500==0) pre_email = random_id(10) File.open("account_email_local.txt","a"){|file|file.puts "#{pre_email}@163.com"} end end end rescue Exception => e puts $! end end # 批量生成回答集合 def batch_answer account_namespace = Nest.new("account",@redis) qa_relation_ns = Nest.new("question",@redis) answer_namespace = Nest.new("answer",@redis) question_id = "lzj4ggcgfpmj5uxnhtgx" # 【提问时间】 默认问题id begin File.open("answer.txt","r") do |file| file.each_line do |line| # 生成随机的20位回答id id = random_id(20) author = random_account_email if (!line.strip.empty?) puts "#{file.lineno} : #{line}" answer_namespace[id].hset("id",id) answer_namespace[id].hset("content",line) answer_namespace[id].hset("author",author) answer_namespace[id].hset("createTime","2014-01-15") answer_namespace[id].hset("approveCount","0") answer_namespace[id].hset("qId",question_id) # 问题和回答关系数据 qa_relation_ns[question_id]["answers"].zadd(1401152040,id) # 问题的所有回答者关系数据 qa_relation_ns[question_id]["respondents"].sadd(author) # 用户所有的回答数据 account_namespace[author]["answers"].zadd(1401159088,id) @answer_count = @answer_count + 1 File.open("answer_id_local.txt", "a") { |f| f.puts id } end # 每个问题下有平均100个回答 if (@answer_count%100==0) question_id = random_question_id end end end rescue Exception => e puts $! end end # 批量生成问题浏览者集合 def batch_question_browser end # 随机返回一个问题id def random_question_id question_id_arr = [] index = 0 File.open("question_id.txt") do |f| f.each_line do |line| question_id_arr[index]=line index = index + 1 end end question_id_arr[rand(question_id_arr.size-1)].chomp end # 随机返回一个回答id def random_answer_id end # 随机返回一个email def random_account_email account_email_arr = [] index = 0 File.open("account_email.txt") do |f| f.each_line do |line| account_email_arr[index]=line index = index + 1 end end account_email_arr[rand(account_email_arr.size-1)].chomp end # 生成随机数 def random_id(len) chars = ("a".."z").to_a + ("a".."z").to_a + ("0".."9").to_a random_id = "" 1.upto(len) { |i| random_id << chars[rand(chars.size-1)] } return random_id end

end

persistence = Persistence.new

1.times

puts "persistence question count : " + persistence.question_count.to_s

persistence.batch_account

1.times {|i| persistence.batch_answer }
puts "persistence answer count : " + persistence.answer_count.to_s

  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 254 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 叶归
    5 引用 • 16 回帖 • 9 关注
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖 • 5 关注
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    92 引用 • 752 回帖 • 1 关注
  • 分享

    有什么新发现就分享给大家吧!

    247 引用 • 1794 回帖
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖 • 1 关注
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    524 引用 • 4601 回帖 • 702 关注
  • Postman

    Postman 是一款简单好用的 HTTP API 调试工具。

    4 引用 • 3 回帖 • 1 关注
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 99 关注
  • Outlook
    1 引用 • 5 回帖
  • WordPress

    WordPress 是一个使用 PHP 语言开发的博客平台,用户可以在支持 PHP 和 MySQL 数据库的服务器上架设自己的博客。也可以把 WordPress 当作一个内容管理系统(CMS)来使用。WordPress 是一个免费的开源项目,在 GNU 通用公共许可证(GPLv2)下授权发布。

    66 引用 • 114 回帖 • 200 关注
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    12 引用 • 54 回帖 • 18 关注
  • Oracle

    Oracle(甲骨文)公司,全称甲骨文股份有限公司(甲骨文软件系统有限公司),是全球最大的企业级软件公司,总部位于美国加利福尼亚州的红木滩。1989 年正式进入中国市场。2013 年,甲骨文已超越 IBM,成为继 Microsoft 后全球第二大软件公司。

    107 引用 • 127 回帖 • 355 关注
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    172 引用 • 516 回帖
  • Word
    13 引用 • 40 回帖
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    289 引用 • 4492 回帖 • 654 关注
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖
  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    7 引用 • 69 回帖 • 1 关注
  • WebClipper

    Web Clipper 是一款浏览器剪藏扩展,它可以帮助你把网页内容剪藏到本地。

    3 引用 • 9 回帖 • 8 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 76 关注
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    8 引用 • 26 回帖 • 2 关注
  • 百度

    百度(Nasdaq:BIDU)是全球最大的中文搜索引擎、最大的中文网站。2000 年 1 月由李彦宏创立于北京中关村,致力于向人们提供“简单,可依赖”的信息获取方式。“百度”二字源于中国宋朝词人辛弃疾的《青玉案·元夕》词句“众里寻他千百度”,象征着百度对中文信息检索技术的执著追求。

    63 引用 • 785 回帖 • 110 关注
  • Sandbox

    如果帖子标签含有 Sandbox ,则该帖子会被视为“测试帖”,主要用于测试社区功能,排查 bug 等,该标签下内容不定期进行清理。

    427 引用 • 1250 回帖 • 597 关注
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    21 引用 • 245 回帖 • 234 关注
  • Mac

    Mac 是苹果公司自 1984 年起以“Macintosh”开始开发的个人消费型计算机,如:iMac、Mac mini、Macbook Air、Macbook Pro、Macbook、Mac Pro 等计算机。

    167 引用 • 595 回帖 • 1 关注
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    12 引用 • 5 回帖 • 636 关注
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖