Selenium WebDriverでWebページを印刷する方法
C#で書きますが、適宜自分の言語で読み替えてください。
やりたいこと
Webページ操作のエビデンス、つまりスクショがいる。だるいのでSeleniumで自動化したいが、pngではなくxpsが必要なので、Selenium スクショ機能が使えない。
テスト準備
C#ならNuGetでSelenium.WebDriver
とSelenium.Support
を入れ、Downloads - ChromeDriver - WebDriver for Chromeからchromedriver.exe
をダウンロード、プロジェクトに追加し、プロパティでビルドするときに出力フォルダにコピーするようにする。
以下、テストするまでのコード。PrintPage()
で印刷をかけることとする。
[TestClass] public class PrintTest { [TestMethod] public void TestSearch() { var chromeOptions = new ChromeOptions(); var chromeDriverService = ChromeDriverService.CreateDefaultService(); IWebDriver driver = new ChromeDriver(chromeDriverService, chromeOptions); driver.Navigate().GoToUrl("http://www.google.com"); IWebElement element = driver.FindElement(By.Name("q")); element.SendKeys("Hello selenium WebDriver"); element.Submit(); PrintPage(driver); } }
SendKeys
ctrl
+ P
を押して……ってやる方法。あまりやりたくない。印刷ドライバ選択も面倒そう。
private static void PrintPage(IWebDriver driver) { var keys = new List<string> { "^(p)", "{Tab}", "{Tab}", "{Enter}"}; // 実際はドライバ選択操作も入れる。 keys.ForEach(s => { SendKeys.SendWait(s); Thread.Sleep(500); // これ入れないと操作がPCの処理を追い越してしまう。 }); }
XPath(できない)
XPathを使う方法。実はchromeの印刷画面はDriverのWindowHandles
の末尾に入るらしい。このHandleを取得して操作する。XPathは要素を右クリックし、検証、ソースの選択行を右クリック、Copy,
Copy XPathでクリップボードにとれる。
private void PrintPage(IWebDriver driver, int loopSpan = 500) { var handles = driver.WindowHandles; // stringでwindowの識別子みたいなのが入る var oldLastTab = driver.WindowHandles.Last(); var executor = (IJavaScriptExecutor)driver; // 印刷windowを開く executor.ExecuteScript("setTimeout(function(){window.print();}, 0);"); // 新しいhandleを取得するだけ while(true) { Thread.Sleep(loopSpan); handles = driver.WindowHandles; var lastTab = driver.WindowHandles.Last(); if (lastTab != oldLastTab) { driver.SwitchTo().Window(lastTab); break; } } // 実際はここでエラーが吐かれる driver.FindElement(By.XPath("//*[@id=\"button-strip\"]/paper-button[1]")); driver.SwitchTo().Window(oldLastTab); }
上記で実行すると、「XPathがみつからない」というエラーが出て実行されない。なぜかというと、chromeの印刷画面ではShadow DOMなるものが使われており、配下の要素はXPathで取得できないらしい。
実際ソースコードを見ると、確かにそれっぽいところが見つかる。
JS Pathをつかう
ではどうするかというと、JSPathなるものを使ってJava Script上で要素を選択しJava Script上でクリックする。chrome上ではCopy XPathのかわりにCopy JS Pathを選択することで、Java Scriptでの要素がコピーできる。
// driver.FindElement(By.XPath("//*[@id=\"button-strip\"]/paper-button[1]")); var jsPath = "document.querySelector('body > print-preview-app')" + ".shadowRoot.querySelector('#sidebar > print-preview-header')" + ".shadowRoot.querySelector('#button-strip > paper-button.action-button').click();" executor.ExecuteScript(jsPath);
これならドライバ選択もJava Scriptでできるので安定しそう。めでたしめでたし。