1个不稳定版本
使用旧的Rust 2015
0.7.2 | 2017年10月14日 |
---|
#405 in HTTP客户端
78KB
1K SLoC
fantoccini-stable
通过WebDriver以编程方式与网页交互的高级API。
这是一个原版fantoccini的临时分支,将所有的implFuture
替换为Box<Future<>>
,以便在Rust稳定版中编译。一旦conservative_impl_trait
进入稳定版,我将停止维护/更新此分支。
此库使用WebDriver协议通过相对高级的操作来驱动符合标准(可能是无头)的浏览器,例如“点击此元素”、“提交此表单”等。目前仅限nightly版本,但一旦conservative_impl_trait
进入稳定版,这将改变。
大多数交互是通过使用CSS选择器来驱动的。由于大多数WebDriver兼容的浏览器都比较新,CSS标准的高级表达形式也得到支持,提供了相当强大的操作符。
首先调用Client::form
来管理表单,然后使用Form
上的方法来操作表单的字段,最后提交表单。
对于对页面的低级访问,可以使用Client::source
获取完整的页面HTML源代码,以及使用Client::raw_client_for
为特定URL构建原始HTTP请求。
示例
以下示例都假设您有一个运行在4444端口的WebDriver兼容进程。快速获取一个进程的方法是在命令行中运行geckodriver
。该代码还对chromedriver
和ghostdriver
使用的旧版WebDriver协议提供了部分支持。
示例将大量使用unwrap
—— 您可能不应该在代码中这样做,而是应该处理发生时的错误。这尤其适用于您预期可能失败的函数,例如通过CSS选择器的查找。
让我们从在维基百科上点击开始吧
let mut core = tokio_core::reactor::Core::new().unwrap();
let (c, fin) = Client::new("https://127.0.0.1:4444", &core.handle());
let c = core.run(c).unwrap();
{
// we want to have a reference to c so we can use it in the and_thens below
let c = &c;
// now let's set up the sequence of steps we want the browser to take
// first, go to the Wikipedia page for Foobar
let f = c.goto("https://en.wikipedia.org/wiki/Foobar")
.and_then(move |_| c.current_url())
.and_then(move |url| {
assert_eq!(url.as_ref(), "https://en.wikipedia.org/wiki/Foobar");
// click "Foo (disambiguation)"
c.by_selector(".mw-disambig")
})
.and_then(|e| e.click())
.and_then(move |_| {
// click "Foo Lake"
c.by_link_text("Foo Lake")
})
.and_then(|e| e.click())
.and_then(move |_| c.current_url())
.and_then(|url| {
assert_eq!(url.as_ref(), "https://en.wikipedia.org/wiki/Foo_Lake");
Ok(())
});
// and set the browser off to do those things
core.run(f).unwrap();
}
// drop the client to delete the browser session
drop(c);
// and wait for cleanup to finish
core.run(fin).unwrap();
我们最初是如何到达Foobar页面的?我们进行了一次搜索!让我们让程序为我们做这件事
// -- snip wrapper code --
// go to the Wikipedia frontpage this time
c.goto("https://www.wikipedia.org/")
.and_then(move |_| {
// find the search form
c.form("#search-form")
})
.and_then(|f| {
// fill it out
f.set_by_name("search", "foobar")
})
.and_then(|f| {
// and submit it
f.submit()
})
// we should now have ended up in the rigth place
.and_then(move |_| c.current_url())
.and_then(|url| {
assert_eq!(url.as_ref(), "https://en.wikipedia.org/wiki/Foobar");
Ok(())
})
// -- snip wrapper code --
如果我们想下载原始文件怎么办?Fantoccini能为您解决
// -- snip wrapper code --
// go back to the frontpage
c.goto("https://www.wikipedia.org/")
.and_then(move |_| {
// find the source for the Wikipedia globe
c.by_selector("img.central-featured-logo")
})
.and_then(|img| {
img.attr("src")
.map(|src| src.expect("image should have a src"))
})
.and_then(move |src| {
// now build a raw HTTP client request (which also has all current cookies)
c.raw_client_for(fantoccini::Method::Get, &src)
})
.and_then(|raw| {
use futures::Stream;
// we then read out the image bytes
raw.body().map_err(fantoccini::error::CmdError::from).fold(
Vec::new(),
|mut pixels, chunk| {
pixels.extend(&*chunk);
futures::future::ok::<Vec<u8>, fantoccini::error::CmdError>(pixels)
},
)
})
.and_then(|pixels| {
// and voilla, we now have the bytes for the Wikipedia logo!
assert!(pixels.len() > 0);
println!("Wikipedia logo is {}b", pixels.len());
Ok(())
})
// -- snip wrapper code --
依赖关系
约15-25MB
约395K SLoC