[Rails]  Selenium-webdriverでWebページのテキストを抽出する

# 概要
* selenium-webdriverを使ってchromiumのheadlessモードで表示したwebページからテキストを抽出する
* CSS or tagで抽出する場所を指定し、テキスト出力できます。
* Excelに優しい☺️タブ区切りで出力もできます。

# 環境
* 拙記事[Herokuのrailsアプリでheadless chromeを使ってwebページのスクリーンショットをとる](https://qiita.com/junara/items/375c4b18486cb4fd2a43)を参照ください

# 使い方

後述のコードを `web_capture.rb` としてrailsの適当なフォルダ（ `lib/web_capture.rb` とか）にいれて、 `rails c` などで下記のようにうごかします。（urlは適当なやつに書き換えて下さい。）

Rails.logger 等のRails固有の記述を削除すればrailsなくてもうごきます。その場合は適宜修正お願いします。

```
rails c
> web_capture = WebCapture.new(url: 'https://xxxxyyyyzzzzexample.com')
> web_capture.extract # bodyタグ内のテキストを改行区切りで出力
> web_capture.extract(tag_name: 'li', format: :tab_text) # liタグの全テキストをタブ区切りで出力
> web_capture.extract(css: 'div.menu') # cssセレクタdiv.menu内のテキストを改行区切りで出力
> web_capture.extract(tag_name: 'li', format: :array) # liタグの全テキストをarrayとして出力
```

# コード

ほとんど[こちらのscreenshot_capture.rb](https://qiita.com/junara/items/375c4b18486cb4fd2a43)と同じです。差分のみ解説します。

```web_capture.rb
# frozen_string_literal: true

require 'selenium-webdriver'
require 'httpclient'
class WebCapture
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :url, :string

  CONVERTER = {
    array:  lambda do |els|
      els.map(&:text)
    end,
    text: lambda do |els|
            ary = els.map(&:text)
            ary.join(' ')
          end,
    tab_text: lambda do |els|
                ary = els.map(&:text)
                ary.join("\t")
              end
  }.freeze

  def initialize(args = {})
    super(args)
    @client = HTTPClient.new
  end

  def extract(tag_name: 'body', format: :text, css: '')
    return unless valid?

    begin
      driver = Selenium::WebDriver.for :chrome, options: headless_chrome_options
      driver.navigate.to url
      sleep(1)
      elements = if css.present?
                   driver.find_elements(:css, css)
                 elsif tag_name.present?
                   driver.find_elements(:tag_name, tag_name)
                 end
      output = CONVERTER[format].call(elements)
    rescue => e
      Rails.logger.error e
    end
    driver.close
    driver.quit
    output
  end

  def valid?
    return false unless url
    return false unless successful?
    true
  end

  def successful?
    @content ||= @client.get(url)
    HTTP::Status.successful?(@content.status)
  rescue StandardError => e
    Rails.logger.error e
  end

  def headless_chrome_options
    options = Selenium::WebDriver::Chrome::Options.new
    options.add_argument('--headless')
    options.add_argument('--no-sandbox')
    options.add_argument('--disable-gpu')
    options.add_argument('--hide-scrollbars')
    options.binary = '/app/.apt/usr/bin/google-chrome' if heroku?
    options
  end

  def heroku?
    Rails.env.production?
  end
end

```


## 解説

seliniumでcssセレクタもしくはタグ名指定で抽出するところは、下記のコードです。
引数でcssが指定されていたら、そちらを優先します。


```web_capture.rb
      elements = if css.present?
                   driver.find_elements(:css, css)
                 elsif tag_name.present?
                   driver.find_elements(:tag_name, tag_name)
                 end

```

* タブ区切り等の出力はここでやっています。

```web_capture.rb
  CONVERTER = {
    array:  lambda do |els|
      els.map(&:text)
    end,
    text: lambda do |els|
            ary = els.map(&:text)
            ary.join(' ')
          end,
    tab_text: lambda do |els|
                ary = els.map(&:text)
                ary.join("\t")
              end
  }.freeze
```

以上。