2016-06-13 5 views
0

私はSidekiqとMechanizeを使用して簡単なWebスパイダーを構築しています。Sidekiqは上書きされたインスタンスを機械化します

これを1つのドメインに対して実行すると、正常に動作します。私は複数のドメインのためにそれを実行すると、失敗します。理由は、他のSidekiqワーカーによってインスタンス化されたときにweb_pageが上書きされるという理由があると思いますが、それが正しいかどうか、またはそれを修正する方法がわかりません。

# my scrape_search controller's create action searches on google. 
def create 
    @scrape = ScrapeSearch.build(keywords: params[:keywords], profession: params[:profession]) 
    agent = Mechanize.new 
    scrape_search = agent.get('http://google.com/') do |page| 
    search_result = page.form... 
    search_result.css("h3.r").map do |link| 
     result = link.at_css('a')['href'] # Narrowing down to real search results 
     @domain = Domain.new(some params) 
     ScrapeDomainWorker.perform_async(@domain.url, @domain.id, remaining_keywords) 
    end 
    end 
end 

私はドメインごとにSidekiqジョブを作成しています。私が探しているドメインのほとんどはほんの数ページしか含まれていないので、ページごとのサブジョブは必要ありません。

これは私の労働者である:

class ScrapeDomainWorker 
    include Sidekiq::Worker 
    ... 

    def perform(domain_url, domain_id, keywords) 
    @domain  = Domain.find(domain_id) 
    @domain_link = @domain.protocol + '://' + domain_url 
    @keywords  = keywords 

    # First we scrape the homepage and get the first links 
    @domain.to_parse = ['/'] # to_parse is an array of PATHS to parse for the domain 
    mechanize_path('/') 
    @domain.verified << '/' # verified is an Array field containing valid domain paths 
    get_paths(@web_page) # Now we should have to_scrape populated with homepage links 

    @domain.scraped = 1 # Loop counter 
    while @domain.scraped < 100 
     @domain.to_parse.each do |path| 
     @domain.to_parse.delete(path) 
     @domain.scraped += 1 
     mechanize_path(path) # We create a Nokogiri HTML doc with mechanize for the valid path 
     ... 
     get_paths(@web_page) # Fire this to repopulate to_scrape !!! 
     end 
    end 
    @domain.save 
    end 

    def mechanize_path(path) 
    agent = Mechanize.new 
    begin 
     @web_page = agent.get(@domain_link + path) 
    rescue Exception => e 
     puts "Mechanize Exception for #{path} :: #{e.message}" 
    end 
    end 

    def get_paths(web_page) 
    paths = web_page.links.map {|link| link.href.gsub((@domain.protocol + '://' + @domain.url), "") } ## This works when I scrape a single domain, but fails with ".gsub for nil" when I scrape a few domains. 
    paths.uniq.each do |path| 
     @domain.to_parse << path 
    end 
    end 

end 

これは、私は、単一のドメインをこすりときに動作しますが、私はいくつかのドメインをこすりときweb_pageため.gsub for nilで失敗します。

+0

ようこそスタックオーバーフロー。 「[mcve]」をお読みください。問題を再現するコードを最小限に抑えてください。 –

答えて

0

あなたはあなたの労働者の中に別のクラスであなたのコードをラップして、作成し、そのクラスのオブジェクトができます。

class ScrapeDomainWrapper 
    def initialize(domain_url, domain_id, keywords) 
    # ... 
    end 

    def mechanize_path(path) 
    # ... 
    end 

    def get_paths(web_page) 
    # ... 
    end 
end 

そして、あなたの労働者:

class ScrapeDomainWorker 
    include Sidekiq::Worker 

    def perform(domain_url, domain_id, keywords) 
    ScrapeDomainWrapper.new(domain_url, domain_id, keywords) 
    end 
end 

また、心の中でクマMechanize::Page#linksことnilとすることができます。

+0

私はあなたが示唆したようにそれを包みました。また、すべてのインスタンス変数をローカル変数に変換しました(@web_pageはweb_pageなどになりました)。私はまだ未定義のメソッド 'gsub 'をnilにつける:NilClass for paths = web_page.links.map {| link | link.href.gsub((@ domain.protocol + '://' + @ domain.url)、 ""}}奇妙なことに、もし私がそれを単独で実行すると、うまく動作します。 – Ben

+0

コードを別のクラスに移動した場合は、変数の名前を変更する必要はありません。それらがクラス変数ではなく、インスタンス変数である限り、すべてが問題ありません。また、 'Mechanize :: Link#href'は' nil'となることもあると思います。それを確認する必要があります。 – Wikiti

+0

ええ、私はそれのためのフェイルセーフを追加しました。助けてくれてありがとう! – Ben

関連する問題