aardio的webview2同步方式,通过js,cdp协同完成。几个重要点记录一下:
1、webview中异步、同步方法不能混用,同步会阻塞消息队列。这里要用cpd是同步方法,因此全部舍弃异步方法;
2、文件上传,用DOM.setFileInputFiles方法,不用模拟文件弹框,直接设置文件即可上传;
3、抖音的视频标题框是封装控件,必须模拟点击获取焦点。这里用Input发送键盘、鼠标、指令方式,模拟鼠键操作;
4、cdp默认是css选择器,通过Runtime.evaluate以xpath方式执行js获取nodeId,再执行其他cdp命令传参。
//自动化
import win.ui;
/*DSG{{*/
var winform = win.form(text="抖音自动发布";right=759;bottom=469;bgcolor=16777215;border="thin")
winform.add()
/*}}*/
import web.view;
import web.json;
import console;
function enableEdit(wb){
// 获取编辑框坐标
var rect = wb.eval(`JSON.stringify(document.querySelector("div#root div.editor-kit-root-container > div > div").getBoundingClientRect())`);
rect = web.json.parse(rect);
// 鼠标左键点击,[x,y]坐标
wb.cdp("Input.dispatchMouseEvent",{type="mousePressed";x=rect["x"]+10;y=rect["y"]+10;button="left"})
}
// 输入文本
function addSendMsg(wb,msg){
// CDP:模拟输入文本
wb.cdp("Input.insertText",{text=msg})
}
// 添加抖音用户
function addSendUser(wb,userId){
if(string.startWith(userId,'@')){
userId = string.right(userId,-2)
}
addSendMsg(wb, "@" ++ userId)
// 等待选人
wb.waitEle(`div.editor-kit-root-container div.mention-suggest-mount-dom img`);
// 选人
wb.eval(`document.evaluate('.//span[contains(string(),"抖音号 ` + userId + `")]',document).iterateNext().click()`)
}
// 获取输入总长度
function getMsgLength(wb){
var data = wb.eval(`document.querySelector("div.toolbar > div:nth-child(2)").innerText.match(/\d+/ig)`);
if(tonumber(data[1]) > tonumber(data[2])){
// 必须要点一下编辑框才能撤销
enableEdit(wb)
// CDP:模拟命令,撤销命令
wb.cdp("Input.dispatchKeyEvent",{
type="rawKeyDown";
commands={"undo","undo"}
})
return false;
}
return true;
}
function uploadVideo(wb,mp4,msgs){
//console.log("2222");
// 等待上传框
wb.waitEle(`input[type="file"]`);
sleep(1000)
// 获取控件
var file = wb.cdpQuery(`input[type="file"]`);
// 设置文件路径,上传
wb.cdp("DOM.setFileInputFiles",{
files = { mp4 };
nodeId = file.nodeId;
})
// 等待编辑框
wb.waitEle("div#root div.editor-kit-root-container");
// 等待3秒
sleep(1000)
enableEdit(wb)
var flag;
// 发消息
for(k,v in msgs){
if(string.startWith(v,'@')){
addSendUser(wb,v)
}else {
addSendMsg(wb,v)
}
flag = getMsgLength(wb);
//console.log(flag);
if(!flag){
break
}
}
sleep(1000)
// 勾选不允许下载
wb.eval(`document.querySelector('div[class^="download-content"] input[type="checkbox"][value="0"]').click()`);
// 等待重新上传框子,说明传完
wb.waitEle(`#root > div > div > div[class^="content-body-"] > div[class^="preview-"] > div > div > div > div[class^="long-card-"]`);
// 发布按钮,滚动到可视区域
var dd = wb.cdp("Runtime.evaluate",{
expression = `document.evaluate(".//button[text()='发布']",document).iterateNext()`;
});
wb.cdp("DOM.scrollIntoViewIfNeeded",{
objectId = dd.result.objectId;
});
// 发布视频
wb.eval(`document.evaluate("//button[text()='发布']",document).iterateNext().click()`);
}
// 要发布的视频地址
var mp4 = "mp4路径";
// 视频title。文字,@抖音号。抖音号自动转成@人
var msgs = {`测试一段文字,测试@人:`,"@77440681","@77440682","@77440683","@77440684"};
// 初始化浏览器
var wb = web.view(winform,`\userdata`);
winform.show();
// 设置禁用选项,关闭右键,开发工具,状态栏
wb.enableDefaultContextMenus(false);
wb.enableDevTools(false);
wb.enableStatusBar(false);
//添加网页默认加载脚本,禁止弹出新窗口
wb.preloadScript("window.open = function(url){ window.location.href = url; } ")
// 打开上传页面
wb.go("https://creator.douyin.com/creator-micro/content/upload");
wb.wait("");
var flag = true;
while(flag){
select(wb.location) {
case "https://creator.douyin.com/creator-micro/content/upload" {
uploadVideo(wb,mp4,msgs)
flag = false;
}
case "https://creator.douyin.com/creator-micro/home" {
// 登录成功,跳转到上传页面
wb.go("https://creator.douyin.com/creator-micro/content/upload");
wb.wait("creator-micro/content/upload");
//console.log("222")
}
case "https://creator.douyin.com/" {
// 没有登录情况下,人工登录
wb.waitEle("span.login")
wb.eval(`document.querySelector("span.login").click()`)
wb.wait("creator-micro/home");
//console.log("3333")
}
}
}
win.loopMessage();
另外异步方式的实现,不稳定,记录一下。
//waitEle 用法演示
import win.ui;
/*DSG{{*/
var winform = win.form(text="抖音自动发帖";right=1097;bottom=759)
winform.add(
btn_video={cls="plus";text="视频目录";left=784;top=688;right=880;bottom=720;align="left";color=3947580;disabled=1;dr=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF07C';notify=1;textPadding={left=25};z=6};
btn_xls={cls="plus";text="Excel文件";left=784;top=656;right=880;bottom=688;align="left";color=3947580;disabled=1;dr=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF07C';notify=1;textPadding={left=25};z=8};
button_clean={cls="button";text="清空";left=1000;top=656;right=1096;bottom=680;disabled=1;z=4};
button_start={cls="button";text="开始";left=888;top=656;right=984;bottom=680;disabled=1;z=3};
edit_log={cls="edit";left=890;top=2;right=1096;bottom=648;color=128;disabled=1;edge=1;multiline=1;readonly=1;z=2};
edit_sleep={cls="edit";text="2000";left=336;top=680;right=448;bottom=720;disabled=1;edge=1;num=1;z=12};
edit_sn={cls="edit";left=72;top=728;right=328;bottom=752;edge=1;readonly=1;z=14};
edit_tips={cls="edit";left=16;top=680;right=328;bottom=720;disabled=1;edge=1;multiline=1;z=9};
edit_video={cls="plus";left=456;top=688;right=784;bottom=714;align="left";border={bottom=1;color=-6908266};disabled=1;dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-13);textPadding={top=6;bottom=2};z=5};
edit_vip={cls="edit";left=392;top=728;right=872;bottom=752;edge=1;z=16};
edit_xls={cls="plus";left=456;top=656;right=784;bottom=682;align="left";border={bottom=1;color=-6908266};disabled=1;dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-13);textPadding={top=6;bottom=2};z=7};
static={cls="static";left=0;top=0;right=889;bottom=651;transparent=1;z=1};
static2={cls="static";text="话术";left=16;top=656;right=80;bottom=672;color=3947580;font=LOGFONT(h=-14);group=1;transparent=1;z=10};
static3={cls="static";text="机器码:";left=16;top=736;right=72;bottom=760;transparent=1;z=13};
static4={cls="static";text="搜索间隔(毫秒)";left=336;top=656;right=432;bottom=672;color=3947580;font=LOGFONT(h=-14);group=1;transparent=1;z=11};
static6={cls="static";text="激活码:";left=336;top=736;right=392;bottom=760;transparent=1;z=15};
static7={cls="static";text="有效期至:";left=880;top=736;right=952;bottom=760;transparent=1;z=17};
static_tm={cls="static";text="-";left=944;top=736;right=1072;bottom=760;transparent=1;z=18}
)
/*}}*/
import winex;
import key;
import config;
import fsys;
import mouse;
import winex.mouse;
import winex.key;
import com.excel;
import web.view;
import win.timer;
import myplu.vip;
// 自动保存配置文件
winform.bindConfig( config.winform,{
edit = "text";
radiobutton = "checked";
checkbox = "checked";
combobox = "selIndex";
plus ={
["^chk"] = "checked";
["^edit"] = "text";
};
} );
// 定时器
var timer = win.timer(winform);
// 间隔1秒
timer.setInterval(1000);
// 更新计时
timer.onTimer = function(hwnd,msg,id,tick){
winform.static_tm.text = "";
}
// 网页控件
var wb = web.view(winform.static,`\userdata`);
var xls_data = null;
var video_data = null;
var start_flag = true;
var wb_hwnd = null;
// 是否离开的弹窗
function checkExit(){
var hwnd = winex.findEx(,,"Chrome\_WidgetWin\_\x+","是否离开网站")
if(hwnd){
winex.key.click(hwnd,"ENTER")
}
}
// 是否离开的弹窗
function checkMap(){
var hwnd = winex.findEx(,,"Chrome\_WidgetWin\_\x+","")
if(hwnd){
winex.key.click(hwnd,"ENTER")
}
}
// 浏览器窗口里面找窗口句柄,然后操作
function getWdHwnd(){
if(not wb_hwnd){
checkMap();
wb_hwnd = winex.findEx(winform.static.hwnd,,"Chrome\_RenderWidgetHostHWND","Chrome Legacy Window");
}
return wb_hwnd;
}
function checkDialog(){
/*
if(xls_data == null or #xls_data == 0){
winform.edit_log.print("excel数据已发布完");
return ;
}
if(video_data == null or #video_data ==0 ){
winform.edit_log.print("视频已发布完");
return ;
}*/
hwnd,hwnd_button,t_id,p_id = winex.wait(,"打开","#32770","Button")
if(hwnd){
winform.edit_log.print(hwnd,hwnd_button,t_id,p_id)
hwnd_edit = winex.findEx(hwnd,,"Edit","")
sleep(100);
winex.mouse.click(hwnd_edit,32,18)
winform.edit_log.print(hwnd_edit)
sleep(100);
var txt = table.pop(video_data,1);
if(txt && #txt > 0){
txt = winform.edit_video.text + "\" + txt;
winform.edit_log.print(txt);
winex.sayIme(txt,hwnd_edit)
sleep(100);
winform.edit_log.print(hwnd_button)
winex.key.click(hwnd_button,"ENTER")
}else {
winform.edit_log.print("没有更多上传视频");
}
return true;
}
sleep(500);
var hwnd = winex.findEx(,,"#32770","打开");
retun (hwnd == null);
}
// 异步调用
wb.external = {
uploadVideo = function(){
checkDialog();
};
sendMsg = function(data,flag){
if(start_flag==false){
winform.edit_log.print("停止");
return ;
}
x,y = data["x"]+data["width"]-5,data["y"]+data["height"]-5;
winform.edit_log.print(x,y);
// 点击输入框位置
winex.mouse.clickDB(getWdHwnd(),x,y);
// 输入文案
if(#winform.edit_tips.text > 0 and flag){
sleep(500);
winex.say(winform.edit_tips.text,hwnd);
}
handleMsg();
};
nextMsg = function(data){
if(start_flag==false){
winform.edit_log.print("停止");
return ;
}
winform.edit_log.print("成功:" + data);
// 成功,可以接续下一个
handleMsg();
};
exitMsg = function(data){
winform.edit_log.print("成功:" + data);
winform.edit_log.print("文字超长,回删");
// 数量大于500,说明可以停止
winex.key.combine(getWdHwnd(),"CTRL","Z");
winex.key.combine(getWdHwnd(),"CTRL","Z");
if(start_flag==false){
winform.edit_log.print("停止");
return ;
}
commitMsg();
};
errorMsg = function(){
winform.edit_log.print("用户未找到");
// 没有找到id,下一个
winex.key.combine(getWdHwnd(),"CTRL","Z");
if(start_flag==false){
winform.edit_log.print("停止");
return ;
}
handleMsg();
};
submitMsg = function(data){
if(start_flag==false){
winform.edit_log.print("停止");
return ;
}
winform.edit_log.print("发布");
sleep(500);
x,y,cx,cy = winform.static.getPos(true);
x,y = x+data["x"]+data["width"]/2,y+data["y"]+data["height"]/2;
winform.edit_log.print(x,y);
mouse.click(x,y,true)
//winex.mouse.clickDB(getWdHwnd(),x,y);
sleep(5000);
// 进行下一页
wb.go("https://creator.douyin.com/creator-micro/content/upload");
//checkExit();
};
}
// 网页加载监听
wb.onDocumentInit = function(url){
winform.edit_log.print(url);
if(url="https://creator.douyin.com/creator-micro/content/upload"){
// 用法三:异步等待参数@1指定CSS选择器的节点,回调 Javascript 代码
// 第一个页面,上传视频
wb.waitEle("label",`
document.querySelector("label").click();
// 这里至关重要,文件弹框跟uploadVideo方法里面等待阻塞,导致可能模拟操作失败。因此这里异步等待弹框先出来。
setTimeout(function () {
aardio.uploadVideo();
},3000);
`);
// 第二个页面,发送文本
wb.waitEle("div#root div.editor-kit-root-container",`
setTimeout(function () {
aardio.sendMsg(document.querySelector("div#root div.editor-kit-root-container > div > div").getBoundingClientRect(),true);
},1000);
`);
// 发布后,取消同步到西瓜
wb.waitEle("button.semi-button.semi-button-tertiary.semi-button-light > span.semi-button-content",`
document.evaluate("//span[@class='semi-button-content' and text()='暂不同步']",document).iterateNext().click();
`);
}
}
// 读取excel数据
function readExcel(xlsPath,sheetName=1){
var excel,err = com.excel();
excel.alerts = false;
var book = excel.Open(xlsPath);
var sheet = book.Sheets(sheetName);
var count = sheet.UsedRange.Rows.Count;
var res = {};
for(i=2;count;1){
table.push(res,sheet.Cells(i,2).Text);
}
excel.Quit();
return res;
}
// 锁定控件
function clockCtrl(flag){
winform.edit_sn.text = myplu.vip.keyCode();
winform.edit_xls.disabled = flag;
winform.edit_video.disabled = flag;
winform.edit_sleep.disabled = flag;
winform.edit_tips.disabled = flag;
winform.btn_xls.disabled = flag;
winform.btn_video.disabled = flag;
winform.button_start.disabled = flag;
winform.button_clean.disabled = flag;
if(flag){
timer.enable();
}else{
timer.disable();
}
}
// 检测时间
function checkVip(){
var res = false;
if(winform.edit_vip.text == ""){
win.msgbox("请联系运营申请激活码!","运行提示");
clockCtrl(true);
return res;
}
try{
var tm = myplu.vip.decode("xxxxx",winform.edit_vip.text);
if(tm){
if(time.now().diffsecond(tm) > 0){
clockCtrl(true);
winform.static_tm.text=tostring(tm,"%Y-%m-%d %H:%M:%S","chs");
winform.edit_log.print("激活码已过期");
}else {
clockCtrl(false);
winform.static_tm.text=tostring(tm,"%Y-%m-%d %H:%M:%S","chs");
winform.edit_log.print("已激活");
res = true;
}
}else {
clockCtrl(true);
winform.edit_log.print("激活失败");
}
}
catch(e){
clockCtrl(true);
win.msgbox("请输入有效的激活码!","运行提示");
}
return res;
}
// 模拟发送
function handleMsg(){
var res;
do{
// 成功,可以接续下一个
if(#xls_data > 0){
res = addUser();
}else {
commitMsg();
res = true;
}
}while(res == false)
}
// 添加配置操作
function addUser(){
var userId = table.pop(xls_data,1);
if(userId and #userId>0){
winform.edit_log.print(userId);
sleep(500)
winex.say("@" + userId)
sleep(tonumber(winform.edit_sleep.text))
wb.doScript(`
(function(){
var node = document.evaluate('.//span[contains(string(),"抖音号 ` + userId + `")]',document).iterateNext();
if(node){
node.click();
// 之后执行
setTimeout(function () {
var psize = document.querySelector("#root div.editor-kit-editor-container > div:nth-child(2)").innerText.match(/\d+/ig);
if(parseInt(psize[0]) < parseInt(psize[1])){
// 说明可以继续填写
aardio.nextMsg(node.innerText);
}else{
// 说明不能继续填写
aardio.exitMsg(node.innerText);
}
}, 200)
}else{
// 不存在,说明失败
aardio.errorMsg();
}
})();
`,function(result,err){
});
return true;
}else{
return false;
}
}
// 发布动作
function commitMsg(){
wb.doScript(`
(function(){
document.evaluate("//label[span/text()='不允许']/input[@type='checkbox']",document).iterateNext().click();
document.evaluate("//button[text()='发布']",document).iterateNext().scrollIntoView();
setTimeout(function () {
aardio.submitMsg(document.evaluate("//button[text()='发布']",document).iterateNext().getBoundingClientRect());
}, 1000)
})();
`,function(result,err){
});
}
// 开始按钮
winform.button_start.oncommand = function(id,event){
if(winform.button_start.text == "停止"){
start_flag = false;
winform.button_start.text = "继续";
return ;
}
var res = checkVip();
if(res == null){
return ;
}
// 检查excel
if(winform.edit_xls.text == ""){
winform.edit_log.print("请选择Excel文件");
winform.btn_xls.oncommand();
return;
}elseif(io.exist(winform.edit_xls.text) == false) {
winform.edit_log.print("Excel文件不存在,重新选择!");
winform.btn_xls.oncommand();
return;
}
// 检查视频
if(winform.edit_video.text == ""){
winform.edit_log.print("请选择要上传视频的目录");
winform.btn_video.oncommand();
return;
}elseif(io.exist(winform.edit_video.text) == false) {
winform.edit_log.print("视频目录不存在,重新选择!");
winform.btn_video.oncommand();
return;
}
// 构造excel数据
if(xls_data == null){
winform.edit_log.print("读取excel数据");
xls_data = readExcel(winform.edit_xls.text);
table.reverse(xls_data);
}
// 查找视频文件名
if(video_data == null){
winform.edit_log.print("扫描上传视频文件");
video_data = fsys.list(winform.edit_video.text)
}
winform.button_start.text = "停止";
if(start_flag){
winform.edit_log.print("开始");
winform.edit_log.print("打开抖音上传页面");
wb.go("https://creator.douyin.com/creator-micro/content/upload");
}else {
start_flag = true;
winform.edit_log.print("继续");
// 第二个页面,发送文本
wb.waitEle("div#root div.editor-kit-root-container",`
aardio.sendMsg(document.querySelector("div#root div.editor-kit-root-container > div > div").getBoundingClientRect(),false);
`);
}
}
// 清空按钮
winform.button_clean.oncommand = function(id,event){
winform.edit_log.text = "";
}
// 视频目录
import fsys.dlg.dir;
winform.btn_video.oncommand = function(id,event){
var path = fsys.dlg.dir(,winform,'请选择视频目录')
if(path){
winform.edit_video.text = path;
video_data = null;
}
// 保持配置
config.saveAll();
}
winform.btn_video.skin({
color={
active=0xFF00FF00;
default=0xFF3C3C3C;
disabled=0xFF6D6D6D;
hover=0xFFFF0000
}
})
// excel 文件
import fsys.dlg;
winform.btn_xls.oncommand = function(id,event){
var path = fsys.dlg.open('Excel文件|*.elx;*.xlsx||',,,winform);
if(path){
winform.edit_xls.text = path;
xls_data = null;
}
// 保持配置
config.saveAll();
}
winform.btn_xls.skin({
color={
active=0xFF00FF00;
default=0xFF3C3C3C;
disabled=0xFF6D6D6D;
hover=0xFFFF0000
}
})
winform.show();
// 话术编辑后保存
winform.edit_tips.onFocusLost = function(id,event){
// 保持配置
config.saveAll();
}
// 间隔时间
winform.edit_sleep.onFocusLost = function(id,event){
// 保持配置
config.saveAll();
}
// 清空日志框
winform.edit_log.text = "请自行登录抖音账号";
// 打开抖音
wb.go("https://www.douyin.com/");
winform.edit_vip.onFocusLost = function(id,event){
checkVip();
}
checkVip();
checkExit();
win.loopMessage();
网友评论