Ruby on Rails使用Nokogiri爬取图片src后无法保存并展示:报Nil location provided错误
解决Rails中Nokogiri爬取图片时的「Nil location provided」错误
看起来你遇到的这个「Nil location provided. Can't build URI.」错误,主要是因为图片链接处理不严谨,再加上视图里的语法问题导致的,咱们一步步来修复:
错误根源分析
- 图片链接为空/无效:当Nokogiri找不到目标
img元素,或者src属性为空时,拼接后的image_url会变成无效的URI(比如空字符串或者https://www.jacadi.fr),image_tag处理这种值就会抛出错误。 - 循环覆盖属性:你的控制器里用
each循环给@product的属性赋值,最后只会保留最后一个循环项的值,如果最后一个图片链接无效,就会导致image_url异常。 - 选择器语法错误:你写的
css("j-wrapper-content")和css("j-prd-price p")缺少类选择器的.前缀,Nokogiri根本找不到对应的元素,会导致name和price为空。 - 视图语法错误:
elsif后面没有任何条件判断,这会直接导致视图渲染失败。
完整修复方案
1. 修正控制器代码(处理爬取逻辑)
def scrape # 先校验page_url的有效性,避免URI.open直接报错 return unless page_url.present? && page_url.start_with?('http') doc = Nokogiri::HTML(URI.open(page_url)) # 修正类选择器:添加.前缀,确保能找到目标元素 items = doc.css(".j-wrapper-content") images = doc.css("div.j-img") # 处理图片:只取第一个有效的图片链接(按需调整是否要所有图片) images.each do |image| # 先拿到第一个img元素,避免NodeSet空值问题 img_element = image.css("img").first next unless img_element # 清理src值,过滤空字符串 link = img_element.attr('src').to_s.strip next if link.empty? # 兼容绝对路径和相对路径,避免重复拼接域名 @product.image_url = if link.start_with?('http', '//') # 处理//开头的协议相对路径 link.start_with?('//') ? "https:#{link}" : link else "https://www.jacadi.fr#{link}" end break # 只取第一个有效图片,不需要所有的话就保留这个break end # 处理产品信息:同样取第一个有效商品数据 items.each do |item| @product.name = item.css("h1").text.strip price_text = item.css(".j-prd-price p").text.strip # 避免空字符串转float报错 @product.price = price_text.to_f if price_text.present? break # 只取第一个商品,按需移除 end # 保存前做基础校验,避免save!抛出异常 if @product.name.present? && (@product.image_url.present? || @product.picture.attached?) @product.save! else flash[:alert] = "未能获取有效的产品信息,请检查爬取目标页面结构是否变更" end end
2. 修正视图代码(修复语法错误)
把elsif后面补全条件判断,确保只有当image_url存在时才渲染外部图片:
<% if product.picture.attached? %> <%= image_tag(product.picture, class: "card-pict") %> <% elsif product.image_url.present? %> <%= image_tag(product.image_url, class: "card-pict") %> <% else %> <%= image_tag("gift.png", class: "card-pict mini") %> <% end %>
额外提示
- 如果需要批量爬取多个产品,不要在循环里复用同一个
@product实例,应该在循环内初始化新的Product.new并保存。 - 爬取外部网站时,最好加上请求头(比如User-Agent),避免被目标网站拦截:
doc = Nokogiri::HTML(URI.open(page_url, "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"))
内容的提问来源于stack exchange,提问作者Ludovic Villain
相关产品推荐
相关产品推荐

