#はじめに
スクレイピングしていると、CAPTCHAが出てプログラムが止まった経験、あると思います。
(そういう方しかこの記事は見ませんよね。)
なんとかCAPTHCAを回避するために、BOTっぽくない動きをさせたり、IP分散という手もありますが、今回は素直にCAPTCHAを解いてやろうと思います。
もちろん、エンジニアなので自分の手で解くより、プログラム上で自動的に解かせたいですよね。
機械学習は学習コストや導入コストが高く、もっと楽したいです。
2Cpathcaというサービスがそれを叶えてくれます。
他にも色々サービスがあるので、自分にあったものを見つけてください。
Pythonの記事はあったのですが、Rubyの記事は見つからなかったので書きました。
##2Capthcaとは
CAPTHCA機能を突破するためのサービスで、APIを利用することで認証を自動化することができます。
有料サービスですが、reCAPTCHA v2なら1,000リクエストで$2.99と安いです。
念の為お断りを入れておきますが、私と2Captchaとの間に販促等での金銭のやりとりはございません。
##Chrome_Remoteとは
ChromeのインスタンスをRubyから操作できるライブラリです。
詳しい使い方は解説ページやリポジトリをご参照ください。
スクレイピングする前提として、そもそもCAPTHCAが出にくいようにしてやる必要があります。
Selenium等と違ってChromeをそのまま動かすChrome_RemoteのほうがBOT判定されにくいと思います。(そのうち違いを検証したい。)
##やりたいこと
reCAPTCHAのデモページを突破します。
2Capthcaのアカウントの作成やapiキーの取得については先人の記事をご参照ください。
#『2Captcha』とRuby+Chrome_RemoteでreCAPTHCAを突破
##2Captchaのapiキーを取得して、ファイル保存しておきます。
---
:2Capthca: 2Captchaのapiキー
##Chromeをdebugging-portつきで起動します。
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 &
##必要なGemをインストールします.
source "https://rubygems.org"
gem 'nokogiri'
gem 'chrome_remote'
bundle install
##rubyプログラム本体です。
require 'nokogiri'
require 'chrome_remote'
require 'yaml'
class CaptchaDetectedException < StandardError; end
class ChromeController
def initialize
@chrome = ChromeRemote.client
# Enable events
@chrome.send_cmd "Network.enable"
@chrome.send_cmd "Page.enable"
end
def open(url)
# ページアクセス
move_to url
captcha_detect
end
def reload_page
sleep 1
@chrome.send_cmd "Page.reload", ignoreCache: false
wait_event_fired
end
def execute_js(js)
@chrome.send_cmd "Runtime.evaluate", expression: js
end
def wait_event_fired
@chrome.wait_for "Page.loadEventFired"
end
# ページ移動
def move_to(url)
sleep 1
@chrome.send_cmd "Page.navigate", url: url
wait_event_fired
end
# HTMLを取得
def get_html
response = execute_js 'document.getElementsByTagName("html")[0].innerHTML'
html = '<html>' + response['result']['value'] + '</html>'
end
def captcha_detect
bot_detect_cnt = 0
begin
html = get_html
raise CaptchaDetectedException, 'captchaが確認されました' if html.include?("captcha")
rescue CaptchaDetectedException => e
p e
bot_detect_cnt += 1
p "captcha突破試行: #{bot_detect_cnt}回目"
doc = Nokogiri::HTML.parse(html, nil, 'UTF-8')
return if captcha_solve(doc) == '解除成功'
reload_page
retry if bot_detect_cnt < 3
p 'captcha突破エラー。Rubyを終了します'
exit
end
p 'captchaはありませんでした'
end
def captcha_solve(doc)
id = request_id(doc).match(/(\d.*)/)[1]
solution = request_solution(id)
return false unless solution
submit_solution(solution)
p captcha_result
end
def request_id(doc)
# APIキーの読み込み
@key = YAML.load_file("key.yaml")[:"2Capthca"]
# data-sitekey属性の値を取得
googlekey = doc.at_css('#recaptcha-demo')["data-sitekey"]
method = "userrecaptcha"
pageurl = execute_js("location.href")['result']['value']
request_url="https://2captcha.com/in.php?key=#{@key}&method=#{method}&googlekey=#{googlekey}&pageurl=#{pageurl}"
# captcha解除を依頼
fetch_url(request_url)
end
def request_solution(id)
action = "get"
response_url = "https://2captcha.com/res.php?key=#{@key}&action=#{action}&id=#{id}"
sleep 15
retry_cnt = 0
begin
sleep 5
# captcha解除コードを取得
response_str = fetch_url(response_url)
raise 'captcha解除前' if response_str.include?('CAPCHA_NOT_READY')
rescue => e
p e
retry_cnt += 1
p "リトライ:#{retry_cnt}回目"
retry if retry_cnt < 10
return false
end
response_str.slice(/OK\|(.*)/,1)
end
def submit_solution(solution)
# 解除コードを所定のtextareaに入力
execute_js("document.getElementById('g-recaptcha-response').innerHTML=\"#{solution}\";")
sleep 1
# 送信ボタンクリック
execute_js("document.getElementById('recaptcha-demo-submit').click();")
end
def captcha_result
sleep 1
html = get_html
doc = Nokogiri::HTML.parse(html, nil, 'UTF-8')
doc.at_css('.recaptcha-success') ? '解除成功' : '解除失敗'
end
def fetch_url(url)
sleep 1
`curl "#{url}"`
end
end
crawler = ChromeController.new
url = 'https://www.google.com/recaptcha/api2/demo'
crawler.open(url)
プログラムを実行すると、reCAPTCHAのデモページにアクセスして、CAPTCHAの突破を試みます。
bundle exec ruby crawler.rb
#最後に
スクレイピングを行う目的や態様、スクレイピングで得たデータの取り扱い方によっては、著作権法、個人情報保護法に抵触してしまう恐れがあります。
皆さんに楽しいスクレイピングライフがあることを祈ります。