使用 MetaInspector (Rails) 从用户输入 URL 抓取图像

Scraping images from user input URL using MetaInspector (Rails)

我正在尝试创建一个应用程序,用户可以在其中提交 URL link、标题和说明,它会创建一个带有标题的 post,描述和图像。我希望能够直接从用户提交的 URL 路径中抓取最佳或主要图像,并使用 MetaInspector 将其显示在显示页面上。 (我没有使用 Nokogiri 或 Mechanize 的原因是因为我不太了解它,而 MetaInspector 似乎不那么令人生畏)

问题是我是 rails 的新手,我很难理解大多数教程。

有谁能一步一步地向我解释如何做到这一点,或者向我展示一个非常详细且新手友好的资源?

我有一个包含 link 的 Post 模型,并且还应该将抓取的图像保存为 Paperclip 附件:

class Post < ActiveRecord::Base
  belongs_to :user
  has_attached_file :image
end

# == Schema Information
#
# Table name: posts
#
# id                  :integer          not null, primary key
# title               :string
# link                :string
# description         :text
# created_at          :datetime
# updated_at          :datetime
# user_id             :integer
# image_file_name     :string
# image_content_type  :string
# image_file_size     :integer
# image_updated_at    :datetime

我的应用程序的完整代码可在 github.com/johnnyji/wanderful 获得。

非常感谢任何帮助!谢谢

让我们一步一步来完成。

首先,将 MetaInspector gem 添加到您的 Gemfile

gem 'metainspector'

和 运行 bundle 命令。

我们还需要一点代码:open-uri。有了它,我们就可以像读取本地文件一样从 URLs 读取远程文件。它是 Ruby 标准库的一部分,所以它已经内置,但我们仍然需要 require 它在你的 post.rb:

的顶部
require 'open-uri'

class Post < ActiveRecord::Base
  belongs_to :user
  has_attached_file :image
end

我们想在帖子 link 发生变化时抓取图像,因此我们制作了一个 before_save 回调,只要发生这种情况就会触发:

class Post < ActiveRecord::Base
  belongs_to :user
  has_attached_file :image

  before_save :get_image_from_link,
              if: ->(post) { post.link_changed? }

end
  • 您可以在 ActiveRecord::Callbacks guide.
  • 中找到有关 before_save 和其他回调的更多信息
  • link_changed? 方法是 "dirty tracking" 功能的一部分 ActiveModel::Dirty 提供
  • 那个 if: ->(post) 东西叫做 "stabby lambda" - 它基本上只是一个 Ruby 函数,用当前的 post 作为参数调用。如果是returnstrue,那么before_action就是运行。也可以写成if: Proc.new { |post| post.link_changed? }

现在我们需要 get_image_from_link 方法。由于它只能从 Post 模型本身内部调用,而不是从外部调用(比如 Post.find(5).get_image_from_link),我们将其设为私有方法:

class Post < ActiveRecord::Base
  belongs_to :user
  has_attached_file :image

  before_save :get_image_from_link,
              if: ->(post) { post.link_changed? }

    private

  def get_image_from_link
  end
end

阅读 MetaInspectors README,它有一个很酷的方法,叫做 page.images.best,它为我们从该页面选择正确的图像做了艰苦的工作。所以我们要

  1. 使用 MetaInspector
  2. 解析 link
  3. open-uri打开它选择的最佳图像作为File类对象
  4. 将类似 File 的对象提供给 Paperclip 以另存为附件

所以:

def get_image_from_link
  # `link` here is `self.link` = the current post.
  # At least when reading attributes, `self` is implicit
  # in Ruby
  page = MetaInspector.new(link)

  # maybe the page didn't have images?
  return unless page.images.best.present?

  # when you use IO resources such as files, you need
  # to take care that you `.close` everything you open.
  # Using the block form takes care of that automatically.
  open(page.images.best) do |file|

    # when writing/assigning a value, `self` is not
    # implicit, because when you write `something = 5`, 
    # Ruby cannot know whether you want to assign to 
    # `self.something` or create a new local variable 
    # called `something`
    self.image = file
  end
end

这远非完美,因为它缺少一些错误处理(如果 MetaInspector 无法打开页面怎么办?或者 open-uri 无法读取图像 URL?)。此外,这还有一个缺点,即所有解析、下载等操作都在用户提交或更新她 post 时发生,因此当她单击保存按钮时,她将不得不等待所有这些操作完成完成。

对于下一次迭代,研究异步执行这些操作,例如使用作业队列。 Rails' 新的 Active Job 系统可能是一个很好的起点。