rubyでInstagramにseleniumでログインして自動でいいね

久しぶりの投稿でございます。普段seluniumを使うことが増え、色々なことができるようになったので、今回はInstagramで自動でいいねをしてみようと思います。多分あんまりよくない気がするので多用するつもりはないですが笑
### 作るに至った理由
私はInstagramで #橋本奈々未 で検索して出てきた投稿の中で気に入ったものを片っ端からいいねするという日課があるんですが、本垢でそれをやっているとたまにいいねした人が自分の投稿にもいいねを付けてくれたりフォローしてくれるんですね。まあ自分はそういった数字に興味はないんですが、自動でいいねするという行動自体に価値はあるんじゃないかなと思いました。ただ自分の日課はななみんの画像を見たいだけなのでこれを自動化するつもりないので自分では使いません笑

# 実装
三つの機能の関数をもつクラスを作ってみました。

- 自分のアカウントでログインする
- ハッシュタグで検索して出てきた投稿をいいねする
- 特定のユーザーの投稿をいいねする

順番に説明していきます。
## ログイン

```rb
def initialize(username,password)
    Selenium::WebDriver::Chrome.driver_path = "/mnt/c/chromedriver.exe"
    ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36'
    caps = Selenium::WebDriver::Remote::Capabilities.chrome('chromeOptions' => { args: ["--user-agent=#{ua}", 'window-size=1280x800', '--incognito'] }) # シークレットモード
    client = Selenium::WebDriver::Remote::Http::Default.new
    client.read_timeout = 300
    @driver = Selenium::WebDriver.for :chrome, desired_capabilities: caps, http_client: client
    @driver.manage.timeouts.implicit_wait = 30
    @driver.navigate.to'https://www.instagram.com/accounts/login/?source=auth_switcher'
    @driver.find_element(:name, 'username').send_keys(username)
    @driver.find_element(:name, 'password').send_keys(password)
    sleep 1
    @driver.find_element(:name, 'password').send_keys(:return)
  end
```
やっていることはseluniumを設定し、Instagramのログインページへアクセスして引数で与えたusernameとpasswordを入力してenterを押すという流れです。ここでは要素の指定がnameで単純に指定できたため、find_elementで要素を指定してsend_keysで動作させています。ホントはここもexecute_script(あとで出てきます)で行きたかったんですがなぜかうまくいかなかった。

## ハッシュタグで検索していいね

```rb
def good_hashtag(key_word,number)
    encode_word = URI.encode(key_word)
    sleep 3
    @driver.navigate.to"https://www.instagram.com/explore/tags/#{encode_word}/"
    sleep 2
    @driver.execute_script("document.querySelectorAll('article img')[9].click()")
    sleep 2
    number.times{
      begin
        @driver.execute_script("document.querySelectorAll('span.glyphsSpriteHeart__outline__24__grey_9')[1].click()")
      rescue
        puts "already good this post"
      end
      sleep 2
      @driver.execute_script("document.querySelector('a.coreSpriteRightPaginationArrow').click()")
      sleep 2
    }
  end
```
この関数でやっているのは検索ワードをurlにエンコードして検索結果ページに飛び、最新の投稿をクリックし、そのあとはいいねボタンと次の投稿に移るボタンを押すことを繰り返しています。（すでにその投稿にいいねしてたらエラーが起きてたのでrescueでメッセージをターミナルに出力するようにしました。）ここからは全てexecute_scriptという、JavaScriptを実行できる関数を使っています。ブラウザのデベロッパーツールにあるコンソールで一つずつ検証していけるので楽なんですよね。場合によってはブラウザのソースの中にクリック時のJavaScriptがそのまま書いてあったりもしますし。find_elementだと、スクロールが必要なサイトとか、画面の拡大/縮小とか、JavaScriptでしか動かせない部分があるので基本的にはexecute_scriptにしてます。あとInstagramの要素のクラス名はランダムに振られているようでCSSでの指定がめんどくさかった。

## 特定のアカウントの投稿をいいね

```rb
def good_user_post(username,number)
    sleep 3
    @driver.navigate.to"https://www.instagram.com/#{username}/"
    sleep 2
    @driver.execute_script("document.querySelectorAll('article img')[0].click()")
    sleep 2
    number.times{
      begin
        @driver.execute_script("document.querySelectorAll('span.glyphsSpriteHeart__outline__24__grey_9')[1].click()")
      rescue
        puts "already good this post"
      end
      sleep 2
      @driver.execute_script("document.querySelector('a.coreSpriteRightPaginationArrow').click()")
      sleep 2
    }
  end
```
ハッシュタグの場合と変わりませんね。最初の要素の指定をちょっと変えただけです。引数のnumberでいいねする数を指定します。

## 全体のソースと実行部分
importとかクラス化、実行部分です。

```rb:insta_good.rb
require 'selenium-webdriver'
require 'uri'

class InstagramBot
  attr_accessor :driver
  def initialize(username,password)
    略
  end

  def good_hashtag(key_word,number)
    略
  end

  def good_user_post(username,number)
    略
  end

end

# 実行部分
username = "username"
password = "password"
bot = InstagramBot.new(username,password)
key_word = "橋本奈々未"
bot.good_hashtag(key_word,15)
username = "ikomarina_1229"
bot.good_user_post(username,15)
```

## おわりに
seluniumを使えるようになるとなんでもできる気がしてきますね。今回の自動いいねは自分にはどうでもいい機能だけど、次は自動でInstagramの画像を保存する機能とか作りたいなーと思ってます。乃木坂の（ななみんの）画像をたくさん収集してえ。
乃木坂のブログスクレイピングとかもやりたいなーと思っています。