mirror of
https://github.com/NaiboWang/EasySpider.git
synced 2025-04-19 23:14:54 +08:00
Dynamic Debugging!!!
This commit is contained in:
parent
c3bab575f4
commit
0c30b27756
@ -0,0 +1 @@
|
||||
{"id":7,"name":"京东全球版-专业的综合网上购物商城","url":"https://www.jd.com","links":"https://www.jd.com","create_time":"12/11/2023, 7:29:08 AM","update_time":"12/11/2023, 8:02:45 AM","version":"0.6.0","saveThreshold":10,"quitWaitTime":60,"environment":0,"maximizeWindow":0,"maxViewLength":15,"recordLog":1,"outputFormat":"xlsx","saveName":"current_time","inputExcel":"","startFromExit":0,"pauseKey":"p","containJudge":false,"desc":"https://www.jd.com","inputParameters":[{"id":0,"name":"urlList_0","nodeId":1,"nodeName":"打开网页","value":"https://www.jd.com","desc":"要采集的网址列表,多行以\\n分开","type":"text","exampleValue":"https://www.jd.com"},{"id":1,"name":"inputText_1","nodeName":"输入文字","nodeId":4,"desc":"要输入的文本,如京东搜索框输入:电脑","type":"text","exampleValue":"Field[\"参数1\"]","value":"Field[\"参数1\"]"}],"outputParameters":[{"id":0,"name":"参数1","desc":"","type":"text","recordASField":1,"exampleValue":"/手机/数码"}],"graph":[{"index":0,"id":0,"parentId":0,"type":-1,"option":0,"title":"root","sequence":[1,2],"parameters":{"history":1,"tabIndex":0,"useLoop":false,"xpath":"","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0},"isInLoop":false},{"id":1,"index":1,"parentId":0,"type":0,"option":1,"title":"打开网页","sequence":[],"isInLoop":false,"position":0,"parameters":{"useLoop":false,"xpath":"","wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"url":"https://www.jd.com","links":"https://www.jd.com","maxWaitTime":10,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"cookies":""}},{"id":2,"index":2,"parentId":0,"type":1,"option":8,"title":"循环采集数据","sequence":[3,4],"isInLoop":false,"position":1,"parameters":{"history":4,"tabIndex":-1,"useLoop":false,"xpath":"/html/body/div[5]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"loopType":1,"pathList":"","textList":"","code":"","waitTime":0,"exitCount":0,"exitElement":"//body","historyWait":2,"breakMode":0,"breakCode":"","breakCodeWaitTime":0,"allXPaths":["/html/body/div[5]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]","//div[contains(., '/手机/数码')]","//DIV[@class='LeftSide_menu_item__SBMWC LeftSide_text_space__2UhbG ']","/html/body/div[last()-5]/div/div[last()-4]/div/div[last()-2]/div/div/div/div[last()-1]/div[last()-12]"]}},{"id":3,"index":3,"parentId":2,"type":0,"option":3,"title":"提取数据","sequence":[],"isInLoop":true,"position":0,"parameters":{"history":4,"tabIndex":-1,"useLoop":false,"xpath":"","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"clear":0,"newLine":1,"paras":[{"nodeType":0,"contentType":7,"relative":true,"name":"参数1","desc":"","extractType":0,"relativeXPath":"","allXPaths":"","exampleValues":[{"num":0,"value":"/手机/数码"}],"unique_index":"kfd57x5t8clq048p17","iframe":false,"default":"","paraType":"text","recordASField":1,"beforeJS":"","beforeJSWaitTime":0,"JS":"","JSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"downloadPic":0}],"loopType":1}},{"id":4,"index":4,"parentId":2,"type":0,"option":4,"title":"输入文字","sequence":[],"isInLoop":true,"position":1,"parameters":{"history":3,"tabIndex":-1,"useLoop":false,"xpath":"//*[@id=\"key\"]","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"value":"Field[\"参数1\"]","index":0,"allXPaths":["/html/body/div[4]/div[1]/div[2]/div[1]/input[1]","//input[contains(., '')]","id(\"key\")","//INPUT[@class='text defcolor']","/html/body/div[last()-6]/div/div[last()-2]/div/input"]}}]}
|
@ -256,7 +256,7 @@ async function findElementAcrossAllWindows(msg, notifyBrowser = true, scrollInto
|
||||
len = len - 1;
|
||||
if (!notify) {
|
||||
notify = true;
|
||||
notify_browser("正在尝试在其他窗口中查找元素,请耐心等待。", "Trying to find elements in other windows, please wait patiently.", "info");
|
||||
// notify_browser("正在尝试在其他窗口中查找元素,请耐心等待。", "Trying to find elements in other windows, please wait patiently.", "info");
|
||||
}
|
||||
if (len == 0) {
|
||||
break;
|
||||
@ -306,7 +306,6 @@ async function beginInvoke(msg, ws) {
|
||||
let height = parseInt(size.height * 0.6)
|
||||
window.setBounds({x: 0, y: size.height * 0.4, height: height, width: width});
|
||||
}
|
||||
|
||||
flowchart_window.show();
|
||||
// flowchart_window.openDevTools();
|
||||
} else if (msg.type == 2) {
|
||||
@ -345,131 +344,256 @@ async function beginInvoke(msg, ws) {
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
} else if (msg.type == 4) { //试运行功能
|
||||
try {
|
||||
let flowchart_url = flowchart_window.webContents.getURL();
|
||||
} catch {
|
||||
flowchart_window = null;
|
||||
}
|
||||
if (flowchart_window == null) {
|
||||
notify_flowchart("试运行功能只能在设计任务阶段,Chrome浏览器打开时使用!", "The trial run function can only be used when designing tasks and opening in Chrome browser!", "error");
|
||||
} else {
|
||||
let node = JSON.parse(msg.message.node);
|
||||
notify_browser("正在试运行操作:" + node.title, "Trying to run the operation: " + node.title, "info");
|
||||
} else if (msg.type == 4) { //标记元素和试运行功能
|
||||
let node = JSON.parse(msg.message.node);
|
||||
let type = msg.message.type;
|
||||
if (type == 0) { //标记元素
|
||||
let option = node.option;
|
||||
let parameters = node.parameters;
|
||||
let beforeJS = "";
|
||||
let beforeJSWaitTime = 0;
|
||||
let afterJS = "";
|
||||
let afterJSWaitTime = 0;
|
||||
try {
|
||||
beforeJS = parameters.beforeJS;
|
||||
beforeJSWaitTime = parameters.beforeJSWaitTime;
|
||||
afterJS = parameters.afterJS;
|
||||
afterJSWaitTime = parameters.afterJSWaitTime;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
if (option == 1) {
|
||||
let url = parameters.links.split("\n")[0].trim();
|
||||
if (parameters.useLoop) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
url = parent_node["parameters"]["textList"].split("\n")[0];
|
||||
//下面是让浏览器自动滚动到元素位置
|
||||
if (option == 2 || option == 4 || option == 6 || option == 7) {
|
||||
let xpath = parameters.xpath;
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
if (parameters.useLoop && option != 4 && option != 6) {
|
||||
let parentXPath = parent_node.parameters.xpath;
|
||||
xpath = parentXPath + xpath;
|
||||
}
|
||||
await driver.get(url);
|
||||
} else if (option == 2 || option == 7) { //点击事件
|
||||
let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
|
||||
if (parameters.useLoop) {
|
||||
let elementInfo = {"iframe": parameters.iframe, "xpath": xpath, "id": -1};
|
||||
//用于跳转到元素位置
|
||||
let element = await findElementAcrossAllWindows(elementInfo, notifyBrowser = false);
|
||||
} else if (option == 3) {
|
||||
let paras = parameters.paras; //所有的提取数据参数
|
||||
let para = paras[0];
|
||||
let xpath = para.relativeXPath;
|
||||
if (para.relative) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
let parent_xpath = parent_node.parameters.xpath;
|
||||
elementInfo.xpath = parent_xpath + elementInfo.xpath;
|
||||
xpath = parent_xpath + xpath;
|
||||
}
|
||||
let element = await findElementAcrossAllWindows(elementInfo);
|
||||
await execute_js(parameters.beforeJS, element, parameters.beforeJSWaitTime);
|
||||
if (option == 2) {
|
||||
await click_element(element);
|
||||
} else if (option == 7) {
|
||||
await driver.actions().move({origin: element}).perform();
|
||||
}
|
||||
await execute_js(parameters.afterJS, element, parameters.afterJSWaitTime);
|
||||
send_message_to_browser(JSON.stringify({"type": "cancelSelection"}));
|
||||
} else if (option == 3) { //提取数据
|
||||
notify_browser("提示:提取数据操作只能试运行设置的JavaScript语句,且只针对第一个匹配的元素。", "Hint: can only test JavaScript statement set in the data extraction operation, and only for the first matching element.", "info");
|
||||
let elementInfo = {"iframe": para.iframe, "xpath": xpath, "id": -1};
|
||||
let element = await findElementAcrossAllWindows(elementInfo, notifyBrowser = false);
|
||||
} else if (option == 11) {
|
||||
let paras = parameters.paras; //所有的提取数据参数
|
||||
let not_found_xpaths = [];
|
||||
for (let i = 0; i < paras.length; i++) {
|
||||
let para = paras[i];
|
||||
let xpath = para.relativeXPath;
|
||||
if (para.relative) {
|
||||
let i = parameters.index;
|
||||
let para = paras[i];
|
||||
let xpath = para.relativeXPath;
|
||||
if (para.relative) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
let parent_xpath = parent_node.parameters.xpath;
|
||||
xpath = parent_xpath + xpath;
|
||||
}
|
||||
let elementInfo = {"iframe": para.iframe, "xpath": xpath, "id": -1};
|
||||
let element = await findElementAcrossAllWindows(elementInfo, notifyBrowser = false);
|
||||
} else if (option == 8) {
|
||||
// <select v-model='loopType' class="form-control" @change = "handleLoopTypeChange" >
|
||||
// < option
|
||||
// :
|
||||
// value = 0 > 单个元素(多用于循环点击下一页)</option>
|
||||
// <option :value = 1 > 不固定元素列表 < /option>
|
||||
// <option :value = 2 > 固定元素列表 < /option>
|
||||
// <option :value = 3 > 文本列表(多用于循环在文本框输入文本)</option>
|
||||
// <option :value = 4 > 网址列表(多用于循环打开网页)</option>
|
||||
// <option :value = 5 > JavaScript命令返回值(需以return
|
||||
// 开头)</option>
|
||||
// <option :value = 6 > 系统命令返回值 < /option>
|
||||
// <option :value = 7 > 执行环境下的Python表达式值(eval操作)</option>
|
||||
// </select>
|
||||
//
|
||||
let loopType = parameters.loopType;
|
||||
if (loopType <= 2) {
|
||||
let xpath = "";
|
||||
if (loopType <= 1) {
|
||||
xpath = parameters.xpath;
|
||||
} else if (loopType == 2) {
|
||||
xpath = parameters.pathList.split("\n")[0].trim();
|
||||
}
|
||||
let elementInfo = {"iframe": parameters.iframe, "xpath": xpath, "id": -1};
|
||||
let element = await findElementAcrossAllWindows(elementInfo, notifyBrowser = false);
|
||||
} else if (loopType == 5) { //JavaScript命令返回值
|
||||
let code = parameters.code;
|
||||
let waitTime = parameters.waitTime;
|
||||
let element = await driver.findElement(By.tagName("body"));
|
||||
let outcome = await execute_js(code, element, waitTime);
|
||||
if (!outcome || outcome == -1) {
|
||||
notify_browser("目前页面中,设置的循环“" + node.title + "”的JavaScript条件不成立", "The condition of the loop " + node.title + " is not met, skip this loop.", "warning");
|
||||
} else {
|
||||
notify_browser("目前页面中,设置的循环“" + node.title + "”的JavaScript条件成立", "The condition of the loop " + node.title + " is met, continue this loop.", "success");
|
||||
}
|
||||
}
|
||||
} else if (option == 10) { //条件分支
|
||||
let condition = parameters.class; //条件类型
|
||||
let result = -1;
|
||||
let additionalInfo = "";
|
||||
if (condition == 5 || condition == 7) { //JavaScript命令返回值
|
||||
let code = parameters.code;
|
||||
let waitTime = parameters.waitTime;
|
||||
let element = await driver.findElement(By.tagName("body"));
|
||||
if (condition == 7) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
let xpath = parent_node.parameters.xpath;
|
||||
elementInfo = {"iframe": parent_node.parameters.iframe, "xpath": xpath, "id": -1};
|
||||
element = await findElementAcrossAllWindows(elementInfo);
|
||||
}
|
||||
let outcome = await execute_js(code, element, waitTime);
|
||||
if (!outcome) {
|
||||
msg.message.result = 0; //条件不成立传入扩展
|
||||
} else if (outcome == -1) {
|
||||
msg.message.result = -1; //JS执行出错
|
||||
} else {
|
||||
msg.message.result = 1; //条件成立传入扩展
|
||||
}
|
||||
}
|
||||
}
|
||||
send_message_to_browser(JSON.stringify({"type": "trial", "message": msg}));
|
||||
} else { //试运行
|
||||
try {
|
||||
let flowchart_url = flowchart_window.webContents.getURL();
|
||||
} catch {
|
||||
flowchart_window = null;
|
||||
}
|
||||
if (flowchart_window == null) {
|
||||
notify_flowchart("试运行功能只能在设计任务阶段,Chrome浏览器打开时使用!", "The trial run function can only be used when designing tasks and opening in Chrome browser!", "error");
|
||||
} else {
|
||||
notify_browser("正在试运行操作:" + node.title, "Trying to run the operation: " + node.title, "info");
|
||||
let option = node.option;
|
||||
let parameters = node.parameters;
|
||||
let beforeJS = "";
|
||||
let beforeJSWaitTime = 0;
|
||||
let afterJS = "";
|
||||
let afterJSWaitTime = 0;
|
||||
try {
|
||||
beforeJS = parameters.beforeJS;
|
||||
beforeJSWaitTime = parameters.beforeJSWaitTime;
|
||||
afterJS = parameters.afterJS;
|
||||
afterJSWaitTime = parameters.afterJSWaitTime;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
if (option == 1) {
|
||||
let url = parameters.links.split("\n")[0].trim();
|
||||
if (parameters.useLoop) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
url = parent_node["parameters"]["textList"].split("\n")[0];
|
||||
}
|
||||
try {
|
||||
await driver.get(url);
|
||||
} catch (e) {
|
||||
driver.switchTo().window(current_handle);
|
||||
await driver.get(url);
|
||||
}
|
||||
} else if (option == 2 || option == 7) { //点击事件
|
||||
let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
|
||||
if (parameters.useLoop) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
let parent_xpath = parent_node.parameters.xpath;
|
||||
xpath = parent_xpath + xpath;
|
||||
elementInfo.xpath = parent_xpath + elementInfo.xpath;
|
||||
}
|
||||
let elementInfo = {"iframe": para.iframe, "xpath": xpath, "id": -1};
|
||||
let element = await findElementAcrossAllWindows(elementInfo, notifyBrowser = false);
|
||||
if (element != null) {
|
||||
await execute_js(para.beforeJS, element, para.beforeJSWaitTime);
|
||||
await execute_js(para.afterJS, element, para.afterJSWaitTime);
|
||||
} else {
|
||||
not_found_xpaths.push(xpath);
|
||||
let element = await findElementAcrossAllWindows(elementInfo); //通过此函数找到元素并切换到对应的窗口
|
||||
await execute_js(parameters.beforeJS, element, parameters.beforeJSWaitTime);
|
||||
if (option == 2) {
|
||||
await click_element(element);
|
||||
let alertHandleType = parameters.alertHandleType;
|
||||
if (alertHandleType == 1) {
|
||||
try {
|
||||
await driver.switchTo().alert().accept();
|
||||
} catch (e) {
|
||||
console.log("No alert");
|
||||
}
|
||||
} else if (alertHandleType == 2) {
|
||||
try {
|
||||
await driver.switchTo().alert().dismiss();
|
||||
} catch (e) {
|
||||
console.log("No alert");
|
||||
}
|
||||
}
|
||||
} else if (option == 7) {
|
||||
await driver.actions().move({origin: element}).perform();
|
||||
}
|
||||
}
|
||||
if (not_found_xpaths.length > 0) {
|
||||
notify_browser("无法找到以下元素,请检查XPath是否正确:" + not_found_xpaths.join("\n"), "Cannot find the element, please check if the XPath is correct: " + not_found_xpaths.join("\n"), "warning");
|
||||
}
|
||||
} else if (option == 4) { //键盘输入事件
|
||||
let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
|
||||
let value = node.parameters.value;
|
||||
if (node.parameters.useLoop) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
value = parent_node["parameters"]["textList"].split("\n")[0];
|
||||
let index = node.parameters.index;
|
||||
if (index > 0) {
|
||||
value = value.split("~")[index - 1];
|
||||
await execute_js(parameters.afterJS, element, parameters.afterJSWaitTime);
|
||||
send_message_to_browser(JSON.stringify({"type": "cancelSelection"}));
|
||||
} else if (option == 3) { //提取数据
|
||||
notify_browser("提示:提取数据操作只能试运行设置的JavaScript语句,且只针对第一个匹配的元素。", "Hint: can only test JavaScript statement set in the data extraction operation, and only for the first matching element.", "info");
|
||||
let paras = parameters.paras; //所有的提取数据参数
|
||||
let not_found_xpaths = [];
|
||||
for (let i = 0; i < paras.length; i++) {
|
||||
let para = paras[i];
|
||||
let xpath = para.relativeXPath;
|
||||
if (para.relative) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
let parent_xpath = parent_node.parameters.xpath;
|
||||
xpath = parent_xpath + xpath;
|
||||
}
|
||||
let elementInfo = {"iframe": para.iframe, "xpath": xpath, "id": -1};
|
||||
let element = await findElementAcrossAllWindows(elementInfo, notifyBrowser = false);
|
||||
if (element != null) {
|
||||
await execute_js(para.beforeJS, element, para.beforeJSWaitTime);
|
||||
await execute_js(para.afterJS, element, para.afterJSWaitTime);
|
||||
} else {
|
||||
not_found_xpaths.push(xpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
let keyInfo = value
|
||||
let enter = false;
|
||||
if (/<enter>/i.test(keyInfo)) {
|
||||
keyInfo = keyInfo.replace(/<enter>/gi, '');
|
||||
enter = true;
|
||||
}
|
||||
if (keyInfo.indexOf("Field(") >= 0 || keyInfo.indexOf("eval(") >= 0) {
|
||||
//两秒后通知浏览器
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
notify_browser("检测到文字中包含Field(\"\")或eval(\"\"),试运行时无法输入两项表达式的替换值,请在任务正式调用阶段测试是否有效。", "Field(\"\") or eval(\"\") is detected in the text, and the replacement value of the two expressions cannot be entered during trial operation. Please test whether it is valid in the formal call stage.", "warning");
|
||||
}
|
||||
let element = await findElementAcrossAllWindows(elementInfo);
|
||||
await execute_js(beforeJS, element, beforeJSWaitTime);
|
||||
await element.sendKeys(Key.HOME, Key.chord(Key.SHIFT, Key.END), keyInfo);
|
||||
if (enter) {
|
||||
await element.sendKeys(Key.ENTER);
|
||||
}
|
||||
await execute_js(afterJS, element, afterJSWaitTime);
|
||||
} else if (option == 5) { //自定义操作的JS代码
|
||||
let code = parameters.code;
|
||||
let waitTime = parameters.waitTime;
|
||||
let element = await driver.findElement(By.tagName("body"));
|
||||
await execute_js(code, element, waitTime);
|
||||
} else if (option == 6) { //切换下拉选项
|
||||
let optionMode = parseInt(parameters.optionMode);
|
||||
let optionValue = parameters.optionValue;
|
||||
if (node.parameters.useLoop) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
optionValue = parent_node["parameters"]["textList"].split("\n")[0];
|
||||
let index = node.parameters.index;
|
||||
if (index > 0) {
|
||||
optionValue = optionValue.split("~")[index - 1];
|
||||
if (not_found_xpaths.length > 0) {
|
||||
notify_browser("无法找到以下元素,请检查XPath是否正确:" + not_found_xpaths.join("\n"), "Cannot find the element, please check if the XPath is correct: " + not_found_xpaths.join("\n"), "warning");
|
||||
}
|
||||
}
|
||||
let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
|
||||
let element = await findElementAcrossAllWindows(elementInfo);
|
||||
execute_js(beforeJS, element, beforeJSWaitTime);
|
||||
let dropdown = new Select(element);
|
||||
// Interacting with dropdown element based on optionMode
|
||||
switch (optionMode) {
|
||||
case 0: //切换到下一个选项
|
||||
let script = `var options = arguments[0].options;
|
||||
} else if (option == 4) { //键盘输入事件
|
||||
let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
|
||||
let value = node.parameters.value;
|
||||
if (node.parameters.useLoop) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
value = parent_node["parameters"]["textList"].split("\n")[0];
|
||||
let index = node.parameters.index;
|
||||
if (index > 0) {
|
||||
value = value.split("~")[index - 1];
|
||||
}
|
||||
}
|
||||
let keyInfo = value
|
||||
let enter = false;
|
||||
if (/<enter>/i.test(keyInfo)) {
|
||||
keyInfo = keyInfo.replace(/<enter>/gi, '');
|
||||
enter = true;
|
||||
}
|
||||
if (keyInfo.indexOf("Field(") >= 0 || keyInfo.indexOf("eval(") >= 0) {
|
||||
//两秒后通知浏览器
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
notify_browser("检测到文字中包含Field(\"\")或eval(\"\"),试运行时无法输入两项表达式的替换值,请在任务正式调用阶段测试是否有效。", "Field(\"\") or eval(\"\") is detected in the text, and the replacement value of the two expressions cannot be entered during trial operation. Please test whether it is valid in the formal call stage.", "warning");
|
||||
}
|
||||
let element = await findElementAcrossAllWindows(elementInfo);
|
||||
await execute_js(beforeJS, element, beforeJSWaitTime);
|
||||
await element.sendKeys(Key.HOME, Key.chord(Key.SHIFT, Key.END), keyInfo);
|
||||
if (enter) {
|
||||
await element.sendKeys(Key.ENTER);
|
||||
}
|
||||
await execute_js(afterJS, element, afterJSWaitTime);
|
||||
} else if (option == 5) { //自定义操作的JS代码
|
||||
let code = parameters.code;
|
||||
let codeMode = parameters.codeMode;
|
||||
let waitTime = parameters.waitTime;
|
||||
let element = await driver.findElement(By.tagName("body"));
|
||||
if (codeMode == 0) {
|
||||
await execute_js(code, element, waitTime);
|
||||
} else if (codeMode == 8) {
|
||||
//刷新页面
|
||||
await driver.navigate().refresh();
|
||||
}
|
||||
} else if (option == 6) { //切换下拉选项
|
||||
let optionMode = parseInt(parameters.optionMode);
|
||||
let optionValue = parameters.optionValue;
|
||||
if (node.parameters.useLoop) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
optionValue = parent_node["parameters"]["textList"].split("\n")[0];
|
||||
let index = node.parameters.index;
|
||||
if (index > 0) {
|
||||
optionValue = optionValue.split("~")[index - 1];
|
||||
}
|
||||
}
|
||||
let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
|
||||
let element = await findElementAcrossAllWindows(elementInfo);
|
||||
execute_js(beforeJS, element, beforeJSWaitTime);
|
||||
let dropdown = new Select(element);
|
||||
// Interacting with dropdown element based on optionMode
|
||||
switch (optionMode) {
|
||||
case 0: //切换到下一个选项
|
||||
let script = `var options = arguments[0].options;
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
if (options[i].selected) {
|
||||
options[i].selected = false;
|
||||
@ -481,26 +605,42 @@ async function beginInvoke(msg, ws) {
|
||||
break;
|
||||
}
|
||||
}`;
|
||||
await driver.executeScript(script, element);
|
||||
break;
|
||||
case 1:
|
||||
await dropdown.selectByIndex(parseInt(optionValue));
|
||||
break;
|
||||
case 2:
|
||||
await dropdown.selectByValue(optionValue);
|
||||
break;
|
||||
case 3:
|
||||
await dropdown.selectByVisibleText(optionValue);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid option mode');
|
||||
await driver.executeScript(script, element);
|
||||
break;
|
||||
case 1:
|
||||
await dropdown.selectByIndex(parseInt(optionValue));
|
||||
break;
|
||||
case 2:
|
||||
await dropdown.selectByValue(optionValue);
|
||||
break;
|
||||
case 3:
|
||||
await dropdown.selectByVisibleText(optionValue);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid option mode');
|
||||
}
|
||||
execute_js(afterJS, element, afterJSWaitTime);
|
||||
} else if (option == 11) { //单个提取数据参数
|
||||
notify_browser("提示:提取数据操作只能试运行设置的JavaScript语句,且只针对第一个匹配的元素。", "Hint: can only test JavaScript statement set in the data extraction operation, and only for the first matching element.", "info");
|
||||
let paras = parameters.paras; //所有的提取数据参数
|
||||
let i = parameters.index;
|
||||
let para = paras[i];
|
||||
let xpath = para.relativeXPath;
|
||||
if (para.relative) {
|
||||
let parent_node = JSON.parse(msg.message.parentNode);
|
||||
let parent_xpath = parent_node.parameters.xpath;
|
||||
xpath = parent_xpath + xpath;
|
||||
}
|
||||
let elementInfo = {"iframe": para.iframe, "xpath": xpath, "id": -1};
|
||||
let element = await findElementAcrossAllWindows(elementInfo);
|
||||
if (element != null) {
|
||||
await execute_js(para.beforeJS, element, para.beforeJSWaitTime);
|
||||
await execute_js(para.afterJS, element, para.afterJSWaitTime);
|
||||
}
|
||||
}
|
||||
execute_js(afterJS, element, afterJSWaitTime);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (msg.type == 8) { //展示元素功能
|
||||
|
||||
} else if (msg.type == 5) {
|
||||
let child = require('child_process').execFile;
|
||||
// 参数顺序: 1. task id 2. server address 3. saved_file_name 4. "remote" or "local" 5. user_data_folder
|
||||
@ -562,6 +702,8 @@ async function beginInvoke(msg, ws) {
|
||||
async function click_element(element) {
|
||||
try {
|
||||
await element.click();
|
||||
//ctrl+click
|
||||
// await driver.actions().keyDown(Key.CONTROL).click(element).keyUp(Key.CONTROL).perform();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
await driver.executeScript("arguments[0].click();", element);
|
||||
@ -569,17 +711,26 @@ async function click_element(element) {
|
||||
}
|
||||
|
||||
async function execute_js(js, element, wait_time = 3) {
|
||||
let outcome = 0;
|
||||
if (js.length != 0) {
|
||||
try {
|
||||
await driver.executeScript(js, element);
|
||||
outcome = await driver.executeScript(js, element);
|
||||
if (wait_time == 0) {
|
||||
wait_time = 30000;
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, wait_time));
|
||||
// await new Promise(resolve => setTimeout(resolve, wait_time));
|
||||
} catch (e) {
|
||||
// await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
notify_browser("执行JavaScript出错,请检查JavaScript语句是否正确:" + js + "\n错误信息:" + e, "Error executing JavaScript, please check if the JavaScript statement is correct: " + js + "\nError message: " + e, "error");
|
||||
outcome = -1;
|
||||
}
|
||||
if (js.indexOf("Field(") >= 0 || js.indexOf("eval(") >= 0) {
|
||||
//两秒后通知浏览器
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
notify_browser("检测到JavaScript中包含Field(\"\")或eval(\"\"),试运行时无法执行两项表达式,请在任务正式调用阶段测试是否有效。", "Field(\"\") or eval(\"\") is detected in JavaScript, and the two expressions cannot be executed during trial operation. Please test whether it is valid in the formal call stage.", "warning");
|
||||
}
|
||||
}
|
||||
return outcome;
|
||||
}
|
||||
|
||||
function notify_flowchart(msg_zh, msg_en, level = "info") {
|
||||
@ -628,25 +779,41 @@ wss.on('connection', function (ws) {
|
||||
// });
|
||||
console.log("set socket_flowchart at time: ", new Date());
|
||||
} else { //其他的ID是用来标识不同的浏览器标签页的
|
||||
await new Promise(resolve => setTimeout(resolve, 2300));
|
||||
// await new Promise(resolve => setTimeout(resolve, 200));
|
||||
let handles = await driver.getAllWindowHandles();
|
||||
if (arrayDifference(handles, old_handles).length > 0) {
|
||||
old_handles = handles;
|
||||
current_handle = handles[handles.length - 1];
|
||||
await driver.switchTo().window(current_handle);
|
||||
console.log("New tab opened, change current_handle to: ", current_handle);
|
||||
// 调整浏览器窗口大小,不然扩展会白屏
|
||||
let size = await driver.manage().window().getRect();
|
||||
let width = size.width;
|
||||
let height = size.height;
|
||||
await driver.manage().window().setRect({width: width, height: height + 10});
|
||||
// height = height - 1;
|
||||
await driver.manage().window().setRect({width: width, height: height});
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
handle_pairs[msg.message.id] = current_handle;
|
||||
console.log("Set handle_pair for id: ", msg.message.id, " to ", current_handle, ", title is: ", msg.message.title);
|
||||
socket_flowchart.send(JSON.stringify({"type": "title", "data": {"title": msg.message.title}}));
|
||||
allWindowSockets.push(ws);
|
||||
allWindowScoketNames.push(msg.message.id);
|
||||
console.log("set socket for id: ", msg.message.id, " at time: ", new Date());
|
||||
ws.on('close', function (event) {
|
||||
ws.on('close', async function (event) {
|
||||
let index = allWindowSockets.indexOf(ws);
|
||||
if (index > -1) {
|
||||
allWindowSockets.splice(index, 1);
|
||||
allWindowScoketNames.splice(index, 1);
|
||||
}
|
||||
let handles = await driver.getAllWindowHandles();
|
||||
if (handles.length < old_handles.length) {
|
||||
old_handles = handles;
|
||||
current_handle = handles[handles.length - 1];
|
||||
await driver.switchTo().window(current_handle);
|
||||
console.log("Current tab closed, change current_handle to: ", current_handle);
|
||||
}
|
||||
console.log("socket for id: ", msg.message.id, " closed at time: ", new Date());
|
||||
});
|
||||
// console.log("handle_pairs: ", handle_pairs);
|
||||
@ -671,6 +838,15 @@ async function runBrowser(lang = "en", user_data_folder = '', mobile = false) {
|
||||
const serviceBuilder = new ServiceBuilder(driverPath);
|
||||
let options = new chrome.Options();
|
||||
options.addArguments('--disable-blink-features=AutomationControlled');
|
||||
options.addArguments('--disable-infobars');
|
||||
// 添加实验性选项以排除'enable-automation'开关
|
||||
options.set('excludeSwitches', ['enable-automation']);
|
||||
options.excludeSwitches("enable-automation")
|
||||
|
||||
// 添加实验性选项来禁用自动化扩展
|
||||
options.set('useAutomationExtension', false);
|
||||
// options.addArguments('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36');
|
||||
options.set
|
||||
language = lang;
|
||||
if (lang == "en") {
|
||||
options.addExtensions(path.join(__dirname, "EasySpider_en.crx"));
|
||||
@ -679,6 +855,7 @@ async function runBrowser(lang = "en", user_data_folder = '', mobile = false) {
|
||||
}
|
||||
options.addExtensions(path.join(__dirname, "XPathHelper.crx"));
|
||||
options.setChromeBinaryPath(chromeBinaryPath);
|
||||
|
||||
if (user_data_folder != "") {
|
||||
let dir = path.join(task_server.getDir(), user_data_folder);
|
||||
console.log(dir);
|
||||
@ -790,7 +967,7 @@ app.whenReady().then(() => {
|
||||
createWindow();
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// On MacOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
|
@ -221,7 +221,8 @@
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
margin-bottom: 0px;
|
||||
background-color: azure;
|
||||
/*background-color: azure;*/
|
||||
background-color: aliceblue;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
|
@ -113,10 +113,10 @@
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['maxWaitTime']" type="number" required></input>
|
||||
<label>After executed, whether scroll down:</label>
|
||||
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
|
||||
<option :vaule = 0>No scrolling</option>
|
||||
<option :vaule = 1>Scroll one screen</option>
|
||||
<option :vaule = 2>Scroll to the end</option>
|
||||
<option :vaule = 3>Keep scrolling until the page data does not change</option>
|
||||
<option :value = 0>No scrolling</option>
|
||||
<option :value = 1>Scroll one screen</option>
|
||||
<option :value = 2>Scroll to the end</option>
|
||||
<option :value = 3>Keep scrolling until the page data does not change</option>
|
||||
</select>
|
||||
<label>Scroll Times (the wait time after scrolling <b>ineffective</b> when the scrolling type is set to <b>no scrolling</b>):</label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
|
||||
@ -157,12 +157,17 @@
|
||||
<option :value = 0>Selenium</option>
|
||||
<option :value = 1>JavaScript</option>
|
||||
</select>
|
||||
<label>Open link in new tab:</label>
|
||||
<select v-model='nowNode["parameters"]["newTab"]' class="form-control">
|
||||
<option :value = 1>Yes</option>
|
||||
<option :value = 0>No</option>
|
||||
</select>
|
||||
<label>Whether to scroll down after clicking:</label>
|
||||
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
|
||||
<option :vaule = 0>No Scrolling</option>
|
||||
<option :vaule = 1>Scroll one screen</option>
|
||||
<option :vaule = 2>Scroll to the end</option>
|
||||
<option :vaule = 3>Keep scrolling until the page data does not change</option>
|
||||
<option :value = 0>No Scrolling</option>
|
||||
<option :value = 1>Scroll one screen</option>
|
||||
<option :value = 2>Scroll to the end</option>
|
||||
<option :value = 3>Keep scrolling until the page data does not change</option>
|
||||
</select>
|
||||
<label>Scroll Times:</label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
|
||||
@ -218,10 +223,10 @@
|
||||
</td>
|
||||
<td style="width:200px">{{paras.parameters[i-1]["exampleValues"][0]["value"]}}</td>
|
||||
<td>
|
||||
<a v-on:mousedown="modifyParas(i-1)">Modify</a>
|
||||
<a v-on:mousedown="deleteParas(i-1)">Delete</a>
|
||||
<a v-on:mousedown="upParas(i-1)">Up</a>
|
||||
<a v-on:mousedown="downParas(i-1)">Down</a>
|
||||
<a v-on:mousedown="modifyPara(i-1)">Modify</a>
|
||||
<a v-on:mousedown="deletePara(i-1)">Delete</a>
|
||||
<a v-on:mousedown="upPara(i-1)">Up</a>
|
||||
<a v-on:mousedown="downPara(i-1)">Down</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -241,6 +246,7 @@
|
||||
</p>
|
||||
<div :class="{collapse: true, 'show': paras.parameters[paraIndex]['beforeJS'].length!=0 || paras.parameters[paraIndex]['afterJS'].length!=0}" id="elementAdvanced">
|
||||
<div>
|
||||
<div><a href="#" v-on:mousedown="trailPara(paraIndex)" style="text-decoration: none">Trail Run</a></div>
|
||||
<label>Execute a JavaScript script <strong>before</strong> extracting data from this element: </label>
|
||||
<textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
|
||||
placeholder='The element should be represented by arguments[0]. Here is an example JavaScript code: arguments[0].innerText = arguments[0].innerText.replace("United States","US"). This code replaces occurrences of "United States" with "US" in the text of the element. Subsequently, when extracting data, you will obtain the replaced value.' v-model='paras.parameters[paraIndex]["beforeJS"]'></textarea>
|
||||
@ -414,8 +420,8 @@ This option is an advanced feature that allows directly returning the expression
|
||||
Please note that this feature does not support assigning values to variables. In other words, you cannot write something like `self.myVar = 1`. If you want to perform assignment operations, please select the previous option, "Run Python code on current environment (the "exec" operation)"</pre>
|
||||
<p style="margin-top: 15px">Whether to record the output/return value of the execution as a field: </p>
|
||||
<p><select v-model='nowNode["parameters"]["recordASField"]' class="form-control">
|
||||
<option :vaule = 0>No</option>
|
||||
<option :vaule = 1>Yes</option>
|
||||
<option :value = 0>No</option>
|
||||
<option :value = 1>Yes</option>
|
||||
</select></p>
|
||||
<p><label>Convert parameter type to:</label>
|
||||
<select v-model='nowNode["parameters"]["paraType"]' class="form-control">
|
||||
@ -490,10 +496,10 @@ Please note that this feature does not support assigning values to variables. In
|
||||
<div v-if="!useLoop">
|
||||
<p>Option switch Mode</p>
|
||||
<select class="form-control" v-model='nowNode["parameters"]["optionMode"]'>
|
||||
<option :vaule = 0>Switch to the next option</option>
|
||||
<option :vaule = 1>Switch options by index (0 is the first option)</option>
|
||||
<option :vaule = 2>Switch options by option value</option>
|
||||
<option :vaule = 3>Switch options by option text</option>
|
||||
<option :value = 0>Switch to the next option</option>
|
||||
<option :value = 1>Switch options by index (0 is the first option)</option>
|
||||
<option :value = 2>Switch options by option value</option>
|
||||
<option :value = 3>Switch options by option text</option>
|
||||
</select>
|
||||
<p>Set value (not applicable for "Switch to the next option" mode)</p>
|
||||
<input class="form-control" id="selectValue" v-model='nowNode["parameters"]["optionValue"]' autoFocus="autofocus" type="text"></input>
|
||||
@ -519,7 +525,7 @@ Please note that this feature does not support assigning values to variables. In
|
||||
<p><input onkeydown="inputDelete(event)" type="checkbox" v-model='nowNode["parameters"]["iframe"]'></input>Operation is in iframe</p>
|
||||
<!-- 循环选项 -->
|
||||
<label>Loop Type:</label>
|
||||
<select v-model='loopType' class="form-control">
|
||||
<select v-model='loopType' class="form-control" @change="handleLoopTypeChange">
|
||||
<option :value = 0>Single Element</option>
|
||||
<option :value = 1>Unfixed Element List</option>
|
||||
<option :value = 2>Fixed Element List</option>
|
||||
@ -568,9 +574,9 @@ If the expression returns a value greater than 0 or evaluates to True, the loop
|
||||
<div>
|
||||
<p><label>(Advanced Operation) Define loop exit condition using code/script; or you can add a <b>Custom Action</b>, then select the "Exit Loop" option:</label></p>
|
||||
<select v-model='nowNode["parameters"]["breakMode"]' class="form-control" style="font-weight: bold">
|
||||
<option :vaule = 0>Do not set script (even if a script is written below, it will not be executed)</option>
|
||||
<option :vaule = 1>JavaScript script (start with 'return ')</option>
|
||||
<option :vaule = 2>Operating system-level command</option>
|
||||
<option :value = 0>Do not set script (even if a script is written below, it will not be executed)</option>
|
||||
<option :value = 1>JavaScript script (start with 'return ')</option>
|
||||
<option :value = 2>Operating system-level command</option>
|
||||
</select>
|
||||
<div>
|
||||
<textarea style="margin-top: 10px" onkeydown="inputDelete(event)" class="form-control" rows="2"
|
||||
@ -584,10 +590,10 @@ If the expression returns a value greater than 0 or evaluates to True, the loop
|
||||
<input onkeydown="inputDelete(event)" required type="number" class="form-control" v-model.number='list.nl[index.nowNodeIndex]["parameters"]["historyWait"]'></input>
|
||||
<label>After executed, whether scroll down:</label>
|
||||
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
|
||||
<option :vaule = 0>No Scrolling</option>
|
||||
<option :vaule = 1>Scroll one screen</option>
|
||||
<option :vaule = 2>Scroll to the end</option>
|
||||
<option :vaule = 3>Keep scrolling until the page data does not change</option>
|
||||
<option :value = 0>No Scrolling</option>
|
||||
<option :value = 1>Scroll one screen</option>
|
||||
<option :value = 2>Scroll to the end</option>
|
||||
<option :value = 3>Keep scrolling until the page data does not change</option>
|
||||
</select>
|
||||
<label>Scroll Times:</label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
|
||||
@ -596,14 +602,14 @@ If the expression returns a value greater than 0 or evaluates to True, the loop
|
||||
</div>
|
||||
|
||||
<div class="elements" v-if="nodeType==9">
|
||||
<label>The conditions are evaluated from left to right, which means if the condition in the leftmost branch is satisfied, the operations within that branch are executed. Otherwise, the condition in the next branch from left to right is evaluated, and so on.</label>
|
||||
<label>The conditions are evaluated from left to right, which means if the condition in the leftmost branch is satisfied, the operations within that branch are executed. Otherwise, the condition in the next branch from left to right is evaluated, and so on. Clicking on a branch while designing tasks allows for <b>dynamic debugging</b> in the browser to verify if the branch satisfies the condition (not applicable to system commands and Python Eval operations). </label>
|
||||
</div>
|
||||
|
||||
<div class="elements" v-if="nodeType==10">
|
||||
<label>The conditions are evaluated from left to right, which means if the condition in the leftmost branch is satisfied, the operations within that branch are executed. Otherwise, the condition in the next branch from left to right is evaluated, and so on.</label>
|
||||
<label>The conditions are evaluated from left to right, which means if the condition in the leftmost branch is satisfied, the operations within that branch are executed. Otherwise, the condition in the next branch from left to right is evaluated, and so on. Clicking on a branch while designing tasks allows for <b>dynamic debugging</b> in the browser to verify if the branch satisfies the condition (not applicable to system commands and Python Eval operations). </label>
|
||||
<p><input onkeydown="inputDelete(event)" type="checkbox" v-model='nowNode["parameters"]["iframe"]'></input>Operation is in iframe</p>
|
||||
<label>Condition Type:</label>
|
||||
<select v-model='TClass' class="form-control">
|
||||
<select v-model='TClass' class="form-control" @change="handleJudgeTypeChange">
|
||||
<option :value = 0>No Condition</option>
|
||||
<option :value = 1>Text inside current page</option>
|
||||
<option :value = 2>Element inside current page</option>
|
||||
@ -653,8 +659,8 @@ If the expression returns a value greater than 0 or evaluates to True, the opera
|
||||
<input onkeydown="inputDelete(event)" required type="number" class="form-control" v-model.number='list.nl[index.nowNodeIndex]["parameters"]["wait"]'></input>
|
||||
<label>Wait Type</label>
|
||||
<select v-model='list.nl[index.nowNodeIndex]["parameters"]["waitType"]' class="form-control">
|
||||
<option :vaule = 0>Fixed wait (set to wait for 10 seconds then it will wait for 10 seconds)</option>
|
||||
<option :vaule = 1>Random wait (set to wait for 10 seconds then it will randomly wait for 10 × 0.5 - 10 × 1.5 seconds)</option>
|
||||
<option :value = 0>Fixed wait (set to wait for 10 seconds then it will wait for 10 seconds)</option>
|
||||
<option :value = 1>Random wait (set to wait for 10 seconds then it will randomly wait for 10 × 0.5 - 10 × 1.5 seconds)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -92,12 +92,12 @@ let app = new Vue({
|
||||
},
|
||||
loopType: { //循环类型发生变化的时候更新参数值
|
||||
handler: function (newVal, oldVal) {
|
||||
this.nowNode["parameters"]["loopType"] = newVal;
|
||||
// this.nowNode["parameters"]["loopType"] = newVal;
|
||||
}
|
||||
},
|
||||
TClass: {
|
||||
handler: function (newVal, oldVal) {
|
||||
this.nowNode["parameters"]["class"] = newVal;
|
||||
// this.nowNode["parameters"]["class"] = newVal;
|
||||
}
|
||||
},
|
||||
useLoop: {
|
||||
@ -122,7 +122,7 @@ let app = new Vue({
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCodeModeChange: function (value) {
|
||||
handleCodeModeChange: function () {
|
||||
// if (this.codeMode == undefined || this.codeMode == null || this.codeMode == -1) {
|
||||
// return;
|
||||
// }
|
||||
@ -166,6 +166,73 @@ let app = new Vue({
|
||||
break;
|
||||
}
|
||||
},
|
||||
handleLoopTypeChange: function () {
|
||||
this.nowNode["parameters"]["loopType"] = this.loopType;
|
||||
switch (parseInt(this.loopType)) {
|
||||
case 0:
|
||||
this.nowNode["title"] = LANG("循环 - 单个元素", "Loop - Single Element");
|
||||
break;
|
||||
case 1:
|
||||
this.nowNode["title"] = LANG("循环 - 不固定元素列表", "Loop - Dynamic Element List");
|
||||
break;
|
||||
case 2:
|
||||
this.nowNode["title"] = LANG("循环 - 固定元素列表", "Loop - Fixed Element List");
|
||||
break;
|
||||
case 3:
|
||||
this.nowNode["title"] = LANG("循环 - 文本列表", "Loop - Text List");
|
||||
break;
|
||||
case 4:
|
||||
this.nowNode["title"] = LANG("循环 - 网址列表", "Loop - URL List");
|
||||
break;
|
||||
case 5:
|
||||
this.nowNode["title"] = LANG("循环 - JavaScript命令返回值", "Loop - JavaScript Command Return Value");
|
||||
break;
|
||||
case 6:
|
||||
this.nowNode["title"] = LANG("循环 - 系统命令返回值", "Loop - OS Command Return Value");
|
||||
break;
|
||||
case 7:
|
||||
this.nowNode["title"] = LANG("循环 - Python表达式返回值", "Loop - Python Expression Evaluation Value");
|
||||
break;
|
||||
default:
|
||||
this.nowNode["title"] = LANG("循环", "Loop");
|
||||
break;
|
||||
}
|
||||
},
|
||||
handleJudgeTypeChange: function () {
|
||||
this.nowNode["parameters"]["class"] = this.TClass;
|
||||
switch (parseInt(this.TClass)) {
|
||||
case 0:
|
||||
this.nowNode["title"] = LANG("无条件", "No Condition");
|
||||
break;
|
||||
case 1:
|
||||
this.nowNode["title"] = LANG("当前页面包含文本", "Current Page Contains Text");
|
||||
break;
|
||||
case 2:
|
||||
this.nowNode["title"] = LANG("当前页面包含元素", "Current Page Contains Element");
|
||||
break;
|
||||
case 3:
|
||||
this.nowNode["title"] = LANG("当前循环项包含文本", "Current Loop Item Contains Text");
|
||||
break;
|
||||
case 4:
|
||||
this.nowNode["title"] = LANG("当前循环项包含元素", "Current Loop Item Contains Element");
|
||||
break;
|
||||
case 5:
|
||||
this.nowNode["title"] = LANG("JavaScript命令返回值", "JavaScript Command Return Value");
|
||||
break;
|
||||
case 6:
|
||||
this.nowNode["title"] = LANG("系统命令返回值", "OS Command Return Value");
|
||||
break;
|
||||
case 7:
|
||||
this.nowNode["title"] = LANG("针对当前循环项的JavaScript命令返回值", "JavaScript Command Return Value for Current Loop Item");
|
||||
break;
|
||||
case 8:
|
||||
this.nowNode["title"] = LANG("执行环境下的Python表达式值", "Python Expression Evaluation Value");
|
||||
break;
|
||||
default:
|
||||
this.nowNode["title"] = LANG("条件分支", "Condition");
|
||||
break;
|
||||
}
|
||||
},
|
||||
getCookies: function () { //获取cookies
|
||||
let command = new WebSocket("ws://localhost:" + getUrlParam("wsport"))
|
||||
command.onopen = function () {
|
||||
@ -216,30 +283,38 @@ let app = new Vue({
|
||||
$("#app > div.elements > div.toolkitcontain > table.toolkittb4 > tbody > tr:last-child")[0].scrollIntoView(false); //滚动到底部
|
||||
}, 200);
|
||||
},
|
||||
modifyParas: function (i) { //修改第i个参数
|
||||
modifyPara: function (i) { //修改第i个参数
|
||||
this.paraIndex = i;
|
||||
console.log(this.paras);
|
||||
let clone_node = DeepClone(this.nowNode);
|
||||
clone_node.option = 11; //单独的提取数据参数节点
|
||||
clone_node.parameters.index = i;
|
||||
trailElement(clone_node, 0);
|
||||
},
|
||||
deleteParas: function (i) { //删除第i个参数
|
||||
trailPara: function (i) { //试运行第i个参数
|
||||
let clone_node = DeepClone(this.nowNode);
|
||||
clone_node.option = 11; //单独的提取数据参数节点
|
||||
clone_node.parameters.index = i;
|
||||
trailElement(clone_node, 1);
|
||||
},
|
||||
deletePara: function (i) { //删除第i个参数
|
||||
this.nowNode["parameters"]["paras"].splice(i, 1);
|
||||
//如果参数删除完了,就把提取数据也删掉
|
||||
if (this.nowNode["parameters"]["paras"].length == 0) {
|
||||
deleteElement();
|
||||
}
|
||||
},
|
||||
upParas: function (i) { //上移第i个参数
|
||||
upPara: function (i) { //上移第i个参数
|
||||
if (i != 0) {
|
||||
let t = this.nowNode["parameters"]["paras"].splice(i, 1)[0];
|
||||
this.nowNode["parameters"]["paras"].splice(i - 1, 0, t);
|
||||
}
|
||||
},
|
||||
downParas: function (i) { //下移第i个参数
|
||||
downPara: function (i) { //下移第i个参数
|
||||
if (i != this.nowNode["parameters"]["paras"].length - 1) {
|
||||
let t = this.nowNode["parameters"]["paras"].splice(i, 1)[0];
|
||||
this.nowNode["parameters"]["paras"].splice(i + 1, 0, t);
|
||||
}
|
||||
},
|
||||
|
||||
getType: function (nodeType, contentType) { //根据类型得到字段名称
|
||||
if (contentType == 2) {
|
||||
return "InnerHTML";
|
||||
@ -283,20 +358,20 @@ function newNode(node) {
|
||||
} else if (type == 1) //循环
|
||||
{
|
||||
return `<div class="loop clk" data="${id}" draggable="true" dataType=${type} id = "${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<p style="background:#d6d6d6;text-align:left;padding:2px">${title}</p>
|
||||
<p style="background:#d6d6d6;text-align:left;padding: 2px 2px 2px 5px">${title}</p>
|
||||
<p class="arrow" draggable="true" position=-1 data = "${id}" pId=${id}>↓</p>
|
||||
</div>
|
||||
<p class="arrow" draggable="true" data = "${id}" position=${node["position"]} pId=${node["parentId"]}>↓</p></div>`;
|
||||
} else if (type == 2) //判断
|
||||
{
|
||||
return LANG(`<div class="loop clk" draggable="true" dataType=${type} data="${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<p style="background:#d6d6d6;text-align:left;padding:2px">${title}</p>
|
||||
<p style="background:#d6d6d6;text-align:left;padding: 2px 2px 2px 5px">${title}</p>
|
||||
<p class="branchAdd" data="${id}">点击此处在最右边增加条件分支</p>
|
||||
<div class="judge" id = "${id}">
|
||||
</div></div>
|
||||
<p class="arrow" draggable="true" data = "${id}" position=${node["position"]} pId=${node["parentId"]}>↓</p></div>`,
|
||||
`<div class="loop clk" draggable="true" dataType=${type} data="${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<p style="background:#d6d6d6;text-align:left;padding:2px">${title}</p>
|
||||
<p style="background:#d6d6d6;text-align:left;padding: 2px 2px 2px 5px">${title}</p>
|
||||
<p class="branchAdd" data="${id}">Click here to add a new condition to the right most</p>
|
||||
<div class="judge" id = "${id}">
|
||||
</div></div>
|
||||
@ -304,7 +379,7 @@ function newNode(node) {
|
||||
} else //判断分支
|
||||
{
|
||||
return `<div class="branch clk" dataType=${type} data="${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<p style="background:#d6d6d6;text-align:left;padding:2px">${title}</p>
|
||||
<p style="background:#d6d6d6;text-align:left;padding: 2px 2px 2px 5px">${title}</p>
|
||||
<p data = "${id}" class="arrow" draggable="true" position=-1 pId=${id}>↓</p>
|
||||
<div id = "${id}">
|
||||
</div></div>`;
|
||||
@ -346,27 +421,7 @@ function branchClick(e) {
|
||||
e.stopPropagation(); //防止冒泡
|
||||
}
|
||||
|
||||
function elementMousedown(e) {
|
||||
if (e.button == 2) //右键点击
|
||||
{
|
||||
try {
|
||||
document.getElementById("contextMenu").remove();
|
||||
} catch {
|
||||
|
||||
}
|
||||
if (nowNode != null) {
|
||||
nowNode.style.borderColor = "skyblue";
|
||||
}
|
||||
nowNode = this;
|
||||
vueData.nowNodeIndex = actionSequence[this.getAttribute("data")];
|
||||
this.style.borderColor = "blue";
|
||||
handleElement(); //处理元素
|
||||
}
|
||||
e.stopPropagation(); //防止冒泡
|
||||
}
|
||||
|
||||
//元素点击事件
|
||||
function elementClick(e) {
|
||||
function operationChange(e, theNode) {
|
||||
try {
|
||||
document.getElementById("contextMenu").remove();
|
||||
} catch (e) {
|
||||
@ -375,23 +430,40 @@ function elementClick(e) {
|
||||
if (nowNode != null) {
|
||||
nowNode.style.borderColor = "skyblue";
|
||||
}
|
||||
nowNode = this;
|
||||
vueData.nowNodeIndex = actionSequence[this.getAttribute("data")];
|
||||
this.style.borderColor = "blue";
|
||||
nowNode = theNode
|
||||
vueData.nowNodeIndex = actionSequence[theNode.getAttribute("data")];
|
||||
theNode.style.borderColor = "blue";
|
||||
handleElement(); //处理元素
|
||||
trailElement(app._data.nowNode, 0);
|
||||
e.stopPropagation(); //防止冒泡
|
||||
}
|
||||
|
||||
function elementMousedown(e) {
|
||||
if (e.button == 2) //右键点击
|
||||
{
|
||||
operationChange(e, this);
|
||||
}
|
||||
e.stopPropagation(); //防止冒泡
|
||||
}
|
||||
|
||||
//元素点击事件
|
||||
function elementClick(e) {
|
||||
operationChange(e, this);
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
function elementDblClick(e) {
|
||||
try {
|
||||
let nodeType = app._data.nowNode["option"]
|
||||
if (nodeType >= 8) {
|
||||
showInfo(LANG("试运行功能不适用于循环和条件分支操作,请试运行循环或条件分支内部的具体操作,如点击元素。", "The trial run function is not applicable to loop and condition branch operations. Please try to run the specific operations in the loop/condition branch, such as clicking elements."));
|
||||
if (nodeType == 8) {
|
||||
showInfo(LANG("试运行功能不适用于循环操作,请试运行循环内部的具体操作,如点击元素。", "The trial run function is not applicable to loop operations. Please try to run the specific operations in the loop, such as clicking elements."));
|
||||
}
|
||||
} else {
|
||||
if(nodeType == 5 && app._data.nowNode["parameters"]["codeMode"] != 0){
|
||||
showInfo(LANG("试运行自定义操作功能只适用于执行JavaScript操作。", "The trial run custom action function is only applicable to run JavaScript operation."))
|
||||
if (nodeType == 5 && (app._data.nowNode["parameters"]["codeMode"] != 0 && app._data.nowNode["parameters"]["codeMode"] != 8)) {
|
||||
showInfo(LANG("试运行自定义操作功能只适用于执行JavaScript和刷新页面操作。", "The trial run custom action function is only applicable to run JavaScript and refresh page operations."));
|
||||
} else {
|
||||
trailRun(app._data.nowNode);
|
||||
trailElement(app._data.nowNode, 1);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@ -513,7 +585,7 @@ function toolBoxKernel(e, para = null) {
|
||||
if (str == "") {
|
||||
title += LANG("元素", "Element");
|
||||
} else {
|
||||
if(window.location.href.indexOf("_CN") != -1){
|
||||
if (window.location.href.indexOf("_CN") != -1) {
|
||||
if (str.length > l) {
|
||||
str = str.substring(0, l) + "...";
|
||||
}
|
||||
@ -549,7 +621,7 @@ function toolBoxKernel(e, para = null) {
|
||||
index: l + 1,
|
||||
type: 3,
|
||||
option: 10,
|
||||
title: LANG("条件分支1", "Condition 1"),
|
||||
title: LANG("无条件", "No Condition"),
|
||||
sequence: [],
|
||||
isInLoop: false,
|
||||
};
|
||||
@ -559,7 +631,7 @@ function toolBoxKernel(e, para = null) {
|
||||
index: l + 2,
|
||||
type: 3,
|
||||
option: 10,
|
||||
title: LANG("条件分支2", "Condition 2"),
|
||||
title: LANG("无条件", "No Condition"),
|
||||
sequence: [],
|
||||
isInLoop: false,
|
||||
};
|
||||
|
@ -157,13 +157,18 @@
|
||||
<option :value = 0>Selenium点击</option>
|
||||
<option :value = 1>JavaScript点击</option>
|
||||
</select>
|
||||
<label>在新标签页打开超链接:</label>
|
||||
<select v-model='nowNode["parameters"]["newTab"]' class="form-control">
|
||||
<option :value = 1>是</option>
|
||||
<option :value = 0>否</option>
|
||||
</select>
|
||||
<label>点击后是否向下滚动页面:</label>
|
||||
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
|
||||
<option :vaule = 0>不滚动</option>
|
||||
<option :vaule = 1>向下滚动一屏</option>
|
||||
<option :vaule = 2>滚动到底部</option>
|
||||
<option :vaule = 3>一直滚动直到页面内容无变化(需设置好滚动后的等待时间用于检测页面变化)</option>
|
||||
</select>
|
||||
<option :value = 0>不滚动</option>
|
||||
<option :value = 1>向下滚动一屏</option>
|
||||
<option :value = 2>滚动到底部</option>
|
||||
<option :value = 3>一直滚动直到页面内容无变化(需设置好滚动后的等待时间用于检测页面变化)</option>
|
||||
</select>
|
||||
<label>滚动次数(滚动类型设置为<b>不滚动</b>或<b>一直滚动</b>时请忽略此项):</label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
|
||||
<label>滚动后等待时间(秒):</label>
|
||||
@ -218,10 +223,10 @@
|
||||
</td>
|
||||
<td style="width:200px">{{paras.parameters[i-1]["exampleValues"][0]["value"]}}</td>
|
||||
<td>
|
||||
<a v-on:mousedown="modifyParas(i-1)">修改</a>
|
||||
<a v-on:mousedown="deleteParas(i-1)">删除</a>
|
||||
<a v-on:mousedown="upParas(i-1)">上移</a>
|
||||
<a v-on:mousedown="downParas(i-1)">下移</a>
|
||||
<a v-on:mousedown="modifyPara(i-1)">修改</a>
|
||||
<a v-on:mousedown="deletePara(i-1)">删除</a>
|
||||
<a v-on:mousedown="upPara(i-1)">上移</a>
|
||||
<a v-on:mousedown="downPara(i-1)">下移</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -241,6 +246,7 @@
|
||||
</p>
|
||||
<div :class="{collapse: true, 'show': paras.parameters[paraIndex]['beforeJS'].length!=0 || paras.parameters[paraIndex]['afterJS'].length!=0}" id="elementAdvanced">
|
||||
<div>
|
||||
<div><a href="#" v-on:mousedown="trailPara(paraIndex)" style="text-decoration: none">试运行</a></div>
|
||||
<label>提取该元素数据<strong>前</strong>针对该元素执行一段JavaScript脚本: </label>
|
||||
<textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
|
||||
placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].innerText = arguments[0].innerText.replace("上海","Shanghai")即实现了将元素文字中的“上海”替换成”Shanghai“的功能,然后后续如提取数据时就会提取到替换后的值。' v-model='paras.parameters[paraIndex]["beforeJS"]'></textarea>
|
||||
@ -519,7 +525,7 @@ print(emotlib.emoji()) # 使用其中的函数。
|
||||
<p><input onkeydown="inputDelete(event)" type="checkbox" v-model='nowNode["parameters"]["iframe"]'></input>在iframe内操作</p>
|
||||
<!-- 循环选项 -->
|
||||
<label>循环类型:</label>
|
||||
<select v-model='loopType' class="form-control">
|
||||
<select v-model='loopType' class="form-control" @change="handleLoopTypeChange">
|
||||
<option :value = 0>单个元素(多用于循环点击下一页)</option>
|
||||
<option :value = 1>不固定元素列表</option>
|
||||
<option :value = 2>固定元素列表</option>
|
||||
@ -596,14 +602,14 @@ print(emotlib.emoji()) # 使用其中的函数。
|
||||
</div>
|
||||
|
||||
<div class="elements" v-if="nodeType==9">
|
||||
<label>判断条件是从左往右判断的,即如果最左边的条件分支的判断条件满足,则执行最左边分支内的操作,否则判断从左向右第二个分支的条件是否满足,以此类推。</label>
|
||||
<label>判断条件是从左往右判断的,即如果最左边的条件分支的判断条件满足,则执行最左边分支内的操作,否则判断从左向右第二个分支的条件是否满足,以此类推。设计任务时点击分支即可在浏览器中<b>动态调试</b>分支是否满足(不适用于系统命令和Python Eval操作)。</label>
|
||||
</div>
|
||||
|
||||
<div class="elements" v-if="nodeType==10">
|
||||
<label>判断条件是从左往右判断的,即如果最左边的条件分支的判断条件满足,则执行最左边分支内的操作,否则判断从左向右第二个分支的条件是否满足,以此类推。</label>
|
||||
<label>判断条件是从左往右判断的,即如果最左边的条件分支的判断条件满足,则执行最左边分支内的操作,否则判断从左向右第二个分支的条件是否满足,以此类推。设计任务时点击分支即可在浏览器中<b>动态调试</b>分支是否满足(不适用于系统命令和Python Eval操作)。</label>
|
||||
<p><input onkeydown="inputDelete(event)" type="checkbox" v-model='nowNode["parameters"]["iframe"]'></input>在iframe内操作</p>
|
||||
<label>条件类型:</label>
|
||||
<select v-model='TClass' class="form-control">
|
||||
<select v-model='TClass' class="form-control" @change="handleJudgeTypeChange">
|
||||
<option :value = 0>无条件</option>
|
||||
<option :value = 1>当前页面包括文本</option>
|
||||
<option :value = 2>当前页面包括元素</option>
|
||||
|
@ -8,8 +8,8 @@ let exampleMsg = { //示例消息
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify(exampleMsg));
|
||||
let ws = new WebSocket("ws://localhost:"+getUrlParam("wsport"));
|
||||
ws.onopen = function() {
|
||||
let ws = new WebSocket("ws://localhost:" + getUrlParam("wsport"));
|
||||
ws.onopen = function () {
|
||||
// Web Socket 已连接上,使用 send() 方法发送数据
|
||||
console.log("已连接");
|
||||
message = {
|
||||
@ -20,12 +20,12 @@ ws.onopen = function() {
|
||||
};
|
||||
this.send(JSON.stringify(message));
|
||||
};
|
||||
ws.onclose = function() {
|
||||
ws.onclose = function () {
|
||||
// 关闭 websocket
|
||||
console.log("连接已关闭...");
|
||||
};
|
||||
let old_title = "";
|
||||
ws.onmessage = function(evt) {
|
||||
ws.onmessage = function (evt) {
|
||||
evt = JSON.parse(evt.data);
|
||||
console.log(evt);
|
||||
if (evt["type"] == "title") { //如果不是特殊处理的话,默认全部是增加元素操作
|
||||
@ -36,9 +36,9 @@ ws.onmessage = function(evt) {
|
||||
} else if (evt["type"] == "notify") {
|
||||
if (evt["level"] == "success") {
|
||||
showSuccess(LANG(evt["msg_zh"], evt["msg_en"]));
|
||||
} else if(evt["level"] == "info"){
|
||||
} else if (evt["level"] == "info") {
|
||||
showInfo(LANG(evt["msg_zh"], evt["msg_en"]));
|
||||
} else if(evt["level"] == "error"){
|
||||
} else if (evt["level"] == "error") {
|
||||
showError(LANG(evt["msg_zh"], evt["msg_en"]));
|
||||
}
|
||||
} else {
|
||||
@ -46,20 +46,20 @@ ws.onmessage = function(evt) {
|
||||
}
|
||||
};
|
||||
|
||||
function changeOutputFormat(para){
|
||||
try{
|
||||
for(let i=0;i<para["parameters"].length;i++) {
|
||||
function changeOutputFormat(para) {
|
||||
try {
|
||||
for (let i = 0; i < para["parameters"].length; i++) {
|
||||
let exampleValue = para["parameters"][i]["exampleValues"][0]["value"];
|
||||
let len = exampleValue.length;
|
||||
if (len > 20000) {
|
||||
if($("#outputFormat").val() == "xlsx") {
|
||||
if ($("#outputFormat").val() == "xlsx") {
|
||||
$("#outputFormat").val("csv"); //如果有一个参数的示例值长度超过20000,就默认输出为csv
|
||||
showInfo(LANG("示例值长度超过16000,超出Excel单个单元格存储限制,已自动切换保存为csv格式。", "The length of the example value exceeds 16000, and the csv save format has been automatically switched."));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch(e){
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
@ -85,7 +85,10 @@ function handleAddElement(msg) {
|
||||
addElement(2, msg);
|
||||
} else if (msg["type"] == "inputText") {
|
||||
addElement(4, msg);
|
||||
} else if (msg["type"] == "changeOption"){
|
||||
} else if (msg["type"] == "batchInputText") {
|
||||
addElement(8, msg);
|
||||
addElement(4, msg);
|
||||
} else if (msg["type"] == "changeOption") {
|
||||
addElement(6, msg);
|
||||
} else if (msg["type"] == "mouseMove") {
|
||||
addElement(7, msg);
|
||||
@ -110,7 +113,8 @@ function handleAddElement(msg) {
|
||||
}
|
||||
changeOutputFormat(msg);
|
||||
app._data.paras.parameters = app._data["nowNode"]["parameters"]["paras"];
|
||||
setTimeout(function(){$("#app > div.elements > div.toolkitcontain > table.toolkittb4 > tbody > tr:last-child")[0].scrollIntoView(false); //滚动到底部
|
||||
setTimeout(function () {
|
||||
$("#app > div.elements > div.toolkitcontain > table.toolkittb4 > tbody > tr:last-child")[0].scrollIntoView(false); //滚动到底部
|
||||
}, 200);
|
||||
} else {
|
||||
addElement(3, msg);
|
||||
@ -122,9 +126,9 @@ function handleAddElement(msg) {
|
||||
addElement(3, msg);
|
||||
changeOutputFormat(msg);
|
||||
notifyParameterNum(msg["parameters"].length); //通知浏览器端参数的个数变化
|
||||
} else if(msg["type"] == "GetCookies"){
|
||||
for(let node of nodeList){
|
||||
if(node["option"] == 1){
|
||||
} else if (msg["type"] == "GetCookies") {
|
||||
for (let node of nodeList) {
|
||||
if (node["option"] == 1) {
|
||||
node["parameters"]["cookies"] = msg["message"];
|
||||
$("#pageCookies").val(msg["message"]);
|
||||
break;
|
||||
@ -138,18 +142,28 @@ function notifyParameterNum(num) {
|
||||
let message = {
|
||||
type: 3, //消息类型,3代表元素增加事件
|
||||
from: 1, //0代表从浏览器到流程图,1代表从流程图到浏览器
|
||||
message: { "pipe": JSON.stringify({ "type": "update_parameter_num", "value": parameterNum }) } // {}全选{BS}退格
|
||||
message: {"pipe": JSON.stringify({"type": "update_parameter_num", "value": parameterNum})} // {}全选{BS}退格
|
||||
};
|
||||
ws.send(JSON.stringify(message));
|
||||
}
|
||||
|
||||
function trailRun(node){
|
||||
function trailElement(node, type = 1) {
|
||||
// type=0代表标记节点,type=1代表试运行
|
||||
let parentNode = nodeList[actionSequence[node["parentId"]]];
|
||||
if (node.option == 10) { //条件分支的话,传父元素的父元素
|
||||
parentNode = nodeList[actionSequence[parentNode["parentId"]]];
|
||||
}
|
||||
if (parentNode.option == 10) { //如果父元素是条件分支,传父元素的爷爷元素
|
||||
parentNode = nodeList[actionSequence[parentNode["parentId"]]];
|
||||
parentNode = nodeList[actionSequence[parentNode["parentId"]]];
|
||||
}
|
||||
let message = {
|
||||
type: 4, //消息类型,4代表试运行事件
|
||||
from: 1, //0代表从浏览器到流程图,1代表从流程图到浏览器
|
||||
message: { "node": JSON.stringify(node), "parentNode": JSON.stringify(nodeList[actionSequence[node["parentId"]]])}
|
||||
message: {"type": type, "node": JSON.stringify(node), "parentNode": JSON.stringify(parentNode)}
|
||||
};
|
||||
ws.send(JSON.stringify(message));
|
||||
console.log(node);
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
@ -165,7 +179,7 @@ function handleElement() {
|
||||
} else if (app._data["nodeType"] == 3) {
|
||||
app._data.paraIndex = 0; //参数索引初始化
|
||||
app._data.paras.parameters = app._data["nowNode"]["parameters"]["paras"];
|
||||
} else if (app._data["nodeType"] == 5){
|
||||
} else if (app._data["nodeType"] == 5) {
|
||||
app._data.codeMode = app._data["nowNode"]["parameters"]["codeMode"];
|
||||
} else if (app._data["nodeType"] == 10) {
|
||||
app._data.TClass = app._data["nowNode"]["parameters"]["class"];
|
||||
@ -203,6 +217,7 @@ function addParameters(t) {
|
||||
t["parameters"]["scrollCount"] = 1; //滚动次数
|
||||
t["parameters"]["scrollWaitTime"] = 1; //滚动后等待时间
|
||||
t["parameters"]["clickWay"] = 0; //点击方式,0代表selenium点击,1代表js点击
|
||||
t["parameters"]["newTab"] = 1; //是否新标签页打开
|
||||
t["parameters"]["maxWaitTime"] = 10; //最长等待时间
|
||||
t["parameters"]["paras"] = []; //默认参数列表
|
||||
t["parameters"]["wait"] = 2; //点击后等待时间默认2s
|
||||
@ -222,7 +237,7 @@ function addParameters(t) {
|
||||
t["parameters"]["beforeJSWaitTime"] = 0; //执行前js等待时间
|
||||
t["parameters"]["afterJS"] = ""; //执行后执行的js
|
||||
t["parameters"]["afterJSWaitTime"] = 0; //执行后js等待时间
|
||||
} else if(t.option == 5) { //自定义操作
|
||||
} else if (t.option == 5) { //自定义操作
|
||||
t["title"] = LANG("执行JavaScript", "Run JavaScript");
|
||||
t["parameters"]["clear"] = 0; //清空其他字段数据
|
||||
t["parameters"]["newLine"] = 1; //生成新行
|
||||
@ -246,6 +261,7 @@ function addParameters(t) {
|
||||
t["parameters"]["optionValue"] = ""; //下拉值
|
||||
t["parameters"]["index"] = 0; //输入框索引
|
||||
} else if (t.option == 8) { //循环
|
||||
t["title"] = LANG("循环 - 单个元素", "Loop - single element");
|
||||
t["parameters"]["scrollType"] = 0; //滚动类型,0不滚动,1向下滚动1屏,2滚动到底部
|
||||
t["parameters"]["scrollCount"] = 1; //滚动次数
|
||||
t["parameters"]["scrollWaitTime"] = 1; //滚动后等待时间
|
||||
@ -262,7 +278,7 @@ function addParameters(t) {
|
||||
t["parameters"]["breakCode"] = ""; //break条件
|
||||
t["parameters"]["breakCodeWaitTime"] = 0; //break条件等待时间
|
||||
} else if (t.option == 9) { //条件
|
||||
|
||||
t["title"] = LANG("判断条件 - 从左往右依次判断", "Judgment condition - judge from left to right");
|
||||
} else if (t.option == 10) { //条件分支
|
||||
t["parameters"]["class"] = 0; //0代表什么条件都没有,1代表当前页面包括文本,2代表当前页面包括元素,3代表当前循环包括文本,4代表当前循环包括元素
|
||||
t["parameters"]["value"] = ""; //相关值
|
||||
@ -305,12 +321,13 @@ function modifyParameters(t, para) {
|
||||
t["parameters"]["value"] = para["value"];
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
} else if(t.option == 6){
|
||||
t["parameters"]["useLoop"] = para["useLoop"];
|
||||
} else if (t.option == 6) {
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
t["parameters"]["optionMode"] = para["optionMode"];
|
||||
t["parameters"]["optionValue"] = para["optionValue"];
|
||||
} else if(t.option == 7){
|
||||
} else if (t.option == 7) {
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["useLoop"] = para["useLoop"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
@ -318,6 +335,7 @@ function modifyParameters(t, para) {
|
||||
t["parameters"]["loopType"] = para["loopType"];
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
t["parameters"]["textList"] = para["value"];
|
||||
if (para["nextPage"]) { //循环点击下一页的情况下
|
||||
t["title"] = LANG("循环点击下一页", "Loop click next page");
|
||||
} else if (para["type"] == "loopClickSingle") { //循环点击单个元素
|
||||
@ -326,8 +344,10 @@ function modifyParameters(t, para) {
|
||||
t["title"] = LANG("循环点击每个元素", "Loop click every element");
|
||||
} else if (para["type"] == "loopMouseMove") { //循环移动到单个元素
|
||||
t["title"] = LANG("循环移动到每个元素", "Loop move to every element");
|
||||
} else if (para["type"] == "multiCollectWithPattern"){
|
||||
} else if (para["type"] == "multiCollectWithPattern") {
|
||||
t["title"] = LANG("循环采集数据", "Loop collect data");
|
||||
} else if (para["type"] == "batchInputText") {
|
||||
t["title"] = LANG("循环输入文字", "Loop input text");
|
||||
} else {
|
||||
t["title"] = LANG("循环", "Loop");
|
||||
}
|
||||
@ -343,24 +363,26 @@ function modifyParameters(t, para) {
|
||||
}
|
||||
}
|
||||
|
||||
function showSuccess(msg, time=4000) {
|
||||
function showSuccess(msg, time = 4000) {
|
||||
$("#tip").text(msg);
|
||||
$("#tip").slideDown(); //提示框
|
||||
let fadeout = setTimeout(function() {
|
||||
let fadeout = setTimeout(function () {
|
||||
$("#tip").slideUp();
|
||||
}, time);
|
||||
}
|
||||
function showInfo(msg, time=4000) {
|
||||
|
||||
function showInfo(msg, time = 4000) {
|
||||
$("#info_message").text(msg);
|
||||
$("#tipInfo").slideDown(); //提示框
|
||||
let fadeout = setTimeout(function() {
|
||||
let fadeout = setTimeout(function () {
|
||||
$("#tipInfo").slideUp();
|
||||
}, time);
|
||||
}
|
||||
function showError(msg, time=4000) {
|
||||
|
||||
function showError(msg, time = 4000) {
|
||||
$("#error_message").text(msg);
|
||||
$("#tipError").slideDown(); //提示框
|
||||
let fadeout = setTimeout(function() {
|
||||
let fadeout = setTimeout(function () {
|
||||
$("#tipError").slideUp();
|
||||
}, time);
|
||||
}
|
||||
@ -370,11 +392,11 @@ function showError(msg, time=4000) {
|
||||
$("#confirm").mousedown(updateUI);
|
||||
|
||||
//点击保存任务按钮时的处理
|
||||
$("#saveButton").mousedown(function() {
|
||||
$("#saveButton").mousedown(function () {
|
||||
saveService(0);
|
||||
});
|
||||
//点击另存为任务按钮时的处理
|
||||
$("#saveAsButton").mousedown(function() {
|
||||
$("#saveAsButton").mousedown(function () {
|
||||
saveService(1);
|
||||
});
|
||||
|
||||
@ -398,170 +420,178 @@ function saveService(type) {
|
||||
text = LANG("确认要另存为任务吗(不能用鼠标点击时,请按键盘回车键)?", "Are you sure to save the task as (if you can't use the mouse to click, please press the enter key)?");
|
||||
}
|
||||
// if (confirm(text)) {
|
||||
let serviceName = $("#serviceName").val();
|
||||
let url = $("#url").val();
|
||||
let serviceDescription = $("#serviceDescription").val();
|
||||
let inputParameters = [];
|
||||
let outputParameters = [];
|
||||
let outputNames = [];
|
||||
let inputIndex = 0;
|
||||
let outputIndex = 0;
|
||||
let links = "about:blank"; //记录所有的link
|
||||
let containJudge = false; //是否含有判断语句
|
||||
let saveThreshold = parseInt($("#saveThreshold").val());
|
||||
let cloudflare = parseInt($("#cloudflare").val());
|
||||
let environment = parseInt($("#environment").val());
|
||||
for (let i = 1; i < nodeList.length; i++) {
|
||||
if (nodeList[i]["id"] != -1) { //已经被删除的节点不进行统计
|
||||
if (nodeList[i]["option"] == 1) //打开网页操作,统计输入框输入操作
|
||||
let serviceName = $("#serviceName").val();
|
||||
let url = $("#url").val();
|
||||
let serviceDescription = $("#serviceDescription").val();
|
||||
let inputParameters = [];
|
||||
let outputParameters = [];
|
||||
let outputNames = [];
|
||||
let inputIndex = 0;
|
||||
let outputIndex = 0;
|
||||
let links = "about:blank"; //记录所有的link
|
||||
let containJudge = false; //是否含有判断语句
|
||||
let saveThreshold = parseInt($("#saveThreshold").val());
|
||||
let cloudflare = parseInt($("#cloudflare").val());
|
||||
let environment = parseInt($("#environment").val());
|
||||
for (let i = 1; i < nodeList.length; i++) {
|
||||
if (nodeList[i]["id"] != -1) { //已经被删除的节点不进行统计
|
||||
if (nodeList[i]["option"] == 1) //打开网页操作,统计输入框输入操作
|
||||
{
|
||||
if (!nodeList[i]["parameters"]["useLoop"]) //如果不是使用循环里的文本
|
||||
{
|
||||
if (!nodeList[i]["parameters"]["useLoop"]) //如果不是使用循环里的文本
|
||||
{
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "urlList_" + inputIndex++,
|
||||
nodeId: i, //记录操作位于的节点位置,重要!!!
|
||||
nodeName: nodeList[i]["title"],
|
||||
value: nodeList[i]["parameters"]["links"],
|
||||
desc: LANG("要采集的网址列表,多行以\\n分开","List of URLs to be collected, separated by \\n for multiple lines",),
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["links"]
|
||||
});
|
||||
links = nodeList[i]["parameters"]["links"];
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 4) //输入文字操作
|
||||
{
|
||||
if (!nodeList[i]["parameters"]["useLoop"]) //如果不是使用循环里的文本
|
||||
{
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "inputText_" + inputIndex++,
|
||||
nodeName: nodeList[i]["title"],
|
||||
nodeId: i,
|
||||
desc: LANG("要输入的文本,如京东搜索框输入:电脑","The text to be entered, such as 'computer' at eBay search box"),
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["value"],
|
||||
value: nodeList[i]["parameters"]["value"],
|
||||
});
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 8) //循环操作
|
||||
{
|
||||
if (parseInt(nodeList[i]["parameters"]["loopType"]) > 2 && parseInt(nodeList[i]["parameters"]["loopType"]) < 5) { //循环中的循环输入文本或循环输入网址
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "loopText_" + inputIndex++,
|
||||
nodeId: i,
|
||||
nodeName: nodeList[i]["title"],
|
||||
desc: LANG("要输入的文本/网址,多行以\\n分开", "Text/URL to be entered, multiple lines should be separated by \\n"),
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["textList"],
|
||||
value: nodeList[i]["parameters"]["textList"],
|
||||
});
|
||||
} else if (parseInt(nodeList[i]["parameters"]["loopType"]) == 0) {
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "loopTimes_" + nodeList[i]["title"] + "_" + inputIndex++,
|
||||
nodeId: i,
|
||||
nodeName: nodeList[i]["title"],
|
||||
desc: LANG("循环" + nodeList[i]["title"] + "执行的次数(0代表无限循环)", "Number of loop executions for loop "+nodeList[i]["title"]+", 0 means unlimited loops (until element not found)"),
|
||||
type: "int",
|
||||
exampleValue: nodeList[i]["parameters"]["exitCount"],
|
||||
value: nodeList[i]["parameters"]["exitCount"],
|
||||
});
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 3) //提取数据操作
|
||||
{
|
||||
for (let j = 0; j < nodeList[i]["parameters"]["paras"].length; j++) {
|
||||
if (outputNames.indexOf(nodeList[i]["parameters"]["paras"][j]["name"]) < 0) { //参数名称还未被添加
|
||||
outputNames.push(nodeList[i]["parameters"]["paras"][j]["name"]);
|
||||
outputParameters.push({
|
||||
id: outputIndex++,
|
||||
name: nodeList[i]["parameters"]["paras"][j]["name"],
|
||||
desc: nodeList[i]["parameters"]["paras"][j]["desc"],
|
||||
type: nodeList[i]["parameters"]["paras"][j]["paraType"],
|
||||
recordASField: nodeList[i]["parameters"]["paras"][j]["recordASField"],
|
||||
exampleValue: nodeList[i]["parameters"]["paras"][j]["exampleValues"][0]["value"],
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 5) //自定义操作
|
||||
{
|
||||
// if (nodeList[i]["parameters"]["recordASField"] == 1) {
|
||||
let id = outputIndex++;
|
||||
let title = nodeList[i]["title"];
|
||||
// if (outputNames.indexOf(title) >= 0) { //参数名称已经被添加
|
||||
// $('#myModal').modal('hide');
|
||||
// $("#tip2").slideDown(); //提示框
|
||||
// fadeout = setTimeout(function() {
|
||||
// $("#tip2").slideUp();
|
||||
// }, 5000);
|
||||
// return;
|
||||
// }
|
||||
outputNames.push(title);
|
||||
outputParameters.push({
|
||||
id: id,
|
||||
name: title,
|
||||
desc: LANG("自定义操作返回的数据","Output of custom action"),
|
||||
type: nodeList[i]["parameters"]["paraType"],
|
||||
recordASField: nodeList[i]["parameters"]["recordASField"],
|
||||
exampleValue: "",
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "urlList_" + inputIndex++,
|
||||
nodeId: i, //记录操作位于的节点位置,重要!!!
|
||||
nodeName: nodeList[i]["title"],
|
||||
value: nodeList[i]["parameters"]["links"],
|
||||
desc: LANG("要采集的网址列表,多行以\\n分开", "List of URLs to be collected, separated by \\n for multiple lines",),
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["links"]
|
||||
});
|
||||
// }
|
||||
} else if (nodeList[i]["option"] == 9) //条件判断
|
||||
{
|
||||
containJudge = true;
|
||||
links = nodeList[i]["parameters"]["links"];
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 4) //输入文字操作
|
||||
{
|
||||
if (!nodeList[i]["parameters"]["useLoop"]) //如果不是使用循环里的文本
|
||||
{
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "inputText_" + inputIndex++,
|
||||
nodeName: nodeList[i]["title"],
|
||||
nodeId: i,
|
||||
desc: LANG("要输入的文本,如京东搜索框输入:电脑", "The text to be entered, such as 'computer' at eBay search box"),
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["value"],
|
||||
value: nodeList[i]["parameters"]["value"],
|
||||
});
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 8) //循环操作
|
||||
{
|
||||
if (parseInt(nodeList[i]["parameters"]["loopType"]) > 2 && parseInt(nodeList[i]["parameters"]["loopType"]) < 5) { //循环中的循环输入文本或循环输入网址
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "loopText_" + inputIndex++,
|
||||
nodeId: i,
|
||||
nodeName: nodeList[i]["title"],
|
||||
desc: LANG("要输入的文本/网址,多行以\\n分开", "Text/URL to be entered, multiple lines should be separated by \\n"),
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["textList"],
|
||||
value: nodeList[i]["parameters"]["textList"],
|
||||
});
|
||||
} else if (parseInt(nodeList[i]["parameters"]["loopType"]) == 0) {
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "loopTimes_" + nodeList[i]["title"] + "_" + inputIndex++,
|
||||
nodeId: i,
|
||||
nodeName: nodeList[i]["title"],
|
||||
desc: LANG("循环" + nodeList[i]["title"] + "执行的次数(0代表无限循环)", "Number of loop executions for loop " + nodeList[i]["title"] + ", 0 means unlimited loops (until element not found)"),
|
||||
type: "int",
|
||||
exampleValue: nodeList[i]["parameters"]["exitCount"],
|
||||
value: nodeList[i]["parameters"]["exitCount"],
|
||||
});
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 3) //提取数据操作
|
||||
{
|
||||
for (let j = 0; j < nodeList[i]["parameters"]["paras"].length; j++) {
|
||||
if (outputNames.indexOf(nodeList[i]["parameters"]["paras"][j]["name"]) < 0) { //参数名称还未被添加
|
||||
outputNames.push(nodeList[i]["parameters"]["paras"][j]["name"]);
|
||||
outputParameters.push({
|
||||
id: outputIndex++,
|
||||
name: nodeList[i]["parameters"]["paras"][j]["name"],
|
||||
desc: nodeList[i]["parameters"]["paras"][j]["desc"],
|
||||
type: nodeList[i]["parameters"]["paras"][j]["paraType"],
|
||||
recordASField: nodeList[i]["parameters"]["paras"][j]["recordASField"],
|
||||
exampleValue: nodeList[i]["parameters"]["paras"][j]["exampleValues"][0]["value"],
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 5) //自定义操作
|
||||
{
|
||||
// if (nodeList[i]["parameters"]["recordASField"] == 1) {
|
||||
let id = outputIndex++;
|
||||
let title = nodeList[i]["title"];
|
||||
// if (outputNames.indexOf(title) >= 0) { //参数名称已经被添加
|
||||
// $('#myModal').modal('hide');
|
||||
// $("#tip2").slideDown(); //提示框
|
||||
// fadeout = setTimeout(function() {
|
||||
// $("#tip2").slideUp();
|
||||
// }, 5000);
|
||||
// return;
|
||||
// }
|
||||
outputNames.push(title);
|
||||
outputParameters.push({
|
||||
id: id,
|
||||
name: title,
|
||||
desc: LANG("自定义操作返回的数据", "Output of custom action"),
|
||||
type: nodeList[i]["parameters"]["paraType"],
|
||||
recordASField: nodeList[i]["parameters"]["recordASField"],
|
||||
exampleValue: "",
|
||||
});
|
||||
// }
|
||||
} else if (nodeList[i]["option"] == 9) //条件判断
|
||||
{
|
||||
containJudge = true;
|
||||
}
|
||||
}
|
||||
serviceInfo = {
|
||||
"id": parseInt(serviceId),
|
||||
"name": serviceName,
|
||||
"url": url,
|
||||
"links": links,
|
||||
"create_time": parseInt(serviceId) == -1 ? new Date().toLocaleString() : $("#create_time").val(),
|
||||
"update_time": new Date().toLocaleString(),
|
||||
"version": "0.6.0",
|
||||
"saveThreshold": saveThreshold,
|
||||
// "cloudflare": cloudflare,
|
||||
"quitWaitTime": parseInt($("#quitWaitTime").val()),
|
||||
"environment": environment,
|
||||
"maximizeWindow": parseInt($("#maximizeWindow").val()),
|
||||
"maxViewLength": parseInt($("#maxViewLength").val()),
|
||||
"recordLog": parseInt($("#recordLog").val()),
|
||||
"outputFormat": $("#outputFormat").val(),
|
||||
"saveName": $("#saveName").val(),
|
||||
"inputExcel": $("#inputExcel").val(),
|
||||
"startFromExit": parseInt($("#startFromExit").val()),
|
||||
"pauseKey": $("#pauseKey").val(),
|
||||
"containJudge": containJudge,
|
||||
"desc": serviceDescription,
|
||||
"inputParameters": inputParameters,
|
||||
"outputParameters": outputParameters,
|
||||
"graph": nodeList, //图结构要存储下来
|
||||
};
|
||||
if(serviceInfo.outputFormat=="mysql"){
|
||||
if(!isValidMySQLTableName(serviceInfo.saveName)) {
|
||||
$('#myModal').modal('hide');
|
||||
showError(LANG("提示:保存名不符合MySQL表名规范,请重试!","The save name is not valid for MySQL table name!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
serviceInfo = {
|
||||
"id": parseInt(serviceId),
|
||||
"name": serviceName,
|
||||
"url": url,
|
||||
"links": links,
|
||||
"create_time": parseInt(serviceId) == -1 ? new Date().toLocaleString() : $("#create_time").val(),
|
||||
"update_time": new Date().toLocaleString(),
|
||||
"version": "0.6.0",
|
||||
"saveThreshold": saveThreshold,
|
||||
// "cloudflare": cloudflare,
|
||||
"quitWaitTime": parseInt($("#quitWaitTime").val()),
|
||||
"environment": environment,
|
||||
"maximizeWindow": parseInt($("#maximizeWindow").val()),
|
||||
"maxViewLength": parseInt($("#maxViewLength").val()),
|
||||
"recordLog": parseInt($("#recordLog").val()),
|
||||
"outputFormat": $("#outputFormat").val(),
|
||||
"saveName": $("#saveName").val(),
|
||||
"inputExcel": $("#inputExcel").val(),
|
||||
"startFromExit": parseInt($("#startFromExit").val()),
|
||||
"pauseKey": $("#pauseKey").val(),
|
||||
"containJudge": containJudge,
|
||||
"desc": serviceDescription,
|
||||
"inputParameters": inputParameters,
|
||||
"outputParameters": outputParameters,
|
||||
"graph": nodeList, //图结构要存储下来
|
||||
};
|
||||
if (serviceInfo.outputFormat == "mysql") {
|
||||
if (!isValidMySQLTableName(serviceInfo.saveName)) {
|
||||
$('#myModal').modal('hide');
|
||||
showError(LANG("提示:保存名不符合MySQL表名规范,请重试!", "The save name is not valid for MySQL table name!"));
|
||||
return;
|
||||
}
|
||||
$.post(backEndAddressServiceWrapper + "/manageTask", { paras: JSON.stringify(serviceInfo) },
|
||||
function(result) { $("#serviceId").val(parseInt(result)) });
|
||||
// alert("保存成功!");
|
||||
$('#myModal').modal('hide');
|
||||
showSuccess(LANG("保存成功!","Save successfully!"));
|
||||
}
|
||||
$.post(backEndAddressServiceWrapper + "/manageTask", {paras: JSON.stringify(serviceInfo)},
|
||||
function (result) {
|
||||
$("#serviceId").val(parseInt(result))
|
||||
if (type == 1) { //任务另存为
|
||||
let currentUrl = window.location.href;
|
||||
let id = getUrlParam("id");
|
||||
let newUrl = currentUrl.replace("id=" + id, "id=" + result + "&saveAs=1");
|
||||
window.location.href = newUrl;
|
||||
}
|
||||
});
|
||||
// alert("保存成功!");
|
||||
$('#myModal').modal('hide');
|
||||
showSuccess(LANG("保存成功!", "Save successfully!"));
|
||||
// }
|
||||
}
|
||||
|
||||
if (sId != null && sId != -1) //加载任务
|
||||
{
|
||||
$.get(backEndAddressServiceWrapper + "/queryTask?id=" + sId, function(result) {
|
||||
$.get(backEndAddressServiceWrapper + "/queryTask?id=" + sId, function (result) {
|
||||
nodeList = result["graph"];
|
||||
app.$data.list.nl = nodeList;
|
||||
for(let node of nodeList){ //兼容旧版本
|
||||
if(node["option"] == 1){
|
||||
if(!("cookies" in node["parameters"])) {
|
||||
for (let node of nodeList) { //兼容旧版本
|
||||
if (node["option"] == 1) {
|
||||
if (!("cookies" in node["parameters"])) {
|
||||
node["parameters"]["cookies"] = "";
|
||||
}
|
||||
}
|
||||
@ -570,15 +600,18 @@ if (sId != null && sId != -1) //加载任务
|
||||
$("#serviceId").val(result["id"]);
|
||||
$("#url").val(result["url"]);
|
||||
$("#serviceDescription").val(result["desc"]);
|
||||
for(let key of Object.keys(result)){
|
||||
try{
|
||||
$("#"+key).val(result[key]);
|
||||
} catch(e){
|
||||
for (let key of Object.keys(result)) {
|
||||
try {
|
||||
$("#" + key).val(result[key]);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
if(result["version"]!= serviceInfo["version"]){
|
||||
showInfo(LANG("提示:该任务为" + result["version"] + "版本任务,当前版本为" + serviceInfo["version"] + ",可能存在兼容性问题,请按照当前版本指南设计任务流程以避免任务执行不正常。","This task is designed by EasySpider " + result["version"] + ", current version of EasySpider is " + serviceInfo["version"] + ", there may be compatibility issues, please design the task flow according to the current version guide to avoid abnormal task execution."));
|
||||
if (result["version"] != serviceInfo["version"]) {
|
||||
showInfo(LANG("提示:该任务为" + result["version"] + "版本任务,当前版本为" + serviceInfo["version"] + ",可能存在兼容性问题,请按照当前版本指南设计任务流程以避免任务执行不正常。", "This task is designed by EasySpider " + result["version"] + ", current version of EasySpider is " + serviceInfo["version"] + ", there may be compatibility issues, please design the task flow according to the current version guide to avoid abnormal task execution."));
|
||||
}
|
||||
if (getUrlParam("saveAs") == 1) {
|
||||
showSuccess(LANG("另存为成功!", "Save as successfully!"));
|
||||
}
|
||||
refresh();
|
||||
});
|
||||
@ -587,7 +620,7 @@ if (sId != null && sId != -1) //加载任务
|
||||
}
|
||||
|
||||
function LANG(zh, en) {
|
||||
if(window.location.href.indexOf("_CN") != -1){
|
||||
if (window.location.href.indexOf("_CN") != -1) {
|
||||
return zh;
|
||||
} else {
|
||||
return en;
|
||||
|
@ -56,7 +56,7 @@
|
||||
<td style="height: 30px;overflow: hidden; max-width: 200px">{{list[i-1]["url"]}}</td>
|
||||
<td style="text-align: left"><a href="javascript:void(0)" v-on:click="browseTask(list[i-1]['id'])">{{"Task Information~任务信息" | lang}}</a></td>
|
||||
<td style="text-align: left;font-weight: bold" v-if="type==3"><a href="javascript:void(0)" v-on:click="modifyTask(list[i-1]['id'],list[i-1]['url'])">{{"Modify Task~修改任务" | lang}}</a></td>
|
||||
<td style="text-align: left"><a disabled href="javascript:void(0)" v-on:click="deleteTask(list[i-1]['id'])">{{"Delete Task~删除任务" | lang}}</a></td>
|
||||
<td style="text-align: left"><a disabled href="javascript:void(0)" v-on:dblclick="deleteTask(list[i-1]['id'])">{{"Delete Task (Double Click)~删除任务(双击)" | lang}}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -96,13 +96,13 @@
|
||||
window.location.href = "taskInfo.html?type="+getUrlParam("type")+"&id=" + id + "&lang="+getUrlParam("lang")+"&wsport="+getUrlParam("wsport")+"&backEndAddressServiceWrapper="+ app.$data.backEndAddressServiceWrapper; //跳转链接
|
||||
},
|
||||
deleteTask: function(id) {
|
||||
let text = "Are you sure to remove the selected task?";
|
||||
if (getUrlParam("lang") == "en"|| getUrlParam("lang")=="") {
|
||||
text = "Are you sure to remove the selected task?";
|
||||
} else if (getUrlParam("lang") == "zh") {
|
||||
text = "确定要删除选中的任务吗?";
|
||||
}
|
||||
if (confirm(text)) {
|
||||
// let text = "Are you sure to remove the selected task?";
|
||||
// if (getUrlParam("lang") == "en"|| getUrlParam("lang")=="") {
|
||||
// text = "Are you sure to remove the selected task?";
|
||||
// } else if (getUrlParam("lang") == "zh") {
|
||||
// text = "确定要删除选中的任务吗?";
|
||||
// }
|
||||
// if (confirm(text)) {
|
||||
$.get(app.$data.backEndAddressServiceWrapper + "/deleteTask?id=" + id, function(res) {
|
||||
$.get(app.$data.backEndAddressServiceWrapper + "/queryTasks", function(re) {
|
||||
result = re.sort(desc);
|
||||
@ -110,7 +110,7 @@
|
||||
});
|
||||
});
|
||||
// alert("Sorry, the task cannot be deleted since the system is a demo system for paper reviewers, please contact the author (naibowang@u.nus.edu) to remove it.")
|
||||
}
|
||||
// }
|
||||
},
|
||||
}
|
||||
});
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/257.json
Normal file
1
ElectronJS/tasks/257.json
Normal file
@ -0,0 +1 @@
|
||||
{"id":257,"name":"百度一下,你就知道","url":"https://www.baidu.com","links":"https://www.baidu.com","create_time":"12/12/2023, 5:59:29 AM","update_time":"12/12/2023, 5:59:29 AM","version":"0.6.0","saveThreshold":10,"quitWaitTime":60,"environment":0,"maximizeWindow":0,"maxViewLength":15,"recordLog":1,"outputFormat":"xlsx","saveName":"current_time","inputExcel":"","startFromExit":0,"pauseKey":"p","containJudge":true,"desc":"https://www.baidu.com","inputParameters":[{"id":0,"name":"urlList_0","nodeId":1,"nodeName":"打开网页","value":"https://www.baidu.com","desc":"要采集的网址列表,多行以\\n分开","type":"text","exampleValue":"https://www.baidu.com"}],"outputParameters":[],"graph":[{"index":0,"id":0,"parentId":0,"type":-1,"option":0,"title":"root","sequence":[1,2,3,6],"parameters":{"history":1,"tabIndex":0,"useLoop":false,"xpath":"","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0},"isInLoop":false},{"id":1,"index":1,"parentId":0,"type":0,"option":1,"title":"打开网页","sequence":[],"isInLoop":false,"position":0,"parameters":{"useLoop":false,"xpath":"","wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"url":"https://www.baidu.com","links":"https://www.baidu.com","maxWaitTime":10,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"cookies":""}},{"id":2,"index":2,"parentId":0,"type":0,"option":2,"title":"点击“同志加兄弟...","sequence":[],"isInLoop":false,"position":1,"parameters":{"history":4,"tabIndex":-1,"useLoop":false,"xpath":"//*[@id=\"hotsearch-content-wrapper\"]/li[1]/a[1]/span[2]","iframe":false,"wait":2,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"clickWay":0,"maxWaitTime":10,"paras":[],"alertHandleType":0,"allXPaths":["/html/body/div[2]/div[1]/div[5]/div[1]/div[1]/div[3]/ul[1]/li[1]/a[1]/span[2]","//span[contains(., '“同志加兄弟”:中越')]","//SPAN[@class='title-content-title']","/html/body/div[last()-5]/div[last()-3]/div[last()-3]/div/div/div/ul/li[last()-5]/a/span"]}},{"id":3,"index":3,"parentId":0,"type":2,"option":9,"title":"判断条件","sequence":[4,5],"isInLoop":false,"position":2,"parameters":{"history":1,"tabIndex":0,"useLoop":false,"xpath":"","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0}},{"id":5,"parentId":3,"index":4,"type":3,"option":10,"title":"条件分支1","sequence":[],"isInLoop":false,"parameters":{"history":1,"tabIndex":0,"useLoop":false,"xpath":"","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"class":1,"value":"hao123","code":"","waitTime":0},"position":0},{"id":6,"parentId":3,"index":5,"type":3,"option":10,"title":"条件分支2","sequence":[],"isInLoop":false,"parameters":{"history":1,"tabIndex":0,"useLoop":false,"xpath":"","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"class":1,"value":"元首","code":"","waitTime":0},"position":1},{"id":4,"index":6,"parentId":0,"type":0,"option":2,"title":"点击\n换一...","sequence":[],"isInLoop":false,"position":3,"parameters":{"history":4,"tabIndex":-1,"useLoop":false,"xpath":"//*[@id=\"head_wrapper\"]","iframe":false,"wait":2,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"clickWay":0,"maxWaitTime":10,"paras":[],"alertHandleType":0,"allXPaths":["/html/body/div[2]/div[1]/div[5]","//div[contains(., '')]","id(\"head_wrapper\")","//DIV[@class='head_wrapper s-isindex-wrap nologin']","/html/body/div[last()-6]/div[last()-3]/div[last()-3]"]}}]}
|
1
ElectronJS/tasks/258.json
Normal file
1
ElectronJS/tasks/258.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/259.json
Normal file
1
ElectronJS/tasks/259.json
Normal file
@ -0,0 +1 @@
|
||||
{"id":259,"name":"","url":"https://mall.espic.com.cn/mall-view/","links":"https://mall.espic.com.cn/mall-view/","create_time":"12/12/2023, 5:26:03 PM","update_time":"12/12/2023, 5:26:03 PM","version":"0.6.0","saveThreshold":10,"quitWaitTime":60,"environment":0,"maximizeWindow":0,"maxViewLength":15,"recordLog":1,"outputFormat":"xlsx","saveName":"current_time","inputExcel":"","startFromExit":0,"pauseKey":"p","containJudge":false,"desc":"https://mall.espic.com.cn/mall-view/","inputParameters":[{"id":0,"name":"urlList_0","nodeId":1,"nodeName":"打开网页","value":"https://mall.espic.com.cn/mall-view/","desc":"要采集的网址列表,多行以\\n分开","type":"text","exampleValue":"https://mall.espic.com.cn/mall-view/"}],"outputParameters":[],"graph":[{"index":0,"id":0,"parentId":0,"type":-1,"option":0,"title":"root","sequence":[1],"parameters":{"history":1,"tabIndex":0,"useLoop":false,"xpath":"","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0},"isInLoop":false},{"id":1,"index":1,"parentId":0,"type":0,"option":1,"title":"打开网页","sequence":[],"isInLoop":false,"position":0,"parameters":{"useLoop":false,"xpath":"","wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"url":"https://mall.espic.com.cn/mall-view/","links":"https://mall.espic.com.cn/mall-view/","maxWaitTime":10,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"cookies":""}}]}
|
1
ElectronJS/tasks/260.json
Normal file
1
ElectronJS/tasks/260.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/261.json
Normal file
1
ElectronJS/tasks/261.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/262.json
Normal file
1
ElectronJS/tasks/262.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/263.json
Normal file
1
ElectronJS/tasks/263.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/264.json
Normal file
1
ElectronJS/tasks/264.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/265.json
Normal file
1
ElectronJS/tasks/265.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/266.json
Normal file
1
ElectronJS/tasks/266.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/267.json
Normal file
1
ElectronJS/tasks/267.json
Normal file
@ -0,0 +1 @@
|
||||
{"id":267,"name":"京东全球版-专业的综合网上购物商城","url":"https://www.jd.com","links":"https://www.jd.com","create_time":"12/13/2023, 2:14:40 AM","update_time":"12/13/2023, 2:14:40 AM","version":"0.6.0","saveThreshold":10,"quitWaitTime":60,"environment":0,"maximizeWindow":0,"maxViewLength":15,"recordLog":1,"outputFormat":"xlsx","saveName":"current_time","inputExcel":"","startFromExit":0,"pauseKey":"p","containJudge":false,"desc":"https://www.jd.com","inputParameters":[{"id":0,"name":"urlList_0","nodeId":1,"nodeName":"打开网页","value":"https://www.jd.com","desc":"要采集的网址列表,多行以\\n分开","type":"text","exampleValue":"https://www.jd.com"},{"id":1,"name":"inputText_1","nodeName":"输入文字","nodeId":2,"desc":"要输入的文本,如京东搜索框输入:电脑","type":"text","exampleValue":"1","value":"1"}],"outputParameters":[],"graph":[{"index":0,"id":0,"parentId":0,"type":-1,"option":0,"title":"root","sequence":[1,2,3],"parameters":{"history":1,"tabIndex":0,"useLoop":false,"xpath":"","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0},"isInLoop":false},{"id":1,"index":1,"parentId":0,"type":0,"option":1,"title":"打开网页","sequence":[],"isInLoop":false,"position":0,"parameters":{"useLoop":false,"xpath":"","wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"url":"https://www.jd.com","links":"https://www.jd.com","maxWaitTime":10,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"cookies":""}},{"id":2,"index":2,"parentId":0,"type":0,"option":4,"title":"输入文字","sequence":[],"isInLoop":false,"position":1,"parameters":{"history":4,"tabIndex":-1,"useLoop":false,"xpath":"//*[@id=\"key\"]","iframe":false,"wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"value":"1","index":0,"allXPaths":["/html/body/div[4]/div[1]/div[2]/div[1]/input[1]","//input[contains(., '')]","id(\"key\")","//INPUT[@class='text']","/html/body/div[last()-6]/div/div[last()-2]/div/input"]}},{"id":3,"index":3,"parentId":0,"type":0,"option":2,"title":"点击元素","sequence":[],"isInLoop":false,"position":2,"parameters":{"history":4,"tabIndex":-1,"useLoop":false,"xpath":"//*[@id=\"search-link\"]/i[1]","iframe":false,"wait":2,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"clickWay":0,"maxWaitTime":10,"paras":[],"alertHandleType":0,"allXPaths":["/html/body/div[4]/div[1]/div[2]/div[1]/a[1]/i[1]","//i[contains(., '')]","/html/body/div[last()-6]/div/div[last()-2]/div/a/i"]}},{"id":-1,"index":4,"parentId":0,"type":0,"option":2,"title":"点击元素","sequence":[],"isInLoop":false,"position":3,"parameters":{"history":4,"tabIndex":-1,"useLoop":false,"xpath":"//*[@id=\"search-link\"]/i[1]","iframe":false,"wait":2,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"waitElement":"","waitElementTime":10,"waitElementIframeIndex":0,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"clickWay":0,"maxWaitTime":10,"paras":[],"alertHandleType":0,"allXPaths":["/html/body/div[4]/div[1]/div[2]/div[1]/a[1]/i[1]","//i[contains(., '')]","/html/body/div[last()-6]/div/div[last()-2]/div/a/i"]}}]}
|
1
ElectronJS/tasks/268.json
Normal file
1
ElectronJS/tasks/268.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/269.json
Normal file
1
ElectronJS/tasks/269.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/270.json
Normal file
1
ElectronJS/tasks/270.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/271.json
Normal file
1
ElectronJS/tasks/271.json
Normal file
File diff suppressed because one or more lines are too long
1
ElectronJS/tasks/272.json
Normal file
1
ElectronJS/tasks/272.json
Normal file
File diff suppressed because one or more lines are too long
2
ExecuteStage/.vscode/launch.json
vendored
2
ExecuteStage/.vscode/launch.json
vendored
@ -12,7 +12,7 @@
|
||||
"justMyCode": false,
|
||||
// "args": ["--ids", "[7]", "--read_type", "remote", "--headless", "0"]
|
||||
// "args": ["--ids", "[9]", "--read_type", "remote", "--headless", "0", "--saved_file_name", "YOUTUBE"]
|
||||
"args": ["--ids", "[20]", "--headless", "0", "--user_data", "0", "--keyboard", "0",
|
||||
"args": ["--ids", "[38]", "--headless", "0", "--user_data", "0", "--keyboard", "0",
|
||||
"--read_type", "remote"]
|
||||
// "args": "--ids '[97]' --user_data 1 --server_address http://localhost:8074 --config_folder '/Users/naibo/Documents/EasySpider/ElectronJS/' --headless 0 --read_type remote --config_file_name config.json --saved_file_name"
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ class BrowserThread(Thread):
|
||||
"will start from the last step, before we already collected", self.startSteps, " items.")
|
||||
else:
|
||||
self.print_and_log("此模式下,任务ID", self.id,
|
||||
"将从头开始执行,如果需要从上次退出的步骤开始执行,请在保存任务时设置是否从上次保存位置开始执行为“是”。")
|
||||
"将从头F开始执行,如果需要从上次退出的步骤开始执行,请在保存任务时设置是否从上次保存位置开始执行为“是”。")
|
||||
self.print_and_log("In this mode, task ID", self.id,
|
||||
"will start from the beginning, if you want to start from the last step, please set the option 'start from the last step' to 'yes' when saving the task.")
|
||||
stealth_path = driver_path[:driver_path.find(
|
||||
@ -325,11 +325,17 @@ class BrowserThread(Thread):
|
||||
# 如果(不)固定元素列表循环中只有一个提取数据操作,且提取数据操作的提取内容为元素截图,那么可以快速提取
|
||||
if len(node["sequence"]) == 1 and self.procedure[node["sequence"][0]]["option"] == 3 and (int(node["parameters"]["loopType"]) == 1 or int(node["parameters"]["loopType"]) == 2):
|
||||
paras = self.procedure[node["sequence"][0]]["parameters"]["paras"]
|
||||
waitElement = self.procedure[node["sequence"][0]]["parameters"]["waitElement"]
|
||||
node["parameters"]["quickExtractable"] = True # 先假设可以快速提取
|
||||
try:
|
||||
waitElement = self.procedure[node["sequence"][0]]["parameters"]["waitElement"]
|
||||
except:
|
||||
waitElement = ""
|
||||
if node["parameters"]["iframe"]:
|
||||
node["parameters"]["quickExtractable"] = False # 如果是iframe,那么不可以快速提取
|
||||
else:
|
||||
node["parameters"]["quickExtractable"] = True # 先假设可以快速提取
|
||||
for para in paras:
|
||||
optimizable = detect_optimizable(para, ignoreWaitElement=False, waitElement=waitElement)
|
||||
if para["iframe"]: # 如果是iframe,那么不可以快速提取
|
||||
if para["iframe"] and not para["relative"]: # 如果是iframe,那么不可以快速提取
|
||||
optimizable = False
|
||||
if not optimizable: # 如果有一个不满足优化条件,那么就不能快速提取
|
||||
node["parameters"]["quickExtractable"] = False
|
||||
@ -911,7 +917,6 @@ class BrowserThread(Thread):
|
||||
self.event.wait() # 等待事件结束
|
||||
|
||||
# 对判断条件的处理
|
||||
|
||||
def judgeExecute(self, node, loopElement, clickPath="", index=0):
|
||||
executeBranchId = 0 # 要执行的BranchId
|
||||
for i in node["sequence"]:
|
||||
@ -1513,11 +1518,19 @@ class BrowserThread(Thread):
|
||||
click_way = int(para["clickWay"])
|
||||
except:
|
||||
click_way = 0
|
||||
try:
|
||||
newTab = int(para["newTab"])
|
||||
except:
|
||||
newTab = 1
|
||||
try:
|
||||
if click_way == 0: # 用selenium的点击方法
|
||||
try:
|
||||
actions = ActionChains(self.browser) # 实例化一个action对象
|
||||
actions.click(element).perform()
|
||||
if newTab == 1: # 在新标签页打开
|
||||
# Ctrl + Click
|
||||
actions.key_down(Keys.CONTROL).click(element).key_up(Keys.CONTROL).perform()
|
||||
else:
|
||||
actions.click(element).perform()
|
||||
except Exception as e:
|
||||
self.browser.execute_script("arguments[0].scrollIntoView();", element)
|
||||
try:
|
||||
|
@ -22,6 +22,8 @@ export var global = {
|
||||
iframe: false,
|
||||
};
|
||||
|
||||
|
||||
|
||||
export function isInIframe() {
|
||||
try {
|
||||
return window.self !== window.parent;
|
||||
@ -261,6 +263,13 @@ export function clearParameters(deal = true) //清空参数列表
|
||||
global.app._data.selectStatus = false;
|
||||
}
|
||||
|
||||
export function LANG(zh, en) {
|
||||
if (global.lang == "zh") {
|
||||
return zh;
|
||||
} else {
|
||||
return en;
|
||||
}
|
||||
}
|
||||
function parameterName(value){
|
||||
if (global.lang == 'zh'){
|
||||
return value;
|
||||
|
@ -1,6 +1,7 @@
|
||||
//实现与后台和流程图部分的交互
|
||||
|
||||
import {getElementXPaths, global, readXPath, isInIframe, clearEl} from "./global.js";
|
||||
import {getElementXPaths, global, readXPath, isInIframe, clearEl, LANG} from "./global.js";
|
||||
import {trial, createNotification} from "./trail.js";
|
||||
|
||||
global.ws = new WebSocket("ws://localhost:8084");
|
||||
global.ws.onopen = function () {
|
||||
@ -24,26 +25,30 @@ global.ws.onmessage = function (evt) {
|
||||
createNotification(LANG(evt["msg_zh"], evt["msg_en"]), evt["level"]);
|
||||
} else if (evt["type"] == "cancelSelection") { //试运行点击元素后取消选中元素
|
||||
clearEl();
|
||||
} else if (evt["type"] == "trial"){
|
||||
trial(evt);
|
||||
}
|
||||
};
|
||||
|
||||
function LANG(zh, en) {
|
||||
if (global.lang == "zh") {
|
||||
return zh;
|
||||
} else {
|
||||
return en;
|
||||
}
|
||||
}
|
||||
|
||||
export function input(value) {
|
||||
|
||||
export function input(value, batch=false) {
|
||||
let type = "inputText";
|
||||
let useLoop = false;
|
||||
if (batch) {
|
||||
type = "batchInputText";
|
||||
useLoop = true;
|
||||
}
|
||||
let message = {
|
||||
"type": "inputText",
|
||||
"type": type,
|
||||
"history": history.length, //记录history的长度
|
||||
"tabIndex": -1,
|
||||
"xpath": readXPath(global.nodeList[0]["node"], 0),
|
||||
"allXPaths": getElementXPaths(global.nodeList[0]["node"]),
|
||||
"iframe": global.iframe,
|
||||
"value": value,
|
||||
"useLoop": useLoop, //是否使用循环内元素
|
||||
"loopType": 3, //循环类型,3为文本列表
|
||||
};
|
||||
window.stop();
|
||||
let message_action = {
|
||||
@ -171,63 +176,6 @@ export function collectSingle() {
|
||||
// createNotification(LANG("采集成功", "Collect successfully"), "success");
|
||||
}
|
||||
|
||||
function createNotification(text, type="info") {
|
||||
// 创建通知元素
|
||||
let notification = document.createElement('div');
|
||||
notification.className = 'notification_of_easyspider'; // 使用 class 方便后续添加样式
|
||||
notification.setAttribute("data-timestamp", new Date().getTime()); // 用于清除通知
|
||||
// 设置通知文本
|
||||
notification.innerText = text;
|
||||
|
||||
// 定义与添加样式
|
||||
let cssText = `
|
||||
position: fixed;
|
||||
bottom: 20px; /* 距底部20px */
|
||||
right: -320px; /* 初始位置在屏幕右侧,假设通知框宽度320px */
|
||||
min-width: 300px;
|
||||
padding: 10px 20px;
|
||||
color: white;
|
||||
z-index: 2147483641;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
|
||||
transition: right 0.5s ease-in-out; /* 动画效果 */
|
||||
`;
|
||||
notification.style.cssText = cssText;
|
||||
|
||||
if (type === "success") {
|
||||
notification.style.backgroundColor = 'rgb(103, 194, 58)';
|
||||
} else if (type === "info") {
|
||||
notification.style.backgroundColor = '#00a8ff';
|
||||
} else if (type === "warning") {
|
||||
notification.style.backgroundColor = 'rgb(230, 162, 60)';
|
||||
} else if (type === "error") {
|
||||
notification.style.backgroundColor = '#ff6b6b';
|
||||
}
|
||||
|
||||
// 将通知添加到页面中
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// 触发动画,通知从右向左滑入
|
||||
setTimeout(function () {
|
||||
notification.style.right = '20px'; // 调整距离左边的位置
|
||||
}, 100);
|
||||
// let removeXPathText = text.split("是否正确:")[0].split("is correct:")[0];
|
||||
// let timeoutInterval = 1500 * removeXPathText.length / 5;
|
||||
let timeoutInterval = 1500 * text.length / 5;
|
||||
// 设置退出动画,通知从右向左滑出
|
||||
setTimeout(function () {
|
||||
notification.style.right = '-320px'; // 向左退出
|
||||
// 确定动画结束后移除通知
|
||||
notification.addEventListener('transitionend', function () {
|
||||
if (notification.parentNode === document.body) {
|
||||
document.body.removeChild(notification); // 避免移除已经不存在的元素
|
||||
}
|
||||
});
|
||||
}, timeoutInterval + 500); // 通知停留时间加上动画时间
|
||||
}
|
||||
|
||||
setInterval(function () {
|
||||
let notifications = document.getElementsByClassName("notification_of_easyspider");
|
||||
for (let i = 0; i < notifications.length; i++) {
|
||||
|
@ -36,7 +36,8 @@
|
||||
v-on:mousedown="selectDescendents">选中子元素</a> <span title="应选尽选模式,如想使用其他模式请先选中全部再选中子元素">☺</span></div>
|
||||
<div v-if="!selectedDescendents && !selectStatus" id="Single">
|
||||
<div v-if="tname()=='选择框'"><a v-on:mousedown="changeSelect">切换下拉选项</a><span title=""></span></div>
|
||||
<div v-if="tname()=='文本框'"><a v-on:mousedown="setInput">输入文字</a><span title=""></span></div>
|
||||
<div v-if="tname()=='文本框'"><a v-on:mousedown="setInput(false)">输入文字</a><span title=""></span></div>
|
||||
<div v-if="tname()=='文本框'"><a v-on:mousedown="setInput(true)">批量输入文字</a><span title=""></span></div>
|
||||
<div v-if="tname()!='图片'"><a v-on:mousedown="getText">采集该{{ tname() }}的文本</a><span
|
||||
title="采集文本"></span></div>
|
||||
<div v-if="tname()=='选择框'"><a v-on:mousedown="getSelectedValue">采集当前选中项的值</a><span title=""></span></div>
|
||||
@ -170,10 +171,10 @@
|
||||
<div><a v-on:mousedown="getCurrentTitle">Collect Title of current page</a><span title="Title of this page">☺</span></div>
|
||||
<div><a v-on:mousedown="getCurrentURL">Collect URL of current page</a><span title="URL of this page">☺</span></div>
|
||||
</div>
|
||||
<p style="color:black">● Mouse move to smiling face ☺ to see operation help.</p>
|
||||
<p style="color:black">● When your mouse moves to the element, please <strong>right-click</strong> your
|
||||
<p style="color:black; margin-top: 10px">● Mouse move to smiling face ☺ to see operation help.</p>
|
||||
<p style="color:black; margin-top: 10px">● When your mouse moves to the element, please <strong>right-click</strong> your
|
||||
mouse button or press <strong>F7</strong> on the keyboard to select it.</p>
|
||||
<p style="color:black">● If this toolbox blocks the page element, you can click the × button in the
|
||||
<p style="color:black; margin-top: 10px">● If this toolbox blocks the page element, you can click the × button in the
|
||||
lower right corner of this toolbox to close it.</p>
|
||||
<p style="color:black; margin-top: 10px">● When clicked with the left mouse button, the page will also respond, but this click operation will not be recorded in the task flow. Similarly, if you want to input in a text box but do not want the action to be recorded , you can move the mouse to the text box and press <strong>F9</strong> on the keyboard to input.</p>
|
||||
<p style="color:black; margin-top: 10px">● If you accidentally left-click on an element and cause the page to jump, simply go back or switch back to the tab.</p>
|
||||
@ -193,7 +194,8 @@
|
||||
<div v-if="!selectedDescendents && !selectStatus" id="Single">
|
||||
<!-- <div v-if="tname()=='selection box'"> <a>循环切换该下拉项</a><span title="">☺</span></div> -->
|
||||
<div v-if="tname()=='选择框'"><a v-on:mousedown="changeSelect">Change selection option</a><span title=""></span></div>
|
||||
<div v-if="tname()=='文本框'"><a v-on:mousedown="setInput">Input Text</a><span title=""></span>
|
||||
<div v-if="tname()=='文本框'"><a v-on:mousedown="setInput(false)">Input Text</a><span title=""></span>
|
||||
<div v-if="tname()=='文本框'"><a v-on:mousedown="setInput(true)">Input Text (Batch)</a><span title=""></span>
|
||||
</div>
|
||||
<div v-if="tname()!='图片'"><a v-on:mousedown="getText">Extract {{ tname() | toEng }}'s text</a><span
|
||||
title="collect text"></span></div>
|
||||
@ -362,6 +364,7 @@ export default {
|
||||
el: '#realcontent',
|
||||
data: {
|
||||
lang: global.lang,
|
||||
batch: false, //是否为批量输入模式
|
||||
option: 0,
|
||||
list: {nl: global.nodeList, opp: global.outputParameters},
|
||||
valTable: [], // 用来存储转换后的参数列表
|
||||
@ -578,7 +581,8 @@ export default {
|
||||
// global.nodeList[0]["node"].click(); //点击元素
|
||||
clearEl();
|
||||
},
|
||||
setInput: function () { //输入文字
|
||||
setInput: function (batch=false) { //输入文字
|
||||
this.batch = batch;
|
||||
this.page = 1;
|
||||
this.$nextTick(function () { //下一时刻获得焦点
|
||||
document.getElementById("WTextBox").focus();
|
||||
@ -591,7 +595,7 @@ export default {
|
||||
// } else{
|
||||
// global.nodeList[0]["node"].setAttribute("value", ""); // 先设置为空,再设置输入 box内容
|
||||
// }
|
||||
input(this.text); // 设置输入
|
||||
input(this.text, this.batch); // 设置输入
|
||||
this.text = "";
|
||||
clearEl();
|
||||
},
|
||||
|
150
Extension/manifest_v3/src/content-scripts/trail.js
Normal file
150
Extension/manifest_v3/src/content-scripts/trail.js
Normal file
@ -0,0 +1,150 @@
|
||||
import {LANG} from "./global.js";
|
||||
|
||||
//试运行操作标记
|
||||
export function trial(evt) {
|
||||
let node = JSON.parse(evt["message"]["message"]["node"]);
|
||||
let parentNode = JSON.parse(evt["message"]["message"]["parentNode"]);
|
||||
let parameters = node.parameters;
|
||||
let type = evt["message"]["message"]["type"];
|
||||
console.log("parameters", parameters);
|
||||
if (type == 0) {
|
||||
let option = node.option;
|
||||
console.log("option", option);
|
||||
if (option == 2 || option == 4 || option == 6 || option == 7) {
|
||||
let xpath = parameters.xpath;
|
||||
if (parameters.useLoop && option != 4 && option != 6) {
|
||||
let parentXPath = parentNode.parameters.xpath;
|
||||
xpath = parentXPath + xpath;
|
||||
}
|
||||
let element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
// if (element != null) {
|
||||
// //移动到元素位置
|
||||
// element.scrollIntoView({block: "center", inline: "center"});
|
||||
// }
|
||||
} else if (option == 10) { //条件分支
|
||||
let condition = parameters.class;
|
||||
let result = 0;
|
||||
let additionalInfo = "";
|
||||
if (condition == 0) { //无条件
|
||||
result = 1;
|
||||
} else if (condition == 1) { //当前页面包含文本
|
||||
let value = parameters.value;
|
||||
let element = document.getElementsByTagName("body")[0];
|
||||
let bodyText = element.innerText;
|
||||
let outcome = bodyText.indexOf(value) >= 0;
|
||||
if (outcome) {
|
||||
result = 1;
|
||||
}
|
||||
} else if (condition == 2) { //当前页面包含元素,xpath
|
||||
let xpath = parameters.value;
|
||||
let element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
if (element != null) {
|
||||
result = 1;
|
||||
}
|
||||
} else if (condition == 3) { //当前循环项包含文本,xpath
|
||||
let value = parameters.value;
|
||||
let xpath = parentNode.parameters.xpath;
|
||||
let element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
if (element != null) {
|
||||
let elementText = element.innerText;
|
||||
let outcome = elementText.indexOf(value) >= 0;
|
||||
if (outcome) {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
if(result == 0){
|
||||
additionalInfo = LANG(",注意只会检索第一个匹配到的循环项", ", note that only the first matching loop item will be retrieved");
|
||||
}
|
||||
} else if (condition == 4) { //当前循环项包含元素,xpath
|
||||
let xpath = parentNode.parameters.xpath;
|
||||
let value = parameters.value;
|
||||
// full_path = "(" + parentPath + ")" + \
|
||||
// "[" + str(index + 1) + "]" + \
|
||||
// relativeXPath + content_type
|
||||
xpath = "(" + xpath + ")" + "[" + "1" + "]" + value;
|
||||
let element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
if (element != null) {
|
||||
result = 1;
|
||||
}
|
||||
if(result == 0){
|
||||
additionalInfo = LANG(",注意只会检索第一个匹配到的循环项", ", note that only the first matching loop item will be retrieved");
|
||||
}
|
||||
} else if (condition == 5 || condition == 7) { //从主程序传入的结果
|
||||
result = evt["message"]["message"]["result"];
|
||||
if(condition == 7 && result == 0){
|
||||
additionalInfo = LANG(",注意只会检索第一个匹配到的循环项", ", note that only the first matching loop item will be retrieved");
|
||||
}
|
||||
} else {
|
||||
result = 2;
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
createNotification(LANG("当前页面下,条件分支“" + node.title + "”的条件未满足" + additionalInfo, "The condition of the conditional branch: " + node.title + " is not met on the current page" + additionalInfo), "warning");
|
||||
} else if (result == 1) {
|
||||
createNotification(LANG("当前页面下,条件分支“" + node.title + "”的条件已满足" + additionalInfo, "The condition of the conditional branch: " + node.title + " is met on the current page" + additionalInfo), "success");
|
||||
} else if (result == 2) {
|
||||
createNotification(LANG("不支持此条件判断类型的动态调试,请在任务正式调用阶段测试是否有效。", "Dynamic debugging of this condition judgment type is not supported. Please test whether it is valid in the formal call stage."), "info");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function createNotification(text, type = "info") {
|
||||
// 创建通知元素
|
||||
let notification = document.createElement('div');
|
||||
notification.className = 'notification_of_easyspider'; // 使用 class 方便后续添加样式
|
||||
notification.setAttribute("data-timestamp", new Date().getTime()); // 用于清除通知
|
||||
// 设置通知文本
|
||||
notification.innerText = text;
|
||||
|
||||
// 定义与添加样式
|
||||
let cssText = `
|
||||
position: fixed;
|
||||
bottom: 20px; /* 距底部20px */
|
||||
right: -320px; /* 初始位置在屏幕右侧,假设通知框宽度320px */
|
||||
min-width: 300px;
|
||||
padding: 10px 20px;
|
||||
color: white;
|
||||
z-index: 2147483641;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
|
||||
transition: right 0.5s ease-in-out; /* 动画效果 */
|
||||
`;
|
||||
notification.style.cssText = cssText;
|
||||
|
||||
if (type === "success") {
|
||||
notification.style.backgroundColor = 'rgb(103, 194, 58)';
|
||||
} else if (type === "info") {
|
||||
notification.style.backgroundColor = '#00a8ff';
|
||||
} else if (type === "warning") {
|
||||
notification.style.backgroundColor = 'rgb(230, 162, 60)';
|
||||
} else if (type === "error") {
|
||||
notification.style.backgroundColor = '#ff6b6b';
|
||||
notification.style.bottom = '70px';
|
||||
}
|
||||
|
||||
// 将通知添加到页面中
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// 触发动画,通知从右向左滑入
|
||||
setTimeout(function () {
|
||||
notification.style.right = '20px'; // 调整距离左边的位置
|
||||
}, 100);
|
||||
// let removeXPathText = text.split("是否正确:")[0].split("is correct:")[0];
|
||||
// let timeoutInterval = 1500 * removeXPathText.length / 5;
|
||||
let timeoutInterval = 1500 * text.length / 5;
|
||||
// 设置退出动画,通知从右向左滑出
|
||||
setTimeout(function () {
|
||||
notification.style.right = '-320px'; // 向左退出
|
||||
// 确定动画结束后移除通知
|
||||
notification.addEventListener('transitionend', function () {
|
||||
if (notification.parentNode === document.body) {
|
||||
document.body.removeChild(notification); // 避免移除已经不存在的元素
|
||||
}
|
||||
});
|
||||
}, timeoutInterval + 500); // 通知停留时间加上动画时间
|
||||
}
|
@ -164,7 +164,8 @@
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
margin-bottom: 0px;
|
||||
background-color: azure;
|
||||
/*background-color: azure;*/
|
||||
background-color: aliceblue;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user