それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

SeleniumのWebDriver(chrome)からCookieを取りだしてHTTP::CookieにしたりCurlで再利用する

Selenium のWebDriverからCookieを取り出したら

Seleniumで画像をだけを取り出して保存したり、ファイルを取り出すのは不便だよね。

だけど、直接Curlで叩くのもCookieの問題でもっと不便だよね。

そこでSeleniumCookieCurlで使える形式やNetscape形式で保存したり、RubyPythonなどの http cookieクラスに変換して保存したら楽だと思ったので作ってみた。

私はスクレイピングメインなのでChromeを使っています。

Selenium からCookieを取り出してファイルに保存する。

保存する形式は次の通り。

imid=HeNFSXsHS82I7KZB9xiYkQ; expires=Thu, 06-Jun-2019 17:43:13 GMT; domain=.im-apps.net; path=/;
def save_cookie_to_cookie_jar(driver)
    s = driver.manage.all_cookies.map{|e| [
        "#{e[:name]}=#{e[:value]}" ,
        "#{e[:secure] ? 'secure; ':'' }",
        (e[:expires]||Time.now).strftime('%a, %d-%b-%Y %T GMT'),
        "domain=#{e[:domain]}" ,
        "path=#{e[:path]}" ,
    ].reject{|e| e == '' }.join("; ")

    }.join("\n")
    tmp = Dir::Tmpname.make_tmpname( 'a', '.cookie_jar.txt' )
    tmp = File.expand_path(tmp, Dir.pwd)
    open(tmp, 'w'){|f| f.puts s}
    return tmp
  end

curl で使える形式に保存してみる

## 取り出したファオルの形式は次の通り。( タブ区切り )
example.com    FALSE    /    TRUE    0    XSRF-TOKEN    ePG7sHRafqxj
  def save_cookie_for_curl(driver)
    s=  driver.manage.all_cookies.map{|e|
      [
        e[:domain],
        ((e[:domain]=~/^\./)==0).to_s.upcase,
        (e[:expires]||Time.now).to_time.to_i ,
        e[:path],
        e[:secure].to_s.upcase,
        e[:name], e[:value]
      ].join("\t")
    }.join("\n")
    tmp = Dir::Tmpname.make_tmpname( 'a', '.cookie_jar.txt' )
    tmp = File.expand_path(tmp, Dir.pwd)
    open(tmp, 'w'){|f| f.puts s}
    return tmp
  end

cookie さえなんとかすれば

スクレイピングでHTMLからデータを抜くのに、並行処理だとか、分散処理が出来て一気に楽になる。

サーバーの負荷はたしかに気になるけど、ドメインを dig って CDN 経由ならもう気にしないことにしてる。

CDNはそう簡単に落ちないし、強い。

wkhtmltopdf と組合せたり、 chrome --print-pdf と組合せたり、curl と組合せたり、Mechanizeと組合せたり、Cookieさえなんとかすれば結構楽しい世界が広がる。

関連資料

http://takuya-1st.hatenablog.jp/entry/2013/08/15/031247

SeleniumのchromeDriverのプロファイルのPATHを調べる方法

起動済みのChromeのプロファイルの場所を調べるには

SeleniumからChromedriverが起動したときに、プロファイルがちゃんと渡されて起動したかどうか

それを調べるにはどうすれば良いのか。少し考えてみたら chrome://version/ を見るしか無いと思われる

chromeのプロファイル

f:id:takuya_1st:20170627035700p:plain

version に行くとわかる。

Seleniumなら

executeScript("window.open('chrome://version')")

を実行して タブで開いて、タブへアクセスして version のページからプロファイルを取得することが出来る。

ただし headlessは

chrome --headless で起動している場合には、version のページが全く空っぽになるのでここから取ることはできなかった。

別の方法を考えたけどやっぱり無理で。 headlessに関わるアレコレ(widthやnew tab )に苦しめられるので linux ならxvfb ( X virtual frame buffer) を使うほうが、ずっと楽な気がします。

selenium で chrome ヘッドレスで新規タブの要素をクリックできない

selenium + chromedriver を headlessを扱うときに困る。

新規タブを開かれると、element not clickable で、currently visibleで、 displayed がfalse になる。

なんで、クリックできないエラーになるのか頭をひねったら、width だった。

原因と再現

driver.execute_script("var tab = window.open('about:blank','_blank');");
driver.switch_to.window(driver.window_handles.last)
driver.execute_script('return {"w":window.innerWidth, "h":window.innerHeight};')
# => {"w"=>0, "h"=>0}

お疲れ様でした。Width=0,Height=0です。これではクリックできない。そうなります

chrome headless はdriver.manage.window.resize も通らないのでこれは詰んだと思う。phantomJSだとたしか出来たような。。

新規タブを開かせないように window.open を 乗っ取るしか無いのかもしれない

追記

Selenium側の問題かもしれないと思って Chrome の Dev tool で ポートにアクセスして、実際に window.open して、リモートデバッグで見てきたが、駄目だった.remote debug 経由でタブを開くと問題ないなけど。window.openが呼ばれるとどうも駄目みたい。chrome 側のバグっぽい。

f:id:takuya_1st:20170614154132p:plain:w600

ちょっとした思いなど。

chrome の headlessは 新規タブが開いてフォーカスが動かないのもちょっと面倒だし。 xvfb の方が楽ですね。それかseleniumすてて chrome の debugging protocol を使う方が良いかもしれない。

Chrome DevTools Protocol Viewer

戻る禁止の銀行WEBは戻る禁止 の制限があるので、新規タブを開きまくる。戻る禁止はCRSFやセッションハイジャック対策だとおもうけど、そんなことはPOSTを使うページだけに限定したらいい話だし。戻る対策としては戻る禁止はtoo bad だし、全ページをPOSTにするから戻るができないので新規タブを開きまくる。なのでwebappとして間違ってると思う。iframeに埋め込まれることを防ぐならHTTPヘッダでやるべきだと思うし。クリックジャッキングやはCSPでなんとかなるだろうし、もう戻る禁止と新規タブはやめてほしいわ