【日期选择】影刀遇到腾讯系/微信视频号日期选择器:不点、不输入,直接改 Vue 内部状态
评论
收藏

【日期选择】影刀遇到腾讯系/微信视频号日期选择器:不点、不输入,直接改 Vue 内部状态

经验分享
p
python自动化
2026-09-11 13:01·浏览量:332
p
python自动化
影刀中级开发者
发布于 2026-08-30 19:51更新于 2026-09-11 13:01332浏览

网页自动化的另一种思路:不模拟点击输入,直接修改前端数据状态

看到官方「内容捕手第五期」活动,本来想着找个案例参加一下。

结果想了半天,居然有点想不起来自己还有什么解决不了的日期难题。(凡尔赛一下 😂)

以前做过不少自动化发布视频、图文内容的平台,基本都有定时发布功能。

那些平台的日期选择器其实都没什么特别大的难点,正常定位元素、点击、选择日期和时间,基本就解决了。

于是想着:

既然活动主题是日期选择器,那就找个真正有点难度的。

然后一番寻找之后,碰到了腾讯系相关网页,视频号、微信小店等场景。

这次的日期选择器就比较典型了:

  • 元素藏在 Shadow DOM 里面

  • 外面看到的是一个 input

  • 但是 input 是 readonly

  • 不能直接输入

  • 点击后还涉及复杂的日期、时间组件

  • 页面使用 Vue

  • 而且最终真正控制日期的,并不是这个 input 本身

一开始我的思路当然也很传统:

找到 input
↓
修改 input.value
↓
触发 input/change
↓
希望页面识别

结果:

没用。

然后开始一步一步排查。


第一关:Shadow DOM

页面结构大致是:

<wujie-app>
    #shadow-root
        <input type="text" readonly>
</wujie-app>

普通的:

document.querySelector(...)

根本找不到。

于是先进入 wujie-app 的 shadowRoot:

const input=[...document.querySelectorAll('wujie-app')].flatMap(x=>x.shadowRoot?[...x.shadowRoot.querySelectorAll('input.weui-desktop-form__input[placeholder="请选择发表时间"]')]:[])[0];

终于拿到了 input。

但新的问题来了。


第二关:readonly

这个 input:

<input type="text" readonly placeholder="请选择发表时间">

显然不是让你直接输入的。

于是尝试修改:

input.value='2026-09-10 12:15';

以及使用原生 setter:

const setter=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set;
setter.call(input,'2026-09-10 12:15');

再触发:

input
change
blur

依然没有真正解决。

这时候就开始怀疑:

这个 input 可能只是显示层,真正的数据根本不在这里。


第三关:顺着 DOM 往上找

继续查看这个 input 的父级:

INPUT
↓
SPAN
↓
DIV
↓
SPAN
↓
DT
↓
DL.weui-desktop-picker__date

这个:

.weui-desktop-picker__date

看起来就是日期选择器组件本体。

然后尝试:

input.closest('.weui-desktop-picker__date').__vue__

没想到:

直接拿到了 Vue 实例。

这一下思路就完全变了。

之前一直在研究:

怎么操作这个 input?

现在变成:

这个 input 背后的 Vue 组件到底保存了什么?


第四关:直接看 Vue 组件内部

拿到实例:

const picker=input.closest('.weui-desktop-picker__date').__vue__;

然后看它有哪些方法:

Object.keys(picker.$options.methods||{})

发现里面有:

handleDaySelect
handleTimeSelect
handleTimeChange
setTime
handleMonthSelect
handleYearSelect
……

再看几个关键状态:

picker.selectedDate
picker.currentDate
picker.inputValue
picker.pick
picker.time
picker.format

其中最关键的就是:

picker.pick

它实际上就是这个日期选择器内部使用的时间戳。


第五关:直接修改真正的数据

于是尝试:

picker.pick=new Date(2026,7,30,20,0,0).getTime();

成功了。

这时候突然发现:

根本不需要点日期选择器。

也不需要:

模拟鼠标
↓
点击日期
↓
点击时间
↓
点击确认

而是:

找到 Vue 实例
↓
找到真正的数据
↓
直接修改
↓
让 Vue 自己更新页面

但事情还没有结束

一开始以为:

一个时间戳

直接设置完整的:

2026-09-10 12:15

就完事了。

结果发现:

日期成功,时间不对。

继续扒组件内部代码,发现:

function(t,e){
    t.setHours(e.getHours()),
    t.setMinutes(e.getMinutes()),
    t.setSeconds(e.getSeconds())
}

原来这个组件的日期和时间是分开处理的。

所以最终才确定下来:

先设置日期
↓
等待 Vue 更新
↓
再调用 setTime()
↓
最后更新完整 pick

最终在浏览器控制台验证成功:

