Mar*_*tto 16 ruby open-uri ruby-on-rails exception
我有一个rake任务,负责对数百万个URL进行批处理.因为这个过程需要很长时间,所以我有时会发现我正在尝试处理的URL不再有效 - 404,网站已关闭,无论如何.
当我最初写这篇文章的时候,基本上只有一个网站会在处理过程中不断下降,所以我的解决方案就是使用open-uri
,解决产生的任何异常,等待一段时间,然后重试.
当数据集较小时,这种工作正常,但现在已经过了很长时间,我发现URL不再存在,并产生404.
使用404的情况,当发生这种情况时,我的脚本就坐在那里并无限循环 - 显然很糟糕.
我应该如何处理页面无法成功加载的情况,更重要的是,它如何适应我构建的"堆栈"?
我对这个很新,还有Rails,所以欢迎任何关于我在这个设计中出错的地方的意见!
这是一些匿名代码,显示了我的内容:
调用MyHelperModule的rake任务:
# lib/tasks/my_app_tasks.rake
namespace :my_app do
desc "Batch processes some stuff @ a later time."
task :process_the_batch => :environment do
# The dataset being processed
# is millions of rows so this is a big job
# and should be done in batches!
MyModel.where(some_thing: nil).find_in_batches do |my_models|
MyHelperModule.do_the_process my_models: my_models
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
MyHelperModule接受my_models
并使用ActiveRecord做更多事情.它叫SomeClass
:
# lib/my_helper_module.rb
module MyHelperModule
def self.do_the_process(args = {})
my_models = args[:my_models]
# Parallel.each(my_models, :in_processes => 5) do |my_model|
my_models.each do |my_model|
# Reconnect to prevent errors with Postgres
ActiveRecord::Base.connection.reconnect!
# Do some active record stuff
some_var = SomeClass.new(my_model.id)
# Do something super interesting,
# fun,
# AND sexy with my_model
end
end
end
Run Code Online (Sandbox Code Playgroud)
SomeClass
将通过网页访问WebpageHelper
并处理页面:
# lib/some_class.rb
require_relative 'webpage_helper'
class SomeClass
attr_accessor :some_data
def initialize(arg)
doc = WebpageHelper.get_doc("http://somesite.com/#{arg}")
# do more stuff
end
end
Run Code Online (Sandbox Code Playgroud)
WebpageHelper
是捕获异常的地方,在404的情况下启动无限循环:
# lib/webpage_helper.rb
require 'nokogiri'
require 'open-uri'
class WebpageHelper
def self.get_doc(url)
begin
page_content = open(url).read
# do more stuff
rescue Exception => ex
puts "Failed at #{Time.now}"
puts "Error: #{ex}"
puts "URL: " + url
puts "Retrying... Attempt #: #{attempts.to_s}"
attempts = attempts + 1
sleep(10)
retry
end
end
end
Run Code Online (Sandbox Code Playgroud)
使用带外错误处理和不同的概念抓取模型来加速操作.
还有许多其他答案可以解决如何处理用例异常的问题.我采取了一种不同的方法,因为出于多种原因,处理异常从根本上说是错误的方法.
在他的" Exceptional Ruby"一书中,Avdi Grimm提供了一些基准测试,显示异常的性能比使用早期返回等替代编码技术慢约156%.
在实用程序员中:作者从Journeyman到Master,作者声明"[E] xceptions应该保留用于意外事件." 在您的情况下,404错误是不可取的,但并不是意料之外的 - 事实上,处理404错误是核心考虑因素!
简而言之,您需要一种不同的方法.优选地,替代方法应该提供带外错误处理并防止您的进程在重试时阻塞.
你有很多选择,但我要推荐的是处理404状态代码作为正常结果.这允许您"快速失败",但也允许您稍后重试页面或从队列中删除URL.
考虑这个示例模式:
ActiveRecord::Schema.define(:version => 20120718124422) do
create_table "webcrawls", :force => true do |t|
t.text "raw_html"
t.integer "retries"
t.integer "status_code"
t.text "parsed_data"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
Run Code Online (Sandbox Code Playgroud)
这里的想法是你只需将整个 scrape视为一个原子过程.例如:
你有这个页面吗?
太棒了,存储原始页面和成功的状态代码.您甚至可以稍后解析原始HTML,以便尽快完成抓取.
你有404了吗?
好的,存储错误页面和状态代码.快点继续!
在您的流程完成抓取网址后,您可以使用ActiveRecord查找来查找最近返回404状态的所有网址,以便您可以采取适当的措施.也许您想重试页面,记录消息,或者只是从您的URL列表中删除URL来抓取 - "适当的操作"取决于您.
通过跟踪重试次数,您甚至可以区分瞬态错误和更多永久性错误.这允许您为不同的操作设置阈值,具体取决于给定URL的抓取失败的频率.
此方法还具有利用数据库管理并发写入和在进程之间共享结果的额外好处.这将允许您在多个系统或进程之间分配工作(可能包含消息队列或分块数据文件).
在初始刮擦过程中花费更少的时间进行重试或错误处理可以显着加快您的过程.但是,某些任务对于单机或单进程方法来说太大了.如果您的流程加速仍然不足以满足您的需求,您可能需要考虑使用以下一项或多项的线性较小的方法:
优化应用程序逻辑应该足以满足常见情况,但如果没有,则扩展到更多进程或扩展到更多服务器.缩小肯定会有更多工作,但也会扩展您可用的处理选项.
Curb
有一个更简单的方法来做到这一点,而不是更好(和更快)的选择open-uri
.
错误遏制报告(你可以拯救并做一些事情:
http://curb.rubyforge.org/classes/Curl/Err.html
遏制宝石:https: //github.com/taf2/curb
示例代码:
def browse(url)
c = Curl::Easy.new(url)
begin
c.connect_timeout = 3
c.perform
return c.body_str
rescue Curl::Err::NotFoundError
handle_not_found_error(url)
end
end
def handle_not_found_error(url)
puts "This is a 404!"
end
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2954 次 |
最近记录: |