0

我正在尝试使用 ruby​​ 脚本将大量信息插入到 Sqlite3 数据库中。在 250 db_prepare_location.execute 执行此操作后,它停止工作说:

.rvm/gems/ruby-1.9.2-p290/gems/sqlite3-1.3.6/lib/sqlite3/statement.rb:67:in `step': unable to open database file (SQLite3::CantOpenException)
    from /Users/ashley/.rvm/gems/ruby-1.9.2-p290/gems/sqlite3-1.3.6/lib/sqlite3/statement.rb:67:in `execute'
    from programs.rb:57:in `get_program_details'
    from programs.rb:22:in `block in get_link'
    from /Users/ashley/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:1768:in `each'
    from /Users/ashley/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:1202:in `block in foreach'
    from /Users/ashley/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:1340:in `open'
    from /Users/ashley/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/csv.rb:1201:in `foreach'
    from programs.rb:20:in `get_link'
    from programs.rb:63:in `<module:Test>'
    from programs.rb:15:in `<main>'

这是我的代码:

require 'net/http'
require 'json'
require 'nokogiri'
require 'open-uri'
require 'csv'
require 'sqlite3'
require "bundler/setup"
require "capybara"
require "capybara/dsl"

Capybara.run_server = false
Capybara.default_driver = :selenium
Capybara.current_driver = :selenium

module Test
  class Tree
    include Capybara::DSL

    def get_link
      CSV.foreach("links.csv") do |row|
        link = row[0]
        get_details(link)
      end
    end

    def get_details(link)
      db = SQLite3::Database.open "development.sqlite3"
      address = []
      address_text = []
      visit("#{link}")
      name = find("#listing_detail_header").find("h3").text
      page.find(:xpath, "//div[@id='listing_detail_header']").all(:xpath, "//span/span").each {|span| address << span }
      if address.size == 4
        street_address = address[0].text
        address.shift
        address.each {|a| address_text << a.text }
        city_state_address = address_text.join(", ")
      else
        puts link
        street_address = ""
        city_state_address = ""
      end
      if page.has_css?('.provider-click_to_call')
        find(".provider-click_to_call").click
        phone_number = find("#phone_number").text.gsub(/[()]/, "").gsub(" ", "-")
      else
        phone_number = ""
      end
      if page.has_css?('.provider-website_link')
        website = find(".provider-website_link")[:href]
      else
        website = ""
      end
      description = find(".listing_details_list").find("p").text
      db_prepare_location = db.prepare("INSERT INTO programs(name, city_state_address, street_address, phone_number, website, description) VALUES (?, ?, ?, ?, ?, ?)")
      db_prepare_location.bind_params name, city_state_address, street_address, phone_number, website, description
      db_prepare_location.execute
    end
end


test = Test::Tree.new
test.get_link
end

这里有什么问题,我能做些什么来解决它?让我知道是否需要其他信息。

4

1 回答 1

3

您可能会用完文件描述符。每次调用时get_details,都会打开 SQLite 数据库:

db = SQLite3::Database.open "development.sqlite3"

但你从来没有明确地关闭它;相反,您依靠垃圾收集器来清理所有dbs 并关闭所有文件描述符。每次打开数据库,都需要分配一个文件描述符,关闭数据库释放文件描述符。如果你调用get_details的速度比 GC 清理的速度快,你将用完文件描述符,随后的SQLite3::Database.open调用将失败。

尝试db.close在末尾添加get_details

您可能还必须关闭准备好的语句,因此您应该db_prepare_location.close在之前db.close

def get_details
  #...
  db_prepare_location.close
  db.close
end

是的,Ruby 具有垃圾收集功能,但这并不意味着您不必手动管理资源。

另一种选择(DGM 暗示)是在构造函数中打开与数据库的连接:

def initialize
  @db = SQLite3::Database.open "development.sqlite3"
end

然后挂断SQLite3::Database.open电话get_details@db改为使用。你不再需要一个db.closeinget_details但你仍然想要这个db_prepare_location.close电话。

于 2012-07-26T02:15:39.243 回答