Selenium/Appium Advent Calendar 2019の19日目の記事です。
seleniumを利用してしばらく立つと、ある問題に直面します。
chromeのメジャーバージョンアップされると、chromeWebDriverが不一致でエラーを吐く・・・
以前は一度更新すれば3versionくらいは利用できたそうですが、
最近ではメジャーバージョンがあがるとアップデートが必要です。
楽をしたくてseleniumを使うのに面倒をみるのは大変なので、自動更新をしてみます。
つまり、seleniumという便利なクローラーを使うために、seleniumを使わずにクローリングします。
(seleniumの記事なのにseleniumを使わない・・・!?)
コードはPythonで記載をしますが、他言語でもかけるように解説をしっかり入れていきます。
以下のインポートをしている前提で話を進めていきます。
import os import re import zipfile import urllib.request from lxml import html
※lxmlはpipで取得しておく必要があります。
■処理の流れ
1. ローカルのChromeのバージョンを確認します。
2. バージョンが異なる場合、適切なドライバを調べます。
3. ファイルをダウンロード・解凍し、フォルダに配置します。
4. 適切なドライバでseleniumを起動!
■サンプルコード
GitHub-getChromeWebDriver
※上記コードはMIT LICENCEです。
※この記事のコピペや参考はフリーです。
【1】ローカルのChromeのバージョンを確認します。
Chromeはコマンドから直接バージョンを取得する方法はありません。
なので、Applicationが配置されている場所に、バージョン名のついたフォルダがあることを利用し、
現在のバージョンを取得してみます。
また、Chromeはバージョンアップの際、次のバージョンのディレクトリを作成し、
次回起動時に変更+旧ディレクトリの削除を行うため、ディレクトリが複数ある場合があります。
なので、ディレクトリの中でもっとも新しいバージョンを確認します。
# インストールしている場所を変更していなければディレクトリはここに作成されている。 files = os.listdir(r"C:\Program Files (x86)\Google\Chrome\Application") # ChromeApplicationフォルダ配\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\下にあるフォルダをフルパスで取得 filesDir = [f for f in files if os.path.isdir(os.path.join(r"C:\Program Files (x86)\Google\Chrome\Application", f))] # 最大値を取るための変数を用意 version = 0 for folder in filesDir : # 正規表現でメジャーバージョンを取得 filePattern = re.compile(r'([0-9]+(?=\.))') result = filePattern.match(folder) # 取得できていたら、最大値を保存 if result: version = max(version,int(result.group()))
これで次に起動する際のメジャーバージョンを取得できました。
【2】バージョンが異なる場合、適切なドライバを調べます。
現在のドライバのバージョンを設定ファイルなどで保持しておき、現在のドライバのバージョンと
Chromeのバージョンが不一致の場合、ドライバのダウンロードを行います。
Chromeドライバの最新版は下記からダウンロードできます。
https://chromedriver.chromium.org/downloads
ただし、毎回同じバージョン毎にパスが変わるので、直接ファイルを取得します。
気をつける必要があるのは、ここのDriverはローカルよりも新しいバージョン用のものが置かれていることがあり、
自分にあったドライバを選ぶ必要があります。
※おそらくディベロッパー向けや地域による差だと思います。
ファイルを取得後、Xpathで対象リンクを洗い出します。
※もっとモダンなやり方があればおしえてください。
# ChromeDriver配信ページを取得 url = 'https://chromedriver.chromium.org/downloads' with urllib.request.urlopen(url) as f: htmltext = f.read().decode('utf-8') # XPATHで対象となる項目を取得 title = html.fromstring(htmltext).xpath("//table[@class='sites-layout-name-one-column sites-layout-hbox']/tbody/tr/td/div/div/ul/li") chromeDriverVersionList = title[0].xpath("li") mi = "0" for versionValue in chromeDriverVersionList : # リンクを取得 versionValueLinklList = versionValue.xpath("a") # 要素が空の場合、スキップ if(len(versionValueLinklList)==0): continue chromeDriverStatus = versionValueLinklList[0].text # メジャーバージョンを取得 m = re.search('(?<=ChromeDriver )[0-9]*', chromeDriverStatus) # ローカルのバージョンと一致するか確認、一致したら結果を保存 if(str(m.group()) == str(versionValue)) : versionOfMajor = re.search('(?<=ChromeDriver ).*', chromeDriverStatus) Driverversion = str(versionOfMajor.group())
【3】ファイルをダウンロード・解凍し、フォルダに配置します。
Windows用のDriverはZIPで圧縮されているため、ダウンロード後、解凍して配置します。
# version情報からURLを決め打ちでダウンロード furl = "https://chromedriver.storage.googleapis.com/" + Driverversion + "/chromedriver_win32.zip" urllib.request.urlretrieve(furl, "temp.zip") with zipfile.ZipFile('temp.zip') as zipF: zipF.extractall("ChromeDriver配置先") # 任意の設定ファイルなどにDriverのversionを記録(毎回取得しないようにするため)
これをseleniumを利用したアプリケーション起動前に実行することで、バージョン違いの問題はクリアされます。
【4】本命のSeleniumアプリを起動
以下で色々書き加えたソースコード公開しています。
GitHub-getChromeWebDriver
【2】「for version in chromeDriverVersionList :」の「version」と【1】の「version」変数名を被ってしまうので、このまま【1】と【2】をつないで実行するとうまくいきませんでした。【2】「for version in chromeDriverVersionList :」の「version」は別の変数名にしたほうがいいです。
あー確かにそうですね。
自身で実装したときはそれぞれ関数に分けちゃってましたけど、
記事で切り出した部分だとそうなりますね。
ありがとうございます。こそっと直しておきます。