Python、SeleniumにChromeの組み合わせは、PhantomJSが息絶えてしまった今では自動化の王道だと思います。そんな王道の組み合わせをWindowsで試してたら、Headlessモードの時だけページの取得が上手くできない事象を目撃しましたので、メモしておきます。Headlessモードじゃないなら正常なのに、Headlessモードへ変更した途端におかしくなってしまいました。
どんなURLを指定しても、HeadlessモードではSeleniumで取得した結果が空っぽのHTMLになってしまいます。Headlessモードを外すだけで、ちゃんと取得します。謎。
Chromedriverは、インストール先のパスが通っている前提です。
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.binary_location = 'Chrome Canaryのアドレス'
driver = webdriver.Chrome(options=options)
driver.get('https://www.yahoo.co.jp/')
time.sleep(3)
html = driver.page_source
print(html)
driver.save_screenshot("hoge.png")
driver.quit()
多分、極端に変なことはしてないと思うんですが、これが動作するとコンソールには下記のHTMLソースが表示されます。
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body></body></html>
中身空っぽじゃねーかよ!
実際save_screenshot
で生成されるファイルを見てみると下記の通りです。
オドロキの白さ!
まぁそうですわな、HTMLファイル中に何もないんだから。
ソースはこんな感じ。Headlessモードをコメントで外しただけです。
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
# options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.binary_location = 'Chrome Canaryのアドレス'
driver = webdriver.Chrome(options=options)
driver.get('https://www.yahoo.co.jp/')
time.sleep(3)
html = driver.page_source
print(html)
driver.save_screenshot("hoge.png")
driver.quit()
これを実行すると数秒だけChromeの画面が出現します。Headlessモードじゃないから当たり前ですね。その後コンソールを見てみると、下記の通り取得したHTMLソースが表示されています。なお、すべてを記述すると長いので省略しています。
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" lang="ja"><head><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /><title>Yahoo! JAPAN</title><meta name="description" content="あなたの毎日をアップデートする情報ポータル。検索、ニュース、天気、スポーツ、メール、ショッピング、オークションなど便利なサービスを展開しています。" /><meta name="robots" content="noodp" /><meta name="viewport" content="width=1010" /><link rel="dns-prefetch" href="//s.yimg.jp" /><link rel="dns-prefetch" href="//yads.c.yimg.jp" /><meta name="google-site-verification" content="fsLMOiigp5fIpCDMEVodQnQC7jIY1K3UXW5QkQcBmVs" /><link rel="alternate" href="android-app://jp.co.yahoo.android.yjtop/yahoojapan/home/top" /><link rel="alternate" media="only screen and (max-width: 640px)" href="https://m.yahoo.co.jp/" /><link rel="canonical" href="https://www.yahoo.co.jp/" /><link rel="shortcut icon" href="https://s.yimg.jp/c/icon/s/bsc/2.0/favicon.ico" type="image/vnd.microsoft.icon" /><link rel="icon" href="https://s.yimg.jp/c/icon/s/bsc/2.0/favicon.ico" type="image/vnd.microsoft.icon" /><meta property="og:title" content="Yahoo! JAPAN" /><meta property="og:type" content="website" /><meta property="og:url" content="https://www.yahoo.co.jp/" /><meta property="og:image" content="https://s.yimg.jp/images/top/ogp/fb_y_1500px.png" /><meta property="og:description" content="あなたの毎日をアップデートする情報ポータル。検索、ニュース、天気、スポーツ、メール、ショッピング、オークションなど便利なサービスを
展開しています。" /><meta property="og:site_name" content="Yahoo! JAPAN" /><meta property="twitter:card" content="summary_large_image" /><meta property="twitter:site" content="@Yahoo_JAPAN_PR" /><meta property="twitter:image" content="https://s.yimg.jp/images/top/ogp/tw_y_1400px.png" /><meta property="fb:app_id" content="472870002762883" /><link rel="stylesheet" href="//s.yimg.jp/images/top/orion/1.0.17/bundle_1.0.17.css" /><script
async="" data-jsonpid="" src="https://cdn-gl.imrworldwide.com/novms/js/2/nlsSDK600.bundle.min.js"></script>・・・(以下省略)
HTMLがちゃんと取得できているのだから、スクリーンショットも正常に取得できています。なお、スクリーンショット画像は下部分を省略しました。
ただHeadlessモードを摘要するだけで、こうも挙動が違うものなんでしょうか。
ここを参考に、Chrome実行時の引数を追加してみました。
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.set_headless(headless=True)
options.add_argument('--ignore-ssl-errors=true')
options.add_argument('--ssl-protocol=any')
options.add_argument('--no-sandbox')
options.binary_location = 'Chrome Canaryのアドレス'
driver = webdriver.Chrome(options=options)
driver.get('https://www.yahoo.co.jp/')
time.sleep(3)
html = driver.page_source
print(html)
driver.save_screenshot("hoge.png")
driver.quit()
結果は・・・変化なし!
たまたま実行環境がProxyの影響下だったので、ここを参考にして、SeleniumにProxyの設定を盛り込んでみました。
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.proxy import Proxy, ProxyType
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.binary_location = 'Chrome Canaryのアドレス'
desired_caps = options.to_capabilities()
prox = Proxy()
prox.proxy_type = ProxyType.MANUAL
prox.http_proxy = "Proxyのアドレス"
prox.add_to_capabilities(desired_caps)
driver = webdriver.Chrome(options=options, desired_capabilities=desired_caps)
driver.get('https://www.yahoo.co.jp/')
time.sleep(3)
html = driver.page_source
print(html)
driver.save_screenshot("hoge.png")
driver.quit()
結果は・・・変化なし!
2連敗・・・。
ここを参考に、Chromedriverの設定を追加してみました。
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.binary_location = 'Chrome Canaryのアドレス'
caps = DesiredCapabilities.CHROME.copy()
caps['acceptInsecureCerts'] = True
driver = webdriver.Chrome(options=options, desired_capabilities=caps)
driver.get('https://www.yahoo.co.jp/')
time.sleep(3)
html = driver.page_source
print(html)
driver.save_screenshot("hoge.png")
driver.quit()
結果は・・・変化なし!
3連敗か・・・。
いかんせん、もともとWindows環境で実行しているのは少数派だからか、情報が少ないことのが痛いです。さらに、「自分はこれで動いたぜ」っていう方法を実行してもことごとくだめなので、そもそも別の要素が影響しているんじゃないか、という疑心暗鬼に陥る始末。
・・・と、そんなときに救世主が表れました。
そうです、ChromeでダメならFirefoxにすればいいのです。動けば正義!
というわけで環境を準備します。
chromedriverのFirefox版です。こちらからダウンロードした実行形式ファイルを任意の場所に展開して、パスを通しておきます。
なお、pip
などで導入すれば他のPythonパッケージと同様にバージョン管理ができます。さらに、パス設定をする必要もないので楽です。どちらを選択するかはお好みで。
$ geckodriver --version
geckodriver 0.26.0 (e9783a644016 2019-10-10 13:38 +0000)
Firefox用に準備したソースはこんな感じ。
import time
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
options = Options()
options.add_argument('-headless')
driver = webdriver.Firefox(firefox_options=options)
driver.get('https://www.yahoo.co.jp/')
time.sleep(3)
html = driver.page_source
print(html)
driver.save_screenshot("hoge.png")
driver.quit()
だいぶ簡略化してるんで何もないですが、これを実行してみましょう。
<html lang="ja"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><title>Yahoo! JAPAN</title><meta name="description" content="あなたの毎日をアップデートする情報ポータル。検索、
ニュース、天気、スポーツ、メール、ショッピング、オークションなど便利なサービスを展開しています。"><meta name="robots" content="noodp"><meta name="viewport" content="width=1010"><link rel="dns-prefetch" href="//s.yimg.jp"><link rel="dns-prefetch" href="//yads.c.yimg.jp"><meta name="google-site-verification" content="fsLMOiigp5fIpCDMEVodQnQC7jIY1K3UXW5QkQcBmVs"><link rel="alternate" href="android-app://jp.co.yahoo.android.yjtop/yahoojapan/home/top"><link rel="alternate" media="only screen and (max-width: 640px)" href="https://m.yahoo.co.jp/"><link rel="canonical" href="https://www.yahoo.co.jp/"><link rel="shortcut icon" href="https://s.yimg.jp/c/icon/s/bsc/2.0/favicon.ico" type="image/vnd.microsoft.icon"><link rel="icon" href="https://s.yimg.jp/c/icon/s/bsc/2.0/favicon.ico" type="image/vnd.microsoft.icon"><meta property="og:title" content="Yahoo! JAPAN"><meta property="og:type" content="website"><meta property="og:url" content="https://www.yahoo.co.jp/"><meta property="og:image" content="https://s.yimg.jp/images/top/ogp/fb_y_1500px.png"><meta property="og:description" content="あなたの毎日をアップデートする情報ポータル。検索、ニュース、天気、スポーツ、メール、ショッピング、オークションなど便利なサービスを展開しています。"><meta property="og:site_name" content="Yahoo! JAPAN"><meta property="twitter:card" content="summary_large_image"><meta property="twitter:site" content="@Yahoo_JAPAN_PR"><meta property="twitter:image" content="https://s.yimg.jp/images/top/ogp/tw_y_1400px.png"><meta property="fb:app_id"
content="472870002762883"><link rel="stylesheet" href="//s.yimg.jp/images/top/orion/1.0.17/bundle_1.0.17.css"><script async="" data-jsonpid="" src="https://cdn-gl.imrworldwide.com/novms/js/2/nlsSDK600.bundle.min.js"></script><script async="" src="https://cdn-gl.imrworldwide.com/conf/P2ED650F9-2101-4CB9-845D-ED37E7119BAD.js#name=nSdkInstance&ns=NOLBUNDLE"></script>・・・(以下省略)・・・
やったぜ!
ちゃんとスクリーンショットも取得できています。やったぜ!
Firefoxはいいぞ。
結局のところ、なんでHeadlessモードのChromeで正常にHTMLを取得できないのか、根本的な解決には至りませんでした。ちなみに、今でも謎です。とくに「ソースをいじってないのに、Headlessモードを外しただけで正常に取得できる」ってのがホントに解せないです。検索すると、そこそこ上記のような現象はあるようなのですが、レアケースっぽいですね。
とりあえず自動化しなくちゃいけないケースでは、Firefoxを利用しようと思います。よろしく、Firefox。