Ctrl+Click & Hint of mobile mode & Default Browser Location & Click with coordinate

This commit is contained in:
naibo 2023-12-18 16:23:00 +08:00
parent ea2d679dd4
commit 7901ee2877
17 changed files with 138 additions and 60 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
{"webserver_address":"http://localhost","webserver_port":8074,"user_data_folder":"./user_data","debug":false,"copyright":1,"sys_version":"x64","mysql_config_path":"./mysql_config.json","absolute_user_data_folder":"/Users/naibo/Documents/EasySpider/ElectronJS/user_data"}
{"webserver_address":"http://localhost","webserver_port":8074,"user_data_folder":"","debug":false,"copyright":1,"sys_version":"x64","mysql_config_path":"./mysql_config.json","absolute_user_data_folder":"/Users/naibo/Documents/EasySpider/ElectronJS/user_data"}

View File

@ -108,8 +108,8 @@ let invoke_window = null;
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 550,
height: 750,
width: 600,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'src/js/preload.js')
},
@ -231,7 +231,7 @@ async function findElementAcrossAllWindows(msg, notifyBrowser = true, scrollInto
xpath = msg.message.xpath;
} catch {
//如果msg.pathList存在说明是循环中的元素
if(msg.pathList != undefined && msg.pathList != null && msg.pathList != ""){
if (msg.pathList != undefined && msg.pathList != null && msg.pathList != "") {
xpath = msg.pathList[0].trim();
} else {
xpath = msg.xpath;
@ -301,15 +301,18 @@ async function beginInvoke(msg, ws) {
// This method has to be called on macOS before changing the window's bounds, otherwise it will throw an error.
// It will prompt an accessibility permission request dialog, if needed.
if (process.platform != "linux" && process.platform != "darwin") {
const {windowManager} = require("node-window-manager");
const window = windowManager.getActiveWindow();
console.log(window);
windowManager.requestAccessibility();
// Sets the active window's bounds.
let size = screen.getPrimaryDisplay().workAreaSize
let width = parseInt(size.width)
let height = parseInt(size.height * 0.6)
window.setBounds({x: 0, y: size.height * 0.4, height: height, width: width});
// 非用户信息模式下,设置窗口位置
if (config.user_data_folder == null || config.user_data_folder == undefined || config.user_data_folder == "") {
const {windowManager} = require("node-window-manager");
const window = windowManager.getActiveWindow();
console.log(window);
windowManager.requestAccessibility();
// Sets the active window's bounds.
let size = screen.getPrimaryDisplay().workAreaSize
let width = parseInt(size.width)
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();
@ -337,7 +340,7 @@ async function beginInvoke(msg, ws) {
if (type.indexOf("Click") >= 0 || type.indexOf("Move") >= 0) {
let element = await findElementAcrossAllWindows(message, notifyBrowser = true, scrollIntoView = false);
if (type.indexOf("Click") >= 0) {
await click_element(element);
await click_element(element, type);
} else if (type.indexOf("Move") >= 0) {
await driver.actions().move({origin: element}).perform();
}
@ -366,6 +369,9 @@ async function beginInvoke(msg, ws) {
}
xpath = parent_xpath + xpath;
}
if (xpath.includes("point(")) {
xpath = "//body";
}
let elementInfo = {"iframe": parameters.iframe, "xpath": xpath, "id": -1};
//用于跳转到元素位置
let element = await findElementAcrossAllWindows(elementInfo);
@ -492,8 +498,13 @@ async function beginInvoke(msg, ws) {
}
}
} else if (option == 2 || option == 7) { //点击事件
let elementInfo = {"iframe": parameters.iframe, "xpath": parameters.xpath, "id": -1};
if (parameters.useLoop) {
let xpath = parameters.xpath;
let point = parameters.xpath;
if (xpath.includes("point(")) {
xpath = "//body"
}
let elementInfo = {"iframe": parameters.iframe, "xpath": xpath, "id": -1};
if (parameters.useLoop && !parameters.xpath.includes("point(")) {
let parent_node = JSON.parse(msg.message.parentNode);
let parent_xpath = parent_node.parameters.xpath;
if (parent_node.parameters.loopType == 2) {
@ -504,7 +515,11 @@ async function beginInvoke(msg, ws) {
let element = await findElementAcrossAllWindows(elementInfo, notifyBrowser = false); //通过此函数找到元素并切换到对应的窗口
await execute_js(parameters.beforeJS, element, parameters.beforeJSWaitTime);
if (option == 2) {
await click_element(element);
if (parameters.xpath.includes("point(")) {
await click_element(element, point);
} else {
await click_element(element);
}
let alertHandleType = parameters.alertHandleType;
if (alertHandleType == 1) {
try {
@ -731,11 +746,25 @@ async function beginInvoke(msg, ws) {
}
}
async function click_element(element) {
async function click_element(element, type = "click") {
try {
await element.click();
//ctrl+click
// await driver.actions().keyDown(Key.CONTROL).click(element).keyUp(Key.CONTROL).perform();
if (type == "loopClickEvery") {
await driver.actions().keyDown(Key.CONTROL).click(element).keyUp(Key.CONTROL).perform();
} else if (type.includes("point(")) {
//point(10, 20)表示点击坐标为(10, 20)的位置
let point = type.substring(6, type.length - 1).split(",");
let x = parseInt(point[0]);
let y = parseInt(point[1]);
// let actions = driver.actions();
// await actions.move({origin: element}).perform();
// await actions.move({x: x, y: y}).perform();
// await actions.click().perform();
let script = `document.elementFromPoint(${x}, ${y}).click();`;
await driver.executeScript(script);
} else {
await element.click();
}
} catch (e) {
console.log(e);
await driver.executeScript("arguments[0].click();", element);
@ -819,12 +848,12 @@ wss.on('connection', function (ws) {
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});
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;
@ -894,6 +923,8 @@ async function runBrowser(lang = "en", user_data_folder = '', mobile = false) {
options.addArguments("--user-data-dir=" + dir);
config.user_data_folder = user_data_folder;
fs.writeFileSync(path.join(task_server.getDir(), "config.json"), JSON.stringify(config));
} else {
config.user_data_folder = "";
}
if (mobile) {
const mobileEmulation = {

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -94,9 +94,9 @@ For individual users, EasySpider is a completely free and ad-free open-source so
</div>
<div v-else-if="step == 1">
<h4 style="margin-top: 20px">Please select design mode</h4>
<p style="margin-top: 20px; text-align: justify; width:310px; margin-left: 18%">
Clean Mode: Start with a clean browser with no cookie/user data.</p>
<p style="text-align: justify; width:310px; margin-left: 18%">
<p style="margin-top: 20px; text-align: justify; width:290px; margin-left: 25%">
Clean Mode: Start with a clean browser with no cookies/user data.</p>
<p style="text-align: justify; width:290px; margin-left: 25%">
Data Mode: Start with a browser that stores user data such as website login information and cookies.</p>
<p><a @click="startDesign('en')"
class="btn btn-primary btn-lg"
@ -177,10 +177,10 @@ For individual users, EasySpider is a completely free and ad-free open-source so
</div>
<div v-else-if="step == 1">
<h4 style="margin-top: 20px">请选择设计模式</h4>
<p style="margin-top: 20px; text-align: left; width:320px; margin-left: 18%">
<p style="margin-top: 20px; text-align: left; width:310px; margin-left: 24%">
纯净版浏览器:无任何用户信息的浏览器。</p>
<p style="text-align: left; width:320px; margin-left: 18%">
带用户信息的浏览器保存有用户数据如网站的登录信息cookie的浏览器。</p>
<p style="text-align: left; width:310px; margin-left: 24%">
带用户信息的浏览器保存有用户数据如网站的登录信息cookies的浏览器。</p>
<p><a @click="startDesign('zh')"
class="btn btn-primary btn-lg"
style="margin-top: 15px; width: 320px;height:60px;padding-top:12px;color:white;">使用纯净版浏览器设计</a>
@ -203,7 +203,7 @@ For individual users, EasySpider is a completely free and ad-free open-source so
<h4 style="margin-top: 20px">指定用户信息目录</h4>
<div style="margin: 0 auto; width:90%">
<p style="margin-top: 20px; text-align: left">
请在下方指定用户信息目录。设置后浏览器将加载目录里的cookie如用户的登录信息等内容目录不变的情况下每次设计和执行时浏览器都会加载此目录里的数据。</p>
请在下方指定用户信息目录。设置后浏览器将加载目录里的cookies,如用户的登录信息等内容,目录不变的情况下,每次设计和执行时浏览器都会加载此目录里的数据。</p>
<p style="margin-top: 10px; text-align: left">例如:设置了./user_data文件夹并在设计过程中登录了知乎网站则下次再次设计或者执行任务时指定./user_data文件夹打开知乎网站页面会仍然保留之前的登录状态。</p>
<p style="margin-top: 10px; text-align: left">如果有多套配置,可以设置不同的目录,每个目录为一套,如果目录不存在将会被自动创建。</p>
<p><textarea class="form-control" style="min-height: 50px;"

View File

@ -146,7 +146,7 @@
<p><input spellcheck=false onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>Use element located by xpath relative to the loop</p>
</div>
<div>
<label>XPath: <span style="font-size: 30px!important;" title="Relative XPATH writing: start with /, e.g. the loop item XPATH is /html/body/div[1], your input is /*[@id='tab-customer'], then the final addressed xpath is: /html/body/div[1]/*[@id='tab-customer']"></span></label>
<label>XPath (Or use "point(10,10)" to represent clicking on the web page at coordinate position (10, 10), suitable for the situation when need to click on a blank area to leave popup dialog): <span style="font-size: 30px!important;" title="Relative XPATH writing: start with /, e.g. the loop item XPATH is /html/body/div[1], your input is /*[@id='tab-customer'], then the final addressed xpath is: /html/body/div[1]/*[@id='tab-customer']"></span></label>
<textarea spellcheck=false onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
<p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode['parameters']['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">Click here to view other equivalent XPath expressions</button></p>
</div>
@ -690,11 +690,11 @@ If the expression returns a value greater than 0 or evaluates to True, the opera
<input spellcheck=false onkeydown="inputDelete(event)" id="serviceDescription" name="serviceDescription" class="form-control"></input>
<label>Export Data Format (Excel/CSV/TXT/Database):</label>
<select id="outputFormat" class="form-control">
<option value = "xlsx">XLSX (EXCEL, we suggest using the CSV format if the length of a single cell exceeds 500)</option>
<option value = "csv">CSV</option>
<option value = "txt">TXT</option>
<option value = "json">JSON</option>
<option value = "mysql">MySQL Database</option>
<option value="xlsx">XLSX (Excel file, recommended use CSV format when single cell exceeds 500 characters)</option>
<option value="csv">CSV (Recommended for collecting long articles)</option>
<option value="txt">TXT</option>
<option value="json">JSON</option>
<option value="mysql">MySQL Database (recommended for large amounts of data)</option>
</select>
<label>Export File Name/Database Table Name (Can use ../ to represent relative path to change the file save location,the keyword "current_time" will be replaced with the timestamp when the task is executed):</label>
<input spellcheck=false onkeydown="inputDelete(event)" value="current_time" id="saveName" class="form-control"></input>

View File

@ -146,7 +146,7 @@
<p><input spellcheck=false onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>使用相对循环内的XPath定位到的元素</p>
</div>
<div>
<label>XPath <span style="font-size: 30px!important;" title="相对XPATH写法:以/开头如循环项XPATH为/html/body/div[1],您的输入为/*[@id='tab-customer'],则最终寻址的xpath为/html/body/div[1]/*[@id='tab-customer']"></span></label>
<label>XPath或者用point(10,10)表示点击网页坐标位置(10, 10)以用来点击空白区域推出弹窗对话框等) <span style="font-size: 30px!important;" title="相对XPATH写法:以/开头如循环项XPATH为/html/body/div[1],您的输入为/*[@id='tab-customer'],则最终寻址的xpath为/html/body/div[1]/*[@id='tab-customer']"></span></label>
<textarea spellcheck=false onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
<p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode['parameters']['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">点此查看其他等价的XPath</button></p>
</div>
@ -691,10 +691,10 @@ print(emotlib.emoji()) # 使用其中的函数。
<label>导出数据格式Excel/CSV/TXT/数据库,<a href="https://www.bilibili.com/video/BV1os4y1679S/" target="_blank">查看MySQL操作教程</a></label>
<select id="outputFormat" class="form-control">
<option value = "xlsx">XLSX即EXCEL文件建议单个单元格长度超过500时使用CSV格式存储</option>
<option value = "csv">CSV</option>
<option value = "csv">CSV(采集长文章推荐使用此格式)</option>
<option value = "txt">TXT</option>
<option value = "json">JSON</option>
<option value = "mysql">MySQL数据库</option>
<option value = "mysql">MySQL数据库(大量数据推荐使用)</option>
</select>
<label>导出文件名/数据库表格名称(可使用../表示相对路径以改变文件保存位置名称中的“current_time”会被替换为执行任务时的时间戳</label>
<input spellcheck=false onkeydown="inputDelete(event)" value="current_time" id="saveName" class="form-control"></input>

View File

@ -42,6 +42,19 @@
<!-- <button type="submit" id="sendWithCookie" style="margin-top: 10px" class="btn btn-primary">{{"Start Design with cookie data from local browser~带本地数据开始设计" | lang}}</button>-->
<!-- </div>-->
<div style="margin-top: 20px">
<div v-if="mobile">
<div v-if="language=='zh'">
<p>提示手机模式设计时如果没有出现操作提示框请按键盘的Ctrl+Shift+I组合键MacOS为Command+Option+I组合键打开开发者工具然后<b>双击</b>“切换设备”按钮,即可正常出现并使用操作提示框。</p>
</div>
<div v-else>
<p>Tip: If the operation prompt box does not appear when designing in mobile mode, please press the
Ctrl+Shift+I key combination (MacOS is the Command+Option+I key combination) to open the
developer tool, and then <b>double-click</b> the "Toggle Device Toolbar" button, you can normally appear and use the operation toolbox.</p>
</div>
<img src="../img/toggle.png" alt="" style="width: 100%;height: 100%">
<p></p>
</div>
<h5>{{"Example 1~示例1" | lang}}</h5>
<p>{{"(Right click) Select a large product block -> Click the 'Select All' option -> Click the 'Select Child Elements' option -> Click the 'Collect Data' option, you can collect the information of all products, and will be saved by sub-field. ~ (右键)选中一个大商品块 -> 自动检测到同类型商品块 -> 点击“选中全部”选项 -> 点击“选中子元素”选项 -> 点击“采集数据”选项,即可采集到所有商品的所有信息,并分成不同字段保存。" | lang}}</p>
<img src="../img/animation_zh.gif" alt="" style="width: 100%;height: 100%">
@ -54,17 +67,18 @@
</div>
</div>
</div>
</body>
</html>
<script src="global.js"></script>
<script>
var app = new Vue({
let app = new Vue({
el: '#newTask',
data: {
backEndAddressServiceWrapper: getUrlParam("backEndAddressServiceWrapper"),
user_data_folder: "",
mobile: getUrlParam("mobile"),
language: getUrlParam("lang"),
},
methods: {
gotoHome: function () {

View File

@ -50,7 +50,7 @@
<p style="word-wrap: break-word;word-break: break-all;overflow: hidden;max-height: 100px;">{{"Create Time:~创建时间:" | lang}} {{dateFormat(task["create_time"])}}</p>
<p style="word-wrap: break-word;word-break: break-all;overflow: hidden;max-height: 100px;">{{"Update Time:~更新时间:" | lang}} {{dateFormat(task["update_time"])}}</p>
<p>{{"Operations (Please close this window and select 'Design Task' button if you want to modify task with a browser)~操作(如要带浏览器修改任务流程请关闭此窗口并选择设计任务)" | lang}}</p>
<p><a style="margin-top: 5px" href="javascript:void(0)" v-on:click="modifyTask(task['id'],task['url'])" class="btn btn-primary">{{"Modify Task Workflow~修改任务流程" | lang}}</a>
<p><a style="margin-top: 5px" href="javascript:void(0)" v-on:click="modifyTask(task['id'],task['url'])" class="btn btn-primary">{{"Modify Task~修改任务" | lang}}</a>
<a style="margin-top: 5px" href="javascript:void(0)" v-on:click="invokeTask(task['id'],task['url'])" class="btn btn-primary">{{"Execute Task~执行任务" | lang}}</a></p>
<p>{{"Input Parameters~输入参数" | lang}}</p>
<table class="table table-bordered">

View File

@ -0,0 +1 @@
{"id":296,"name":"知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具","url":"https://www.zsxq.com","links":"https://wx.zsxq.com/dweb2/index/group/48844244282448","create_time":"12/18/2023, 3:49:05 PM","update_time":"12/18/2023, 4:12:12 PM","version":"0.6.0","saveThreshold":10,"quitWaitTime":60,"environment":0,"maximizeWindow":0,"maxViewLength":15,"recordLog":1,"outputFormat":"xlsx","saveName":"current_time","dataWriteMode":1,"inputExcel":"","startFromExit":0,"pauseKey":"p","containJudge":false,"browser":"chrome","desc":"https://www.zsxq.com","inputParameters":[{"id":0,"name":"urlList_0","nodeId":1,"nodeName":"打开网页","value":"https://wx.zsxq.com/dweb2/index/group/48844244282448","desc":"要采集的网址列表,多行以\\n分开","type":"text","exampleValue":"https://wx.zsxq.com/dweb2/index/group/48844244282448"}],"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.zsxq.com","links":"https://wx.zsxq.com/dweb2/index/group/48844244282448","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":8,"tabIndex":-1,"useLoop":false,"xpath":"//*[contains(@class, \"group-file-container\")]/div[2]/div[1]/div[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,"newTab":0,"maxWaitTime":10,"params":[],"alertHandleType":0,"allXPaths":["/html/body/app-root[1]/app-index[1]/div[1]/app-topic-flow[1]/div[1]/app-group-preview[1]/div[1]/div[1]/app-group-file[1]/div[1]/div[2]/div[1]/div[2]","//div[contains(., '【腾讯研究院】全真互')]","//DIV[@class='file-name']","/html/body/app-root/app-index/div/app-topic-flow/div/app-group-preview/div/div/app-group-file/div/div/div[last()-2]/div"]}},{"id":3,"index":3,"parentId":0,"type":0,"option":2,"title":"点击文件详情\n查...","sequence":[],"isInLoop":false,"position":2,"parameters":{"history":8,"tabIndex":-1,"useLoop":false,"xpath":"point(100, 10)","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,"newTab":0,"maxWaitTime":10,"params":[],"alertHandleType":0,"allXPaths":["/html/body/app-root[1]/app-index[1]/div[1]/app-topic-flow[1]/div[1]/app-file-preview[1]/div[1]","//div[contains(., '文件详情查看原主题本')]","//DIV[@class='file-preview-container']","/html/body/app-root/app-index/div/app-topic-flow/div/app-file-preview/div"]}}]}

View File

@ -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", "[28]", "--headless", "0", "--user_data", "0", "--keyboard", "0",
"args": ["--ids", "[61]", "--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"
}

View File

@ -466,9 +466,9 @@ class BrowserThread(Thread):
def run(self):
# 挨个执行程序
for i in range(len(self.links)):
self.print_and_log("正在执行第", i + 1, "/ ", len(self.links), "个链接")
self.print_and_log("正在执行第", i + 1, "/", len(self.links), "个链接")
self.print_and_log("Executing link", i + 1,
"/ ", len(self.links))
"/", len(self.links))
self.executeNode(0)
self.urlId = self.urlId + 1
files = os.listdir("Data/Task_" + str(self.id) + "/" + self.saveName)
@ -1208,7 +1208,7 @@ class BrowserThread(Thread):
if len(elements) == 0:
self.print_and_log("Loop element not found: ",
xpath)
self.print_and_log("找不到循环元素: ", xpath)
self.print_and_log("找不到循环元素", xpath)
index = 0
while index < len(elements):
try:
@ -1258,7 +1258,7 @@ class BrowserThread(Thread):
index = index + 1
except NoSuchElementException:
self.print_and_log("Loop element not found: ", xpath)
self.print_and_log("找不到循环元素: ", xpath)
self.print_and_log("找不到循环元素", xpath)
except Exception as e:
raise
elif int(node["parameters"]["loopType"]) == 2: # 固定元素列表
@ -1303,7 +1303,7 @@ class BrowserThread(Thread):
index, element = self.handleHistory(node, path, thisHistoryURL, thisHistoryLength, index, element=element)
except NoSuchElementException:
self.print_and_log("Loop element not found: ", path)
self.print_and_log("找不到循环元素: ", path)
self.print_and_log("找不到循环元素", path)
index += 1
continue # 循环中找不到元素就略过操作
except Exception as e:
@ -1533,7 +1533,10 @@ class BrowserThread(Thread):
clickPath, self.outputParameters, self)
xpath = replace_field_values(
param["xpath"], self.outputParameters, self)
if param["useLoop"]: # 使用循环的情况下传入的clickPath就是实际的xpath
if xpath.find("point(") >= 0: # 如果xpath中包含point(),说明是相对坐标的点击
index = 0
path = "//body"
elif param["useLoop"]: # 使用循环的情况下传入的clickPath就是实际的xpath
if xpath == "":
path = clickPath
else:
@ -1567,7 +1570,19 @@ class BrowserThread(Thread):
except:
newTab = 0
try:
if click_way == 0: # 用selenium的点击方法
if xpath.find("point(") >= 0: # 如果xpath中包含point(),说明是相对坐标的点击
point = xpath.split("point(")[1].split(")")[0].split(",")
x = int(point[0])
y = int(point[1])
# try:
# actions = ActionChains(self.browser) # 实例化一个action对象
# actions.move_to_element(element).perform()
# actions.move_by_offset(x, y).perform()
# actions.click().perform()
# except Exception as e:
script = "document.elementFromPoint(" + str(x) + "," + str(y) + ").click();"
self.browser.execute_script(script)
elif click_way == 0: # 用selenium的点击方法
try:
actions = ActionChains(self.browser) # 实例化一个action对象
if newTab == 1: # 在新标签页打开

View File

@ -9,8 +9,8 @@ import time
import uuid
# import keyboard
from openpyxl import Workbook, load_workbook
import pandas as pd
import xlsxwriter
# import pandas as pd
# import xlsxwriter
import requests
from urllib.parse import urlparse
import pymysql

View File

@ -320,3 +320,7 @@ let closeButton = document.getElementById("closeButton");
closeButton.addEventListener("click", function() {
toolkit.style.display = "none"; // 隐藏元素
});
let closeButtonLeft = document.getElementById("closeButtonLeft");
closeButtonLeft.addEventListener("click", function() {
toolkit.style.display = "none"; // 隐藏元素
});

View File

@ -3,6 +3,7 @@
<!-- <div id="EasySpiderResizer" style="width: 10px; height: 10px; background-color: black; position: absolute; left: 0; bottom: 0; cursor: ne-resize;"></div>-->
<div id="EasySpiderResizer"
style="width: 10px; height: 10px; position: absolute; left: 0; top: 0; cursor: nw-resize;"></div>
<span id="closeButtonLeft">&#x2716;</span>
<span id="closeButton">&#x2716;</span>
<div v-if="lang == 'zh'">
<div class="tooldrag">操作台点此拖动左上角调整大小</div>
@ -33,7 +34,7 @@
<p style="color:black; margin-top: 10px"> 操作完成后如点击确认采集后任务流程图内没有提取数据操作被添加<strong>重试一次</strong>即可
</p>
<p style="color:black; margin-top: 10px">
如果此操作台把页面元素挡住了可以点击此操作台右下角的×按钮键关闭操作台</p>
如果此操作台把页面元素挡住了可以点击此操作台左下角或右下角的×按钮键关闭操作台</p>
{{ initial() }}
</div>
<div v-if="list.nl.length==1">
@ -243,7 +244,7 @@
again.</p>
<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>
lower left/right corner of this toolbox to close it.</p>
{{ initial() }}
</div>
<div v-if="list.nl.length==1">

View File

@ -188,11 +188,23 @@
#closeButton {
position: absolute;
bottom: 0;
right: 0;
right: 4px;
cursor: pointer;
padding: 5px;
opacity: 0.05;
opacity: 0.2;
}
#closeButton:hover {
opacity: 1.0;
}
#closeButtonLeft {
position: absolute;
bottom: 0;
left: 4px;
cursor: pointer;
padding: 5px;
opacity: 0.2;
}
#closeButtonLeft:hover {
opacity: 1.0;
}