Seleniumコトハジメ - PubMed検索とUCSC Genome browserへのCustom Trackのアップロードを自動化してみた
はじめに
Seleniumとは、Webアプリケーションの画面操作を自動化するためのツールです。主に、Webアプリケーション開発のUIテスト時に利用するものです。また、キー入力などの操作を自動化できることから、Webスクレイピングのツールとして活用される例もあるようです。
Seleniumは、Apatch 2.0ライセンスで公開されているオープンソースのツールです。
https://www.seleniumhq.org/about/license.jsp
Seleniumの内部で使われているWebDriverは、Webブラウザを外部から遠隔操作するためのインターフェースであり、W3Cによって標準化されています(厳密には、標準化された内容と乖離する部分あり?)。
https://www.w3.org/TR/webdriver/
Selenium2では、Selenium RCと呼ばれる古い実装が残っていましたが、2016年に公開されたSelenium3では、完全にWebDriverの実装に移行しました。古い記事では、Selenium RCでの操作方法が記載されており、これは現在では動作しない方法になるので注意が必要です。
本来はUIテスト用のツールですが、今回は研究室時代にめんどくさいと思っていた作業を自動化してみました。1つはPubMedによるAdvanced Search、もう1つはUCSC Genome BrowserにCustom trackをアップロードする作業です。繰り返し同じことを繰り返す作業に対して、こういった自動化の方法は役に立ちますね。
Selenium実行サンプル
PubMed検索の自動化
UCSC Genome BrowserへのCustom Trackのアップロード
実行環境・ツール
Mac OSX Sierra (v10.12.6)
- Selenium Client for Java (v3.10.0)
- ChromeDriver (v2.35)
- GeckoDriver (v0.19.0)
- SafariDriver + Safari (v11.0.3)
Windows10 (Build 16299)
- ChromeDriver (v2.35)
- Microsoft Edge (Build 16299)
- Internet Explorer 11 (v3.9.0; 32bit)
Seleniumの導入
Seleniumを動かすのに必要なものは以下の通りです。
- Selenium Client & WebDriver Language Bindings
- 各種ブラウザのWebDriver
今回はJavaでの例を示したいと思いますが、javaであれば、MavenやGradleを使って導入する方法もあります。ただ、今回はJARファイルをダウンロードして1からプロジェクトを作っていきます。
Selenium Client & WebDriver Language Bindings
Seleniumは、Java、C#、Ruby、Python、Javascript (Node)を公式でサポートしており、それぞれの言語についてSeleniumのAPIが使えるライブラリ(Selenium Client)を用意しています。
まず、以下のサイトからJavaのSelenium Clientのファイルをダウンロードします。
selenium-java-3.xx.xx.zip
というファイルをダウンロードできるはずです。
https://www.seleniumhq.org/download/
各種ブラウザのWebDriver
続いて、各種ブラウザのWebDriverをダウンロードします。私の使っているPCはMac OSXなので、とりあえずFireFox、Google Chrome、SafariのWebDriverをそれぞれダウンロードすることにします(自身のパソコンにそれぞれのブラウザがすでにインストール済みでであることを前提とします)。
余談ですが、WebDriverはSeleniumを開発しているグループが作成しているわけではなく、Webブラウザの開発元であるGoogleやMozilla、Apple、Microsoftなどがそれぞれ開発をしています。そのため、基本的に最新バージョンのWebブラウザに対応するWebDriverを、これらのITベンダーが提供してくれる状態にあります。こういった点も、Seleniumを使うメリットですね。
Mozilla GeckoDriver (FireFox) (Selenium3.5以降でのみ動作)
以下からダウンロードします。
https://github.com/mozilla/geckodriver/releases
自身のパソコンのOSに合ったWebDriverをダウンロードしてきます。
以下のコマンドでファイルを解凍します。
$ tar zxvf geckodriver-v0.19.1-macos.tar.gz
geckodriver
という名前のWebDriverの実行ファイルが解凍されます。
Google Chrome Driver (Google Chrome)
以下からダウンロードします。
https://sites.google.com/a/chromium.org/chromedriver/
WebDriverをインストールする際は、Google Chromeのバージョンに気をつけてください。最新のものだと、対応しているバージョンはv62-64に限定されます(もちろん、以前のバージョンのWebDriverについてもダウンロード可能です)。自分のパソコンにインストールされているGoogle Chromeのバージョンを確認し、古いバージョンであれば、そのバージョンをサポートしているWebDriverをダウンロードしてください。
自身のパソコンのOSに合ったWebDriverをダウンロードしてきます。
以下のコマンドでファイルを解凍します。
$ unzip chromedriver_mac64.zip
chromedriver
という名前のWebDriverの実行ファイルが解凍されます。
SafariDriver (Safari)
Mac OSX内にすでにインストール済みです。
/usr/bin/safaridriver
の場所に存在します。
Safariの設定を変更する必要があります。 まず、Safariの環境設定を開きます。
次に、「メニューバーに'開発'メニューを表示」にチェックをつけます。
すると、メニューバーに「開発」が追加されるので、そこから「リモートオートメーションを許可」をクリックします。これで、ブラウザ側の設定は完了です。
リモートオートメーションを許可していない場合、Javaのテストコードを実行すると、以下のようなエラーが出力されます。このエラーが出た場合は、上記の設定ができてないので、設定を変更してください。
org.openqa.selenium.SessionNotCreatedException: Could not create a session: You must enable the 'Allow Remote Automation' option in Safari's Develop menu to control Safari via WebDriver. (WARNING: The server did not provide any stacktrace information)
Microsoft Edge
以下からダウンロードします。
https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
自身のパソコンのOSのビルドに合ったWebDriverをダウンロードしてきます。
Internet Explorer 11
設定が複雑なので、必要でない限り使わないほうが良いと思います。
こちらに詳しい使用方法が記載されているので参照してください。 保護モードの設定が、「インターネット」「イントラネット」「信頼済みサイト」「制限付きサイト」の4つで統一されていることがかなり重要です(この設定が揃っていないとWebDriverが動作しません)。
seleniumでinternet-explorer11を動かす方法
http://bitwave.showcase-tv.com/seleniumでinternet-explorer11を動かす方法/
トラブルシューティング(一部)
画面は拡大せずに、100%でないと動作しません。以下のエラーが出た場合は、拡大率を100%にしましょう。
org.openqa.selenium.SessionNotCreatedException: Unexpected error launching Internet Explorer. Browser zoom level was set to 110%. It should be set to 100%
Seleniumを動かしてみる
以下では、Eclipseがインストール済みであることを前提として説明を行います。
素のEclipseをインストールするのではなく、以下のAll-in-Oneのパッケージ(Pleiades All in One)をインストールすることをお勧めします。
http://mergedoc.osdn.jp/
1. プロジェクトファイルの作成
Seleniumを動かすためのプロジェクトファイルを作成します。 [新規] -> [Javaプロジェクト]をクリックします。
適当なプロジェクト名をつけて、「次へ」をクリックします。
先ほどダウンロードしたSelenium Clientを解凍し、libs
フォルダのjarとclient-combined-3.10.0.jar
とclient-combined-3.10.0-sources.jar
をそれぞれプロジェクトファイルに取り込みます。
まず、「ライブラリ」タブを選択し、「外部JARの追加」をクリックします。
上述のSelenium ClientのJARファイル一式を追加します。 最後に、「完了」ボタンをクリックし、プロジェクトを生成します。
すると、参照ライブラリに先ほど取り込んだSelenium ClientのJARファイルが参照されて取り込まれていることが確認できると思います。
次に、WebDriverを保存するためのフォルダを用意します。 [新規] -> [フォルダー]をクリックします。
フォルダー名を「exe」として、「完了」ボタンをクリックします。
作成した「exe」フォルダ内に、先ほどダウンロードしたWebDriverをドラック&ドロップでインポートします。(ここでは、chromedriver.exe
をインポートした例を示した。)
Mac OSXの場合、実行権限を付与しておきます。
$ chmod 755 geckodriver
続いて、Seleniumを実行するためのテストクラスを作成していきます。 プロジェクトの「src」フォルダを右クリックし、[新規] -> [Junitテスト・ケース]をクリックします。
適当なパッケージ名とクラス名を記入し、setUp()
とtearDown()
にチェックを付けます。
JUnit4がビルドパスに入っていない場合、追加するかどうか聞かれるので、「OK」をクリックしてビルドパスに加えます。
2. PubMedとUCSC Genome Browserを操作するコードを書いてみる
ページオブジェクトパターン
下図にページオブジェクトパターンと呼ばれる、一般的なクラスの設計方法を示しました。この方法を取り入れることで、メニューやボタン、検索入力欄など、Webページへの操作を共通クラスにまとめることでメンテナンス性の高いコードを書くことができます。
ロケータの調べ方
selenium用のコードを書くために、まずロケータ(ボタンやメニューなどを操作するための目印)を調べる必要があります。例えば、FireFoxを使うと、要素(下の例だと"Advanced")を右クリックして「要素を調査」をクリックすることでロケータを調べることができます。
どのタグが使われているかなどの情報を調べることができます(以下の図だと、INPUTタグのID要素が"Submit"であることがわかります。これを目印にしてSeleniumでWebブラウザを操作していきます)。
コーディング
以下のような構成で、プログラムを用意しました。 pageパッケージにページオブジェクトクラスを、testパッケージにJUnitのテストケースクラスを配置するようにしました。
WebDriverの呼び出し
System.setProperty()
の2番目の引数は、ダウンロードしたWebDriverを指定してください。OSによって、拡張子が異なるので注意する必要があります。以下でいくつか例を示しました。
Google Chrome (Mac OSX)
System.setProperty("webdriver.chrome.driver", "exe/chromedriver"); WebDriver driver = new ChromeDriver();
Firefox (Mac OSX)
System.setProperty("webdriver.gecko.driver", "exe/geckodriver"); WebDriver driver = new FirefoxDriver();
Safari (Mac OSX)
WebDriver driver = new SafariDriver();
Microsoft Edge (Windows10)
System.setProperty("webdriver.edge.driver", "exe/MicrosoftWebDriver.exe"); WebDriver driver = new EdgeDriver();
Internet Explorer (Windows10)
System.setProperty("webdriver.ie.driver", "exe/IEDriverServer.exe"); WebDriver driver = new InternetExplorerDriver();
ソースコード例
ソースコードの内容について言及しません。 主に、「Selenium実践入門」と言う本と下記のWebサイトを参考にしました。
PubMedSearchTest.java
package test; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import page.PubMedAdvancedSearchPage; public class PubMedSearchTest { private WebDriver driver; @Before public void setUp() throws Exception { System.setProperty("webdriver.chrome.driver", "exe/chromedriver.exe"); driver = new ChromeDriver(); //System.setProperty("webdriver.edge.driver", "exe/MicrosoftWebDriver.exe"); //driver = new EdgeDriver(); //System.setProperty("webdriver.ie.driver", "exe/IEDriverServer.exe"); //driver = new InternetExplorerDriver(); driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS); } @After public void tearDown() throws Exception { //driver.quit(); } @Test public void pubmedAdvancedSearchTest() { // PubMed Top PubMedAdvancedSearchPage pmPage = new PubMedAdvancedSearchPage(driver); // Go to Advanced Search page pmPage.goToAdvancedSearch(); // Set keywords pmPage.setJournal("Nature communications"); pmPage.setJournal("Nature biotechnology"); pmPage.setSearchSelect(1, "OR"); pmPage.setJournal("Nature methods"); pmPage.setSearchSelect(2, "OR"); pmPage.setJournal("Nature genetics"); pmPage.setSearchSelect(3, "OR"); pmPage.setJournal("Molecular cell"); pmPage.setSearchSelect(4, "OR"); pmPage.setJournal("eLife"); pmPage.setSearchSelect(5, "OR"); pmPage.setJournal( "PLoS biology"); pmPage.setSearchSelect(6, "OR"); pmPage.setJournal("Genome research"); pmPage.setSearchSelect(7, "OR"); pmPage.setJournal("Genes & development"); pmPage.setSearchSelect(8, "OR"); pmPage.setJournal("Nature cell biology"); pmPage.setSearchSelect(9, "OR"); // Click Search button pmPage.clickSearchButton(); } }
UCSCGenomeBrowserTest.java
package test; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import page.UCSCGenomeBrowserUploadPage; public class UCSCGenomeBrowserTest { private WebDriver driver; @Before public void setUp() throws Exception { System.setProperty("webdriver.chrome.driver", "exe/chromedriver.exe"); driver = new ChromeDriver(); //System.setProperty("webdriver.edge.driver", "exe/MicrosoftWebDriver.exe"); //driver = new EdgeDriver(); //System.setProperty("webdriver.ie.driver", "exe/IEDriverServer.exe"); //driver = new InternetExplorerDriver(); } @After public void tearDown() throws Exception { } @Test public void test() { UCSCGenomeBrowserUploadPage gmPage = new UCSCGenomeBrowserUploadPage(driver); gmPage.goToCustomTrack(); gmPage.setGenomeRefSelect("hg19"); // Upload file gmPage.UploadFile("C://Users/imama/Desktop/test1.bed"); gmPage.submit(); // Back to custom track page gmPage.goBackToCustomTrack(); // Upload file gmPage.UploadFile("C://Users/imama/Desktop/test2.bed"); gmPage.submit(); // Back to custom track page gmPage.goBackToCustomTrack(); // Upload file gmPage.UploadFile("C://Users/imama/Desktop/test3.bed"); gmPage.submit(); } }
PubMedAdvancedSearchPage.java
package page; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.Select; import org.openqa.selenium.support.ui.WebDriverWait; public class PubMedAdvancedSearchPage { /* WebDriverクラスのインスタンス */ private WebDriver driver; // 明示的な待機 WebDriverWait wait; // キーワードカウンタ private Integer keywordCounter; // コンストラクタ public PubMedAdvancedSearchPage(WebDriver driver){ this.driver = driver; this.keywordCounter = 0; this.wait = new WebDriverWait(driver, 10); } // Advanced saerch画面へ遷移 public void goToAdvancedSearch() { driver.get("https://www.ncbi.nlm.nih.gov/pubmed/"); driver.findElement(By.linkText("Advanced")).click(); // Goto PubMed Advanced search String previousURL = driver.getCurrentUrl(); System.out.println(previousURL); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); ExpectedCondition<Boolean> e = new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return (d.getCurrentUrl() != previousURL); } }; wait.until(e); String currentURL = driver.getCurrentUrl(); System.out.println(currentURL); // ページタイトルが変更されるまで待ち wait.until(ExpectedConditions.titleContains("Advanced search")); } // Set journal public void setJournal(String journal) { // FieldにJournalを選択 Select select = new Select(driver.findElement(By.id("ff_" + keywordCounter))); select.selectByValue("Journal"); // Journal名を入力 WebElement searchBox = driver.findElement(By.id("fv_" + keywordCounter)); searchBox.sendKeys(journal); // カウントアップ keywordCounter += 1; } // Set search select public void setSearchSelect(Integer id, String value) { Select select = new Select(driver.findElement(By.id("fop_" + id))); select.selectByValue(value); } // Click Search button public void clickSearchButton() { driver.findElement(By.id("search")).click(); } }
UCSCGenomeUploadPage.java
package page; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.Select; import org.openqa.selenium.support.ui.WebDriverWait; public class UCSCGenomeBrowserUploadPage { /* WebDriverクラスのインスタンス */ private WebDriver driver; // 明示的な待機 WebDriverWait wait; // コンストラクタ public UCSCGenomeBrowserUploadPage(WebDriver driver){ this.driver = driver; this.wait = new WebDriverWait(driver, 10); driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS); } // Go to Custom track page public void goToCustomTrack() { driver.get("https://genome.ucsc.edu/"); driver.findElement(By.id("myData")).click(); wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("customTracksMenuLink"))); driver.findElement(By.id("customTracksMenuLink")).click(); } // Upload file public void UploadFile(String filePath) { WebElement upload = driver.findElement(By.name("hgt.customFile")); upload.sendKeys(filePath); } // Submit public void submit() { driver.findElement(By.id("Submit")).click(); } // Return Custom track page public void goBackToCustomTrack() { WebDriverWait wait2 = new WebDriverWait(driver, 10000); wait2.until(ExpectedConditions.visibilityOfElementLocated(By.id("addTracksButton"))); driver.findElement(By.id("addTracksButton")).click(); } public void setGenomeRefSelect(String genomeRef) { Select select = new Select(driver.findElement(By.id("db"))); select.selectByValue(genomeRef); } }
スクリーンショットの取り方について(トラブルシューティング)
余談ですが、Java8の場合、FileUtils(Java7以前)ではなく、FileHandler(Java8以降)なので間違いがないように。 https://github.com/SeleniumHQ/selenium/issues/4919
File tmpFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); try { FileHandler.copy(tmpFile, new File("/Users/imamachinaoto/Desktop/test.png")); } catch (IOException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); }
参考
[初心者向け] JavaでSeleniumを動かす
https://qiita.com/tsukakei/items/41bc7f3827407f8f37e8
selenium get current url after loading a page
https://stackoverflow.com/questions/16242340/selenium-get-current-url-after-loading-a-page
Selenium WebDriverのwaitを活用しよう
http://softwaretest.jp/labo/tech/labo-294/
Best Practices & Tips: Selenium File Upload
https://saucelabs.com/resources/articles/best-practices-tips-selenium-file-upload