const input=[...document.querySelectorAll('wujie-app')].flatMap(x=>x.shadowRoot?[...x.shadowRoot.querySelectorAll('input.weui-desktop-form__input[placeholder="请选择发表时间"]')]:[])[0];
const picker=input.closest('.weui-desktop-picker__date').__vue__;
const dateTime='2026-09-10 12:15';
const [date,time]=dateTime.split(' ');
const [year,month,day]=date.split('-').map(Number);
const [hour,minute]=time.split(':').map(Number);
const dateObj=new Date(year,month-1,day);
const timeObj=new Date(1970,0,1,hour,minute,0);
picker.pick=dateObj.getTime();
setTimeout(()=>{
    const d=new Date(picker.pick);
    picker.setTime(d,timeObj);
    picker.pick=d.getTime();
    console.log('设置成功:',picker.inputValue);
},0);

影刀环境又踩了一个坑

把代码放进影刀 JS 模块以后,发现:

F12 能运行,不代表影刀环境就完全一样。

于是开始增加日志,把:

picker.pick
picker.selectedDate
picker.currentDate
picker.inputValue
input.value

全部打印/返回出来。

结果又发现一个非常有意思的问题:

Vue 内部状态已经变成了目标日期:

picker.selectedDate
picker.inputValue

都已经正确。

但是:

input.value

还可能是旧值。

也就是说:

Vue 状态已经更新
        ↓
DOM 还没完成更新

这也解释了为什么前面会出现一些看起来“明明设置成功了,页面却不对”的情况。


还有一个很典型的 RPA 参数坑

影刀传入的参数有一次实际上是:

"2026-09-16"

而不是:

2026-09-16

多了一层字面量引号。

结果:

String(dateTime).trim().split('-')

解析出来当然不对。

最后增加:

String(dateTime).trim().replace(/^["']|["']$/g,'')

兼容这种情况。


最终影刀 JS 模块

最终用于影刀的版本:

function(element,dateTime){
    const input=[...document.querySelectorAll('wujie-app')].flatMap(x=>x.shadowRoot?[...x.shadowRoot.querySelectorAll('input.weui-desktop-form__input[placeholder="请选择发表时间"]')]:[])[0];
    if(!input)throw new Error('未找到发表时间输入框');
    const picker=input.closest('.weui-desktop-picker__date')?.__vue__;
    if(!picker)throw new Error('未找到日期选择器Vue实例');
    const value=String(dateTime).trim().replace(/^["']|["']$/g,'');
    const [date,time]=value.split(/\s+/);
    if(!date||!time)throw new Error('dateTime格式必须为 YYYY-MM-DD HH:mm');
    const [year,month,day]=date.split('-').map(Number);
    const [hour,minute]=time.split(':').map(Number);
    if([year,month,day,hour,minute].some(isNaN))throw new Error('dateTime格式错误,必须为 YYYY-MM-DD HH:mm');
    const dateObj=new Date(year,month-1,day);
    const timeObj=new Date(1970,0,1,hour,minute,0);
    picker.pick=dateObj.getTime();
    setTimeout(()=>{
        const d=new Date(picker.pick);
        picker.setTime(d,timeObj);
        picker.pick=d.getTime();
    },0);
    return null;
}

输入:

2026-09-10 12:15

最终可以直接修改日期和时间。


这个案例让我觉得更有意思的地方

其实这次解决的根本不是一个“腾讯/微信日期选择器”。

日期选择器只是一个例子。

真正值得记录的是一个网页自动化的思路:

传统 RPA

找到元素
↓
点击
↓
输入
↓
选择
↓
确认

这种方式本质上是在:

模拟用户操作 UI。

而另一种思路是:

找到元素
↓
找到背后的组件
↓
找到组件的数据状态
↓
直接修改数据
↓
让前端框架自己完成渲染

也就是:

UI 只是数据的表现形式。

如果一个网页使用了 Vue、React、Angular 等前端框架,而我们又能够找到它背后的组件实例、状态对象、数据模型,甚至网站本身就暴露了相关数据对象,那么很多时候就没必要再模拟人工操作。

例如:

输入框
下拉框
日期选择器
时间选择器
单选框
复选框
表单

都有可能存在类似的思路。

当然,这并不意味着存在一个真正万能的“直接改值函数”。

不同网站、不同框架、不同组件,数据存放的位置完全可能不同。

真正通用的,其实不是某一段代码。

而是一套排查思路:

UI 操作困难
↓
先找元素
↓
判断是不是 Shadow DOM
↓
判断是不是框架组件
↓
寻找组件实例/状态
↓
寻找真正的数据源
↓
直接修改数据
↓
观察框架是否自动同步 UI

所以以后再遇到一个:

“这个控件怎么都点不动、输不进去、元素定位特别难”的网页

除了继续研究 XPath、CSS、鼠标模拟、键盘模拟之外,还可以换一个方向:

别急着操作它,先看看它背后的数据是什么。

这可能才是网页自动化里更值得掌握的一种思路。

收藏
全部评论(1)
最新
发布评论
评论