Ruby 脚本实现数据爬取

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

工作上使用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 回帖 • 238 关注
  • 爬虫

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

    106 引用 • 275 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 487 关注
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    581 引用 • 3535 回帖
  • 国际化

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

    8 引用 • 26 回帖 • 2 关注
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    325 引用 • 1395 回帖
  • 新人

    让我们欢迎这对新人。哦,不好意思说错了,让我们欢迎这位新人!
    新手上路,请谨慎驾驶!

    52 引用 • 228 回帖
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 641 关注
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    6 引用 • 26 回帖 • 539 关注
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 113 关注
  • Hadoop

    Hadoop 是由 Apache 基金会所开发的一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。

    86 引用 • 122 回帖 • 625 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    181 引用 • 400 回帖 • 1 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 702 关注
  • Google

    Google(Google Inc.,NASDAQ:GOOG)是一家美国上市公司(公有股份公司),于 1998 年 9 月 7 日以私有股份公司的形式创立,设计并管理一个互联网搜索引擎。Google 公司的总部称作“Googleplex”,它位于加利福尼亚山景城。Google 目前被公认为是全球规模最大的搜索引擎,它提供了简单易用的免费服务。不作恶(Don't be evil)是谷歌公司的一项非正式的公司口号。

    49 引用 • 192 回帖
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 384 回帖 • 5 关注
  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    197 引用 • 541 回帖 • 1 关注
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    111 引用 • 54 回帖 • 1 关注
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 304 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    142 引用 • 209 回帖 • 1 关注
  • Facebook

    Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。

    4 引用 • 15 回帖 • 437 关注
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 83 关注
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    20 引用 • 7 回帖 • 1 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖 • 1 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 72 关注
  • Flume

    Flume 是一套分布式的、可靠的,可用于有效地收集、聚合和搬运大量日志数据的服务架构。

    9 引用 • 6 回帖 • 651 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 779 关注
  • IPFS

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

    21 引用 • 245 回帖 • 239 关注
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    170 引用 • 414 回帖 • 374 关注