Appearance
获取网页内容
获取网页内容是一个非常常见的需求,这里我们使用简单的案例演示如何使用 MB 获取网页内容
概述
获取网页内容一般有两种思路,一是使用接口 mbGetSource
获取网页的 HTML 字符串,再用 C++ 对这个 HTML 进行解析和处理,二是使用 JS 获取页面内容,再传回 C++。
除非需求特别简单,建议使用第二种,原因如下:
- JS 拥有丰富的 DOM 接口可以对页面进行操作,而 C++ 等需要引入 HTML 解析库或手写字符串解析进行获取,另外无法在 HTML 中直接体现的属性则无力获取,更不用说后续可能还要操作页面。
- 对于异步或者一些单页面网页,通过接口很难判断需要的内容是否加载完成。而 JS 却有多种手段,如循环检查,注册事件,甚至可以重写内置方法实现 hook。
使用第二种方式,首先需要在页面上执行自己的 JS,然后在 JS 获得数据之后与 C++ 通信。
如何在页面上执行自己的 JS
在之前的快速开始中,我们使用了 mbRunJs(webView, frameId, "alert(1);", true, nullptr, nullptr, nullptr);
直接执行 JS 代码片段 alert(1);
,但是直接使用 JS 字符串比较麻烦,比如需要处理一些转义。
假如我们有一个 JS 文件,想引入到一个页面,如何操作呢?利用一个简单的 JS 技巧就可以完成:
cpp
mbRunJs(webView, frameId, "var script = document.createElement('script');script.src = 'file:///C:/xx_dir/test.js';document.getElementsByTagName('body')[0].appendChild(script);", true, nullptr, nullptr, nullptr);
里面的 JS 代码格式化之后就是:
javascript
var script = document.createElement('script');
script.src = 'file:///C:/xx_dir/test.js';
document.getElementsByTagName('body')[0].appendChild(script);
其中 src
可以修改为一个合法的本地路径,网络路径也可。
当然执行自己的 JS 还有其它办法,如修改网络请求等,此处不再赘述。
JS 获得数据之后如何与 C++ 通信
mbRunJs
执行代码片段获得 JS 数据
首先,mbRunJs
可以配置参数 mbRunJsCallback
设置回调,案例如下:
调用 mbRunJs
:
cpp
mbRunJs(webView, frameId, "return 999 + 1;", true, onRunJsCallback, nullptr, nullptr);
onRunJsCallback 如下:
cpp
void MB_CALL_TYPE onRunJsCallback(mbWebView webView, void* param, mbJsExecState es, mbJsValue v)
{
double value = mbJsToDouble(es, v);
}
此时 value 值为 1000.0,注意需要调用 mbJsToDouble
、mbJsToBoolean
,mbJsToString
等函数从 JS 获取值,如果不确定是什么类型,可以使用 mbGetJsValueType。
JS 在任意时刻与 C++ 通信
对于异步或者一些单页面网页,执行完 JS 片段不能立即返回所需结果,或者我们使用了插入 script
标签的办法执行 JS,这要求 JS 能在任意时刻与 C++ 通信。
使用 mbOnJsQuery
注册 JS 通知的回调
cpp
mbOnJsQuery(mbView, onJsQueryCallback, nullptr);
onJsQueryCallback
为:
cpp
void MB_CALL_TYPE onJsQueryCallback(mbWebView webView, void* param, mbJsExecState es, int64_t queryId, int customMsg, const utf8* request)
{
constexpr int postForSomething = 0;
if (customMsg == postForSomething)
{
std::string requestStr(request);
// do someting
mbResponseQuery(webView, queryId, postForSomething, "responseData");
}
}
利用上一节的办法加载一个 JS 文件进入网页,内容为:
javascript
var POST_FOR_SOMETHING = 0;
setTimeout(function() {
window.mbQuery(POST_FOR_SOMETHING, "requestData", function(customMsg, response) {
// do something
});
}, 2000);
这里模拟了一个异步操作,2秒之后 JS 使用 window.mbQuery
调用 C++ 。注意,window.mbQuery
为 MB 注入进页面的函数,直接调用即可。
此时 onJsQueryCallback
接收到参数,其中 customMsg 为 0,request 为字符串 "requestData"。如果要传结构体,可以使用 JSON,两边各自解析处理即可。
类似的,mbResponseQuery
也可以向 JS 的回调返回内容。
如果想深入了解如何实现 C++ 与 JS 绑定,可以参考 Javascript 和 C++ 绑定如何实现(WIP)
获取页面内容
了解前面两节的内容之后,如何获取页面内容的问题实际变成了如何通过 JS 获取页面内容的问题,深入讨论 JS 超出了本文的范围,这里只举一个简单案例。
举例:获取 MB 接口文档 中所有的接口函数名。
javascript
// 简单案例演示,不考虑错误路径
function collectionToArr(collection) {
return [].slice.call(collection);
}
// "void mbInit(const mbSettings* settings)" => "mbInit"
function getFunctionName(str) {
var match = str.match(/(mb[A-Za-z0-9]*)\(/);
return match[1] ? match[1] : "";
}
function calcMBFunctionNameArr() {
var main = document.getElementById("content");
var h4Arr = main.getElementsByTagName("h4");
return collectionToArr(h4Arr).map(function(ele){return getFunctionName(ele.innerText)});
}
var nameArr = calcMBFunctionNameArr();
var requestData = JSON.stringify({
code: 0,
data: nameArr
});
// post requestData to C++
现在 requestData 是 JSON 字符串,可以通过上一节介绍的方法传入 C++ 中。
如果不太擅长使用 JS 语法,建议查阅 MDN。