aardio 可以嵌入一大堆的浏览器组件,而且代码都非常简洁,不过今天我们要说的不是这些功能。
我们知道,在其他编程语言里比较流行的是使用 WebDriver 协议控制浏览器,这个非常方便,但实际用起来要做的事还不少,例如 WebDriver 控制 Chrome 你要安装 ChromeDriver,用 WebDriver 控制 Edge,你要安装 EdgeDriver ,而且每个不同版本的浏览器,要安装的对应的 Driver*ex.e** 都 是不同的,这就真是个大坑了。
试想一下,别人电脑上有没有装 Chrome 还不一定,你得让用户自己装这个,还要自己去爬楼找到对应的 Driver*ex.e** ,这是有多累,即使你基于这些写了个软件工具,大概率也只能自己玩玩了。
不过 aardio 里所有这些事都可以自动完成,aardio 的标准库里有个 chrome.driver,虽然他叫 chrome.driver,但是兼容非常多的浏览器,也兼容现在越来越普及的 Edge。使用 aardio里的 chrome.driver, 你只需要简单 地写自动化控制网页的代码就可以了,所有环境配置都由 aardio 自动处理,aardio 会自动检测系统的 Chrome, Edge浏览器,如果没有就自动安装合适的浏览器,而且自动*载下**对应版本的 ChromeDriver*ex.e**, EdgeDriver*ex.e**,这样你编写的软件工具也可以方便地分发给普通用户。
实际上我写的库 chrome.driver 主要的代码都是在处理 ChromeDriver,EdgeDriver 的安装部署与配置,至于 WebDriver 协议其实没有几句代码,aardio 先天就可以将任何基于 Web API 的接口自动导入为 aardio 对象,在这方面 aardio 就省事多了。
来看几句 aardio 调用代码体会一下丝滑与轻松的感觉:
import chrome.driver;
var driver = chrome.driver();
var browser = driver.startBrowser();
//打开网页
browser.go("http://www.baidu.com")
//查找文本输入框,返回的DOM对象也可以使用ququerySelector继续查找子节点
var ele = browser.querySelector("body").querySelector("#kw");
//在网页输入框输入内容
ele.setValue( "ChromeDriver" )
//调用JS,并且可以返回值(也可以返回DOM节点对象)
var searchButton = browser.doScript(`
//可以使用arguments访问aardio传来的参数
return arguments[0].querySelector("#su");
`
,browser.querySelector("body")//可以传任意个调用参数给JS,还可以直接传DOM节点对象
)
//JS返回的DOM节点对象也可以操作控制
searchButton.click();
有老的 aardio 用户看到这里可能就要说了,我 aardio 都用很久了,这些代码你天天吹了不知道多少遍了。好吧,其实今天这篇文章不是要说上面这些。之所以说这些,是考虑还有不少刚接触 aardio 的新手,对 aardio 的节奏和风格先有一个大致的概念。
前面已经说过了,aardio 的风格一贯都是这样,简单,简单 …… 还要更简单,人生苦短,我们没有那么多时间去*载下**、配置,看文档。能用程序做的事就用程序自动去做。
今天我要说一个新的东西,叫 SeleniumBasic, 一个 VB 用户向我介绍了这个开源组件,Selenium 大家应该知道了,WebDriver 就是 Selenium 使用的协议。而 SeleniumBasic 是基于 Selenium 的 .Net 版本封装的一个 VB 开源组件,用的人应该也不少。然后 SeleniumBasic v3 的作者 ryueifu 提到SeleniumBasic 需要注册 COM 控件,在很多电脑上存在兼容性问题,出现“无法创建对象”,“自动化错误”,所以进行了改进 ...... 具体的我在范例附上了文章链接大家可以自行了解。
我对这个开源组件的注册坑很感兴趣,aardio 中加载 COM 控件通常可以免注册加载,甚至是自内存加载,这不但是省事,不容易出错,对系统的污染也小,而且不同的程序即使用同一个组件也不会出现版本冲突(尤其是这些开源组件,万一有人修改了接口,却用了同一个 ProgID 注册呢?)
我把 SeleniumBasic 下过来看了一下,发现这是一个 .Net 写的组件,一堆 DLL,然后有一个注册的批处理,其实 .Net 的 DLL 不是那种原生 DLL,某种意义上更像是 aardio 中的库模块(把后缀名改为 dll ), 所以他这个注册控件相比原生 DLL 可能有更多的兼容问题,实际上我们也很少看到用 .Net 写的 COM 组件。
这个 SeleniumBasic 组件既然是 .Net 写的,我们其实也就没必要注册了,因为 .Net 的程序集本来就可以直接调用。我写了几句代码试了一下,可以使用,但是这个 DLL 又依赖 3 个其他的 DLL,这个就不太好了。你说写几句代码就拖 4 个 DLL,就算不用注册了,总是不太完美 。
于是我决定把这 4 个 DLL 合并为一个 DLL文件,我们可以用微软提供的 ILMerge 来做这件事,但是这个工具要码一堆命令行,这个就略有些麻烦了,于是顺手就写了个 aardio 扩展库 dotNet.merge 简化了合并程序集操作,然后又顺手写了个 GUI 工具:

