1. 前情提要
上一次,我写了一篇:# 用JavaScript写一个自动刷新网页通知软考成绩是否出来的脚本 用来通过原生JS,粗暴的定时刷新网页,看看软考成绩是否出来了
后面热心网友 @iCodingShow 热情的试图把这个代码使用在自动刷新判断北京马拉松报名窗口是否开放上,然后失败了:
不得不说,我也很沮丧,这怎么就不行了呢????
然后我就决定去一探究竟~~~
2. 查bug: 跨域问题
当我尝试把前一篇文章中的代码改改丢进这个网页的console的时候,就会报错:
根据网上的资料,一般情况是因为部分网站对于跨域请求有限制。
也就是说,当我们试图使用<iframe>这些去嵌套目标网站的时候,写在父级(也就是iframe这一级别)的方法,是没有办法直接操作子级(也就是目标网页)的内容的。
这个时候如果需要解决父子传参的问题,可以参考这篇博文:# iframe 内嵌报跨域SecurityError: Blocked a frame with origin “http://x“from accessing a cross-origin frame
但是很尴尬的是,这个网页本身并不是需要父子传参,而是需要在父级抓取子级的dom元素。
3. 尝试debug
然后我又尝试逐步调试,看看能不能成功刷新获取dom,又发现了新的问题:
第一次可以正确读取内容,但是第二次刷新后就无法拿到frame中的内容了,全局对象从global变成了window:
这个问题比较尴尬,还没有解决,先把代码放在这里记录一下:
// 以下代码存在bug,尚未调试通过
// 设置:刷新间隔、考试时间string
timeout=prompt('页面刷新间隔(秒):'); // 每隔5s自动更新
compareText=prompt('考试时间', '2021贝壳北京马拉松')
// 初始化变量
count=0; // 已刷新次数
current=location.href; // 当前页面链接
// 判断是否开启了浏览器通知,若开启成功,则可以在自动刷新满足条件后正确发送通知
// 可以打开 chrome://settings/content/notifications 设置“允许发送通知”链接
if(window.Notification && Notification.permission !== "denied") {
if(timeout > 0) setTimeout('reload()', 1000 * timeout);
else location.replace(current);
}
// 自动重载页面
function reload() {
// 刷新页面,更新刷新次数
setTimeout('reload()', 1000 * timeout);
count++;
console.log(`每${timeout}秒自动刷新,刷新次数:${count}`);
// 创建frame,保证刷新的是frame内容,而不会在刷新整个页面后清除掉本页执行代码,停止刷新
fr4me='<frameset cols=\'*\'>\n<frame id src=\''+current+'\'/>';
fr4me+='</frameset>';
frameDOM = window.frames ? window.frames.document : null;
// 修改适配处:马拉松比赛的链接中,window.frames不是数组
// frameDOM = window.frames[0] ? window.frames[0].document : null;
console.log(window.protptype)
// // 调用方法,检查是否满足条件
checkInfo(frameDOM ? frameDOM : document)
// 刷新嵌套的frame
with(document) {
write(fr4me);
void(close());
}
}
// 检查是否满足条件:包含xx字段,满足时开启浏览器通知提示
// 可根据实际情况调整该部分判断内容
function checkInfo(doc) {
targetDomList = doc.getElementsByClassName('saas-select-list')
if (doc && targetDomList && targetDomList.length > 0) {
targetDom=targetDomList[0];
targetText = targetDom.innerText
console.log(`当前查询:${compareText}`);
var regTest = new RegExp(compareText, 'i')
if (targetText && regTest.test(targetText)) {
console.log(`查询成功:${targetText}`);
notify();
}
}
}
// 浏览器通知提示
function notify() {
Notification.requestPermission(function(status) {
var n = new Notification(`${compareText}`, { body: '页面已更新!!!快来看成绩' });
});
}
4. 换思路:通过自动重复请求接口来抓取数据
由于一般来说,这种页面中的下拉列表的数据都是通过修改数据库,然后API接口自动请求渲染的。
所以,我打开控制台的network,去看一下有什么接口和这个list有关系:
由于这个接口本身就是get请求的,所以直接把这个接口复制到浏览器中尝试打开,是能够直接显示数据的👇
于是,我们依法炮制,根据之前的这篇文章# 用JavaScript写一个自动刷新网页通知软考成绩是否出来的脚本 来尝试写一个自动刷新这个接口请求页面的方法:
4.1 查看该页面元素
非常简单粗暴,可以看到显示内容就是在<pre>里面的:
4.2 修改一个适配升级版代码
修改要素:
-
checkInfo()中需要抓取的元素内容 -
获取元素内容的string,转换为JSON对象方便使用
-
使用正则表达式/通配符,来模糊匹配目标内容(比如检测内容输入:“2021”,即可和“2021贝壳马拉松”匹配上,从而发出正确通知)
备注:在checkInfo()中,甚至可以考虑添加一个路由跳转,在查询成功后,自动为你打开报名页面
// 设置:刷新间隔、考试时间string
timeout=prompt('页面刷新间隔(秒):'); // 每隔5s自动更新
compareText=prompt('考试时间', '2021')
// 初始化变量
count=0; // 已刷新次数
current=location.href; // 当前页面链接
// 判断是否开启了浏览器通知,若开启成功,则可以在自动刷新满足条件后正确发送通知
// 可以打开 chrome://settings/content/notifications 设置“允许发送通知”链接
if(window.Notification && Notification.permission !== "denied") {
if(timeout > 0) setTimeout('reload()', 1000 * timeout);
else location.replace(current);
}
// 自动重载页面
function reload() {
// 刷新页面,更新刷新次数
setTimeout('reload()', 1000 * timeout);
count++;
console.log(`每${timeout}秒自动刷新,刷新次数:${count}`);
// 创建frame,保证刷新的是frame内容,而不会在刷新整个页面后清除掉本页执行代码,停止刷新
fr4me='<frameset cols=\'*\'>\n<frame id src=\''+current+'\'/>';
fr4me+='</frameset>';
frameDOM = window.frames[0] ? window.frames[0].document : null;
// 调用方法,检查是否满足条件
checkInfo(frameDOM ? frameDOM : document)
// 刷新嵌套的frame
with(document) {
write(fr4me);
void(close());
}
}
// 检查是否满足条件:包含xx字段,满足时开启浏览器通知提示
// 可根据实际情况调整该部分判断内容
function checkInfo(doc) {
if (doc && doc.getElementsByTagName('li')) {
targetDom=doc.getElementsByTagName('pre');
console.log(`当前查询:${compareText}`);
// 获取接口返回参数并转为JSON格式显示
var res = targetDom && targetDom[0] ? targetDom[0].innerText : ''
var JSONres = JSON.parse(res)
// 匹配判断
if(JSONres.matchs && JSONres.matchs.length > 0) {
targetText = JSONres.matchs[0].match.name
var regTest = new RegExp(compareText, 'i')
if (targetText && regTest.test(targetText)) {
console.log(`查询成功:${targetText}`);
notify();
}
}
}
}
// 浏览器通知提示
function notify() {
Notification.requestPermission(function(status) {
var n = new Notification(`${compareText}`, { body: '页面已更新!!!快来看' });
});
}