iKnow をスクレイピングしようとしたら https://iknow.jp/login が派手に JavaScript を使っていた。
仕方がないので Ghost Driver 経由で PhantomJS を呼ぼうと思ったら、最近 Ghost Driver はあまりメンテナンスされていないらしく
If you need a better maintained WebDriver implementation, and write your code in Java, why not checkout Machine Publishers’ jBrowserDriver? Tell Dan Hollingsworth I sent you.
代わりに、jBrowserDriver がすすめられていた。jBrowserDriver は
A programmable, embedded web browser driver compatible with the Selenium WebDriver spec – fast, headless, WebKit-based, 100% pure Java, and no browser dependencies
をうたうライブラリで、JavaFX の javafx.scene.web.WebView を使っている。
val driver = new JBrowserDriver(Settings.builder.timezone(Timezone.AMERICA_NEWYORK).build)
driver.get("https://iknow.jp/login")
driver.findElement(By.name("user[email]")).sendKeys("alice@example.com")
driver.findElement(By.name("user[password]")).sendKeys("pa55w0rd")
driver.findElement(By.tagName("form")).submit()
こんな感じに使える。
実装
実装を少し見てみると、JBrowserDriver を new するたびに、java を子プロセスとして起動して
public JBrowserDriver(final Settings settings) {
...
sessionId = new SessionId(launchProcess(settings, configuredPortGroup.get()));
if (actualPortGroup.get() == null) {
Util.handleException(new IllegalStateException("Could not launch browser."));
}
JBrowserDriverRemote instanceTmp = null;
try {
synchronized (lock) {
instanceTmp = (JBrowserDriverRemote) LocateRegistry
.getRegistry(settings.host(), (int) actualPortGroup.get().child,
new SocketFactory(settings.host(), actualPortGroup.get(), locks))
.lookup("JBrowserDriverRemote");
instanceTmp.setUp(settings);
}
} catch (Throwable t) {
Util.handleException(t);
}
remote = instanceTmp;
...
}
子プロセスとは RMI 経由で通信していた。
@Override
public String getPageSource() {
try {
synchronized (lock) {
return remote.getPageSource();
}
} catch (Throwable t) {
Util.handleException(t);
return null;
}
}
なにもインストールせずに Java から簡単に WebKit が呼べるというのはちょっと面白い。
Source: posts/2016/jbrowserdriver.md