用这个工具成功把 SeleniumBasic 合并为一个DLL,这个 DLL 不支持内存加载,不过没关系,用 aardio 提供的 dotNet.loadAppData() 一句代码解决 —— 支持生成独立 EXE 文件。
虽然我们做了一系列的简化,但还不够简单, SeleniumBasic 还是需要自己写一堆初始化的配置参数,需要你自己*载下** Chrome,自己去找对应版本的 ChromeDriver*ex.e** …… 于是我继承 aardio 的优良传统,封装了一个扩展库:dotNet.seleniumBasic ,把前面所有这些事同样全自动化的做了。
然后我高兴地发布了扩展库,正准备上公众号写文章向大家介绍介绍,在写以前,随手试了一下 …… 发现在我刚换的这台笔记本上,居然运行不了,原因是他的 ChromeDriver 不支持 Edge Chromium, 亲!你难道不知道 Edge 是正宗的 Chromium 核吗?!
要知道 chrome.driver 是兼容 Edge的,于是我对 dotNet.seleniumBasic 一通改,加了个懒人包函数 SeleniumBasic.CreateDefaultWebDriver() 自动做好所有的事,并且自动安装、切换、兼容 ChromeDriver 与 EdgeDriver。
好吧,最后我们看一下 aardio 调用 SeleniumBasic 到底有多简单:
import console;
console.showLoading(" 正在启动 SeleniumBasic ");
import dotNet.seleniumBasic;
var SeleniumBasic = dotNet.seleniumBasic;
var wd = SeleniumBasic.CreateDefaultWebDriver();
if(!wd) error("初始化或配置环境失败",2)
//打开网站
wd.Url = "https://www.baidu.com"
//查找表单
var form = wd.FindElementById("form")
//查找输入框
var keyword = form.FindElementById("kw")
//清空输入框,aardio 不能像 VB 那样省略函数调用后的括号
keyword.Clear();
//发送字符串
keyword.SendKeys("aardio");
//自动点击按钮
var button = form.FindElementById("su");
button.Click();
//重开控制台以前置控制台窗口,然后等待按键
console.close();
console.pause(,"按任意键退出浏览器");
//退出浏览器
wd.Quit();
是不是很简单?!
这个库发布一段时间以后,已经改进为使用 dotNet.reference 函数完美内存加载 SeleniumBasic 的这几个 DLL 了( 比 ILMerge 简单 ),相关源码如下:
dotNet.reference({
["WebDriver.Support"] = #34;~\lib\dotNet\seleniumBasic\.res\WebDriver.Support.dll";
["WebDriver"] = #34;~\lib\dotNet\seleniumBasic\.res\WebDriver.dll";
["SeleniumBasic"] = #34;~\lib\dotNet\seleniumBasic\.res\SeleniumBasic.dll";
});
aardio 代码在文件路径前加上 $ 符号 —— 表示将这个文件的二进制数据直接嵌入到程序里(发布后就不再需要这个文件了)。
dotNet.reference 函数用于在 .Net 中虚拟内存程序集, 原来的 .Net 代码一般不需要修改, 除非程序集内有获取程序集类径的代码。例如 SeleniumBasic.IWebDriver 构造函数内的
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
需要改为
System.AppDomain.CurrentDomain.BaseDirectory。