Bug Fix and Picture download update

This commit is contained in:
naibo 2023-12-12 01:30:13 +08:00
parent 17587218f5
commit c3bab575f4
14 changed files with 205 additions and 124 deletions

View File

@ -0,0 +1 @@
{"id":6,"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"]}}]}

View File

@ -1 +1 @@
{"id":255,"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, 7:30: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":8,"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"]}}]} {"id":255,"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"]}}]}

View File

@ -198,7 +198,7 @@ async function findElement(driver, by, value, iframe = false) {
} }
} }
async function findElementAcrossAllWindows(msg, notifyBrowser = true) { async function findElementAcrossAllWindows(msg, notifyBrowser = true, scrollIntoView = true) {
let handles = await driver.getAllWindowHandles(); let handles = await driver.getAllWindowHandles();
// console.log("handles", handles); // console.log("handles", handles);
let content_handle = current_handle; let content_handle = current_handle;
@ -266,6 +266,16 @@ async function findElementAcrossAllWindows(msg, notifyBrowser = true) {
if (element == null && notifyBrowser) { if (element == null && notifyBrowser) {
notify_browser("无法找到元素请检查XPath是否正确" + xpath, "Cannot find the element, please check if the XPath is correct: " + xpath, "warning"); notify_browser("无法找到元素请检查XPath是否正确" + xpath, "Cannot find the element, please check if the XPath is correct: " + xpath, "warning");
} }
if (element != null && scrollIntoView) {
// 浏览器切换到元素位置稍微靠上的位置
try {
// let script = `arguments[0].scrollIntoView(true);`;
let script = `arguments[0].scrollIntoView({block: "center", inline: "center"});`;
await driver.executeScript(script, element);
} catch (e) {
console.log("Cannot scrollIntoView");
}
}
return element; return element;
} }
@ -308,7 +318,7 @@ async function beginInvoke(msg, ws) {
keyInfo = keyInfo.replace(/<enter>/gi, ''); keyInfo = keyInfo.replace(/<enter>/gi, '');
enter = true; enter = true;
} }
let element = await findElementAcrossAllWindows(msg); let element = await findElementAcrossAllWindows(msg, notifyBrowser = true, scrollIntoView = false);
await element.sendKeys(Key.HOME, Key.chord(Key.SHIFT, Key.END), keyInfo); await element.sendKeys(Key.HOME, Key.chord(Key.SHIFT, Key.END), keyInfo);
if (enter) { if (enter) {
await element.sendKeys(Key.ENTER); await element.sendKeys(Key.ENTER);
@ -321,7 +331,7 @@ async function beginInvoke(msg, ws) {
let type = message.type; let type = message.type;
console.log("FROM Browser: ", message); console.log("FROM Browser: ", message);
if (type.indexOf("Click") >= 0 || type.indexOf("Move") >= 0) { if (type.indexOf("Click") >= 0 || type.indexOf("Move") >= 0) {
let element = await findElementAcrossAllWindows(message); let element = await findElementAcrossAllWindows(message, notifyBrowser = true, scrollIntoView = false);
if (type.indexOf("Click") >= 0) { if (type.indexOf("Click") >= 0) {
await click_element(element); await click_element(element);
} else if (type.indexOf("Move") >= 0) { } else if (type.indexOf("Move") >= 0) {

View File

@ -38,6 +38,7 @@
border-radius: 7px; border-radius: 7px;
/*border: skyblue solid;*/ /*border: skyblue solid;*/
border: #007bff solid; border: #007bff solid;
border-color: transparent;
/*background: rgb(73, 156, 189);*/ /*background: rgb(73, 156, 189);*/
background: rgb(10, 120, 200); background: rgb(10, 120, 200);
color: white; color: white;

View File

@ -113,10 +113,10 @@
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['maxWaitTime']" type="number" required></input> <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['maxWaitTime']" type="number" required></input>
<label>After executed, whether scroll down:</label> <label>After executed, whether scroll down:</label>
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control"> <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
<option value = 0>No scrolling</option> <option :vaule = 0>No scrolling</option>
<option value = 1>Scroll one screen</option> <option :vaule = 1>Scroll one screen</option>
<option value = 2>Scroll to the end</option> <option :vaule = 2>Scroll to the end</option>
<option value = 3>Keep scrolling until the page data does not change</option> <option :vaule = 3>Keep scrolling until the page data does not change</option>
</select> </select>
<label>Scroll Times (the wait time after scrolling <b>ineffective</b> when the scrolling type is set to <b>no scrolling</b>):</label> <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> <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
@ -159,10 +159,10 @@
</select> </select>
<label>Whether to scroll down after clicking:</label> <label>Whether to scroll down after clicking:</label>
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control"> <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
<option value = 0>No Scrolling</option> <option :vaule = 0>No Scrolling</option>
<option value = 1>Scroll one screen</option> <option :vaule = 1>Scroll one screen</option>
<option value = 2>Scroll to the end</option> <option :vaule = 2>Scroll to the end</option>
<option value = 3>Keep scrolling until the page data does not change</option> <option :vaule = 3>Keep scrolling until the page data does not change</option>
</select> </select>
<label>Scroll Times:</label> <label>Scroll Times:</label>
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input> <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
@ -414,8 +414,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> 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 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"> <p><select v-model='nowNode["parameters"]["recordASField"]' class="form-control">
<option value = 0>No</option> <option :vaule = 0>No</option>
<option value = 1>Yes</option> <option :vaule = 1>Yes</option>
</select></p> </select></p>
<p><label>Convert parameter type to:</label> <p><label>Convert parameter type to:</label>
<select v-model='nowNode["parameters"]["paraType"]' class="form-control"> <select v-model='nowNode["parameters"]["paraType"]' class="form-control">
@ -490,10 +490,10 @@ Please note that this feature does not support assigning values to variables. In
<div v-if="!useLoop"> <div v-if="!useLoop">
<p>Option switch Mode</p> <p>Option switch Mode</p>
<select class="form-control" v-model='nowNode["parameters"]["optionMode"]'> <select class="form-control" v-model='nowNode["parameters"]["optionMode"]'>
<option value=0>Switch to the next option</option> <option :vaule = 0>Switch to the next option</option>
<option value=1>Switch options by index (0 is the first option)</option> <option :vaule = 1>Switch options by index (0 is the first option)</option>
<option value=2>Switch options by option value</option> <option :vaule = 2>Switch options by option value</option>
<option value=3>Switch options by option text</option> <option :vaule = 3>Switch options by option text</option>
</select> </select>
<p>Set value (not applicable for "Switch to the next option" mode)</p> <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> <input class="form-control" id="selectValue" v-model='nowNode["parameters"]["optionValue"]' autoFocus="autofocus" type="text"></input>
@ -568,9 +568,9 @@ If the expression returns a value greater than 0 or evaluates to True, the loop
<div> <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> <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"> <select v-model='nowNode["parameters"]["breakMode"]' class="form-control" style="font-weight: bold">
<option value=0>Do not set script (even if a script is written below, it will not be executed)</option> <option :vaule = 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 :vaule = 1>JavaScript script (start with 'return ')</option>
<option value=2>Operating system-level command</option> <option :vaule = 2>Operating system-level command</option>
</select> </select>
<div> <div>
<textarea style="margin-top: 10px" onkeydown="inputDelete(event)" class="form-control" rows="2" <textarea style="margin-top: 10px" onkeydown="inputDelete(event)" class="form-control" rows="2"
@ -584,10 +584,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> <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> <label>After executed, whether scroll down:</label>
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control"> <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
<option value = 0>No Scrolling</option> <option :vaule = 0>No Scrolling</option>
<option value = 1>Scroll one screen</option> <option :vaule = 1>Scroll one screen</option>
<option value = 2>Scroll to the end</option> <option :vaule = 2>Scroll to the end</option>
<option value = 3>Keep scrolling until the page data does not change</option> <option :vaule = 3>Keep scrolling until the page data does not change</option>
</select> </select>
<label>Scroll Times:</label> <label>Scroll Times:</label>
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input> <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
@ -604,15 +604,15 @@ If the expression returns a value greater than 0 or evaluates to True, the loop
<p><input onkeydown="inputDelete(event)" type="checkbox" v-model='nowNode["parameters"]["iframe"]'></input>Operation is in iframe</p> <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='nowNode["parameters"]["iframe"]'></input>Operation is in iframe</p>
<label>Condition Type:</label> <label>Condition Type:</label>
<select v-model='TClass' class="form-control"> <select v-model='TClass' class="form-control">
<option value = 0>No Condition</option> <option :value = 0>No Condition</option>
<option value = 1>Text inside current page</option> <option :value = 1>Text inside current page</option>
<option value = 2>Element inside current page</option> <option :value = 2>Element inside current page</option>
<option v-if="nowNode['isInLoop']" value = 3>Text inside current loop</option> <option v-if="nowNode['isInLoop']" :value = 3>Text inside current loop</option>
<option v-if="nowNode['isInLoop']" value = 4>Element inside current loop</option> <option v-if="nowNode['isInLoop']" :value = 4>Element inside current loop</option>
<option value = 5>Return value of JavaScript command (start with 'return ')</option> <option :value = 5>Return value of JavaScript command (start with 'return ')</option>
<option value = 6>Return value of system command</option> <option :value = 6>Return value of system command</option>
<option v-if="nowNode['isInLoop']" value = 7>Return value of JavaScript command for the current loop item</option> <option v-if="nowNode['isInLoop']" :value = 7>Return value of JavaScript command for the current loop item</option>
<option value = 8>Return value of Python code under current environment</option> <option :value = 8>Return value of Python code under current environment</option>
</select> </select>
<div v-if='TClass > 0 && TClass < 5'> <div v-if='TClass > 0 && TClass < 5'>
@ -653,8 +653,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> <input onkeydown="inputDelete(event)" required type="number" class="form-control" v-model.number='list.nl[index.nowNodeIndex]["parameters"]["wait"]'></input>
<label>Wait Type</label> <label>Wait Type</label>
<select v-model='list.nl[index.nowNodeIndex]["parameters"]["waitType"]' class="form-control"> <select v-model='list.nl[index.nowNodeIndex]["parameters"]["waitType"]' class="form-control">
<option value = 0>Fixed wait (set to wait for 10 seconds then it will wait for 10 seconds)</option> <option :vaule = 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> <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>
</select> </select>
</div> </div>
</div> </div>

View File

@ -113,10 +113,10 @@
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['maxWaitTime']" type="number" required></input> <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['maxWaitTime']" type="number" required></input>
<label>打开网页后是否向下滚动:</label> <label>打开网页后是否向下滚动:</label>
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control"> <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
<option value = 0>不滚动</option> <option :value = 0>不滚动</option>
<option value = 1>向下滚动一屏</option> <option :value = 1>向下滚动一屏</option>
<option value = 2>滚动到底部</option> <option :value = 2>滚动到底部</option>
<option value = 3>一直滚动直到页面内容无变化(需设置好滚动后的等待时间用于检测页面变化)</option> <option :value = 3>一直滚动直到页面内容无变化(需设置好滚动后的等待时间用于检测页面变化)</option>
</select> </select>
<label>滚动次数(滚动类型设置为<b>不滚动</b><b>一直滚动</b>时请忽略此项):</label> <label>滚动次数(滚动类型设置为<b>不滚动</b><b>一直滚动</b>时请忽略此项):</label>
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input> <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
@ -159,10 +159,10 @@
</select> </select>
<label>点击后是否向下滚动页面:</label> <label>点击后是否向下滚动页面:</label>
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control"> <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
<option value = 0>不滚动</option> <option :vaule = 0>不滚动</option>
<option value = 1>向下滚动一屏</option> <option :vaule = 1>向下滚动一屏</option>
<option value = 2>滚动到底部</option> <option :vaule = 2>滚动到底部</option>
<option value = 3>一直滚动直到页面内容无变化(需设置好滚动后的等待时间用于检测页面变化)</option> <option :vaule = 3>一直滚动直到页面内容无变化(需设置好滚动后的等待时间用于检测页面变化)</option>
</select> </select>
<label>滚动次数(滚动类型设置为<b>不滚动</b><b>一直滚动</b>时请忽略此项):</label> <label>滚动次数(滚动类型设置为<b>不滚动</b><b>一直滚动</b>时请忽略此项):</label>
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input> <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
@ -490,10 +490,10 @@ print(emotlib.emoji()) # 使用其中的函数。
<div v-if="!useLoop"> <div v-if="!useLoop">
<p>切换模式</p> <p>切换模式</p>
<select class="form-control" v-model='nowNode["parameters"]["optionMode"]'> <select class="form-control" v-model='nowNode["parameters"]["optionMode"]'>
<option value=0>切换到下一个选项</option> <option :value=0>切换到下一个选项</option>
<option value=1>按索引值切换选项第一个选项索引值为0</option> <option :value=1>按索引值切换选项第一个选项索引值为0</option>
<option value=2>按选项值切换选项</option> <option :value=2>按选项值切换选项</option>
<option value=3>按选项文本切换选项</option> <option :value=3>按选项文本切换选项</option>
</select> </select>
<p>设定值(不适用于切换到下一个选项模式)</p> <p>设定值(不适用于切换到下一个选项模式)</p>
<input class="form-control" id="selectValue" v-model='nowNode["parameters"]["optionValue"]' autoFocus="autofocus" type="text"></input> <input class="form-control" id="selectValue" v-model='nowNode["parameters"]["optionValue"]' autoFocus="autofocus" type="text"></input>
@ -568,9 +568,9 @@ print(emotlib.emoji()) # 使用其中的函数。
<div> <div>
<p><label>(自定义操作)使用代码/脚本定义循环退出条件(也可以在流程中添加<b>自定义操作</b>,然后选择<b>退出循环</b>选项): </label></p> <p><label>(自定义操作)使用代码/脚本定义循环退出条件(也可以在流程中添加<b>自定义操作</b>,然后选择<b>退出循环</b>选项): </label></p>
<select v-model='nowNode["parameters"]["breakMode"]' class="form-control" style="font-weight: bold"> <select v-model='nowNode["parameters"]["breakMode"]' class="form-control" style="font-weight: bold">
<option value = 0>不设置脚本(选择这个下面写了脚本也不会执行)</option> <option :value = 0>不设置脚本(选择这个下面写了脚本也不会执行)</option>
<option value = 1>JavaScript脚本返回值需以return 开头)</option> <option :value = 1>JavaScript脚本返回值需以return 开头)</option>
<option value = 2>操作系统级别命令</option> <option :value = 2>操作系统级别命令</option>
</select> </select>
<div> <div>
<textarea style="margin-top: 10px" onkeydown="inputDelete(event)" class="form-control" rows="2" <textarea style="margin-top: 10px" onkeydown="inputDelete(event)" class="form-control" rows="2"
@ -584,10 +584,10 @@ print(emotlib.emoji()) # 使用其中的函数。
<input onkeydown="inputDelete(event)" required type="number" class="form-control" v-model.number='list.nl[index.nowNodeIndex]["parameters"]["historyWait"]'></input> <input onkeydown="inputDelete(event)" required type="number" class="form-control" v-model.number='list.nl[index.nowNodeIndex]["parameters"]["historyWait"]'></input>
<label>执行完是否向下滚动:</label> <label>执行完是否向下滚动:</label>
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control"> <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
<option value = 0>不滚动</option> <option :value = 0>不滚动</option>
<option value = 1>向下滚动一屏</option> <option :value = 1>向下滚动一屏</option>
<option value = 2>滚动到底部</option> <option :value = 2>滚动到底部</option>
<option value = 3>一直滚动直到页面内容无变化(需设置好滚动后的等待时间用于检测页面变化)</option> <option :value = 3>一直滚动直到页面内容无变化(需设置好滚动后的等待时间用于检测页面变化)</option>
</select> </select>
<label>滚动次数(滚动类型设置为<b>不滚动</b><b>一直滚动</b>时请忽略此项):</label> <label>滚动次数(滚动类型设置为<b>不滚动</b><b>一直滚动</b>时请忽略此项):</label>
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input> <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
@ -596,23 +596,23 @@ print(emotlib.emoji()) # 使用其中的函数。
</div> </div>
<div class="elements" v-if="nodeType==9"> <div class="elements" v-if="nodeType==9">
<label>判断条件是从左往右判断的,即如果最左边的条件分支的判断条件满足,则执行最左边分支内的操作,否则判断从左向右第二个分支的条件是否满足,此类推。</label> <label>判断条件是从左往右判断的,即如果最左边的条件分支的判断条件满足,则执行最左边分支内的操作,否则判断从左向右第二个分支的条件是否满足,此类推。</label>
</div> </div>
<div class="elements" v-if="nodeType==10"> <div class="elements" v-if="nodeType==10">
<label>判断条件是从左往右判断的,即如果最左边的条件分支的判断条件满足,则执行最左边分支内的操作,否则判断从左向右第二个分支的条件是否满足,此类推。</label> <label>判断条件是从左往右判断的,即如果最左边的条件分支的判断条件满足,则执行最左边分支内的操作,否则判断从左向右第二个分支的条件是否满足,此类推。</label>
<p><input onkeydown="inputDelete(event)" type="checkbox" v-model='nowNode["parameters"]["iframe"]'></input>在iframe内操作</p> <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='nowNode["parameters"]["iframe"]'></input>在iframe内操作</p>
<label>条件类型:</label> <label>条件类型:</label>
<select v-model='TClass' class="form-control"> <select v-model='TClass' class="form-control">
<option value = 0>无条件</option> <option :value = 0>无条件</option>
<option value = 1>当前页面包括文本</option> <option :value = 1>当前页面包括文本</option>
<option value = 2>当前页面包括元素</option> <option :value = 2>当前页面包括元素</option>
<option v-if="nowNode['isInLoop']" value = 3>当前循环项包括文本</option> <option v-if="nowNode['isInLoop']" :value = 3>当前循环项包括文本</option>
<option v-if="nowNode['isInLoop']" value = 4>当前循环项包括元素</option> <option v-if="nowNode['isInLoop']" :value = 4>当前循环项包括元素</option>
<option value = 5>JavaScript命令返回值需以return 开头)</option> <option :value = 5>JavaScript命令返回值需以return 开头)</option>
<option value = 6>系统命令返回值</option> <option :value = 6>系统命令返回值</option>
<option v-if="nowNode['isInLoop']" value = 7>针对当前循环项的JavaScript命令返回值需以return 开头)</option> <option v-if="nowNode['isInLoop']" :value = 7>针对当前循环项的JavaScript命令返回值需以return 开头)</option>
<option value = 8>执行环境下的Python表达式值eval操作</option> <option :value = 8>执行环境下的Python表达式值eval操作</option>
</select> </select>
<div v-if='TClass > 0 && TClass < 5'> <div v-if='TClass > 0 && TClass < 5'>
@ -653,8 +653,8 @@ print(emotlib.emoji()) # 使用其中的函数。
<input onkeydown="inputDelete(event)" required type="number" class="form-control" v-model.number='list.nl[index.nowNodeIndex]["parameters"]["wait"]'></input> <input onkeydown="inputDelete(event)" required type="number" class="form-control" v-model.number='list.nl[index.nowNodeIndex]["parameters"]["wait"]'></input>
<label style="margin-top:5px">等待类型</label> <label style="margin-top:5px">等待类型</label>
<select v-model='list.nl[index.nowNodeIndex]["parameters"]["waitType"]' class="form-control"> <select v-model='list.nl[index.nowNodeIndex]["parameters"]["waitType"]' class="form-control">
<option value = 0>固定等待设置等10秒就等10秒</option> <option :value = 0>固定等待设置等10秒就等10秒</option>
<option value = 1>随机等待设置等10秒会随机等10×0.5 - 10 × 1.5 秒)</option> <option :value = 1>随机等待设置等10秒会随机等10×0.5 - 10 × 1.5 秒)</option>
</select> </select>
</div> </div>
</div> </div>

View File

@ -0,0 +1 @@
{"id":255,"name":"京东全球版-专业的综合网上购物商城","url":"https://www.jd.com","links":"https://www.jd.com","create_time":"12/12/2023, 12:05:21 AM","update_time":"12/12/2023, 12:05:21 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"}],"outputParameters":[{"id":0,"name":"参数1_图片地址","desc":"","type":"text","recordASField":1,"exampleValue":"//storage.360buyimg.com/i.imageUpload/31383731303839333234345f7031363633333436303135373830_mid.jpg"}],"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":0,"option":3,"title":"提取数据","sequence":[],"isInLoop":false,"position":1,"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":4,"contentType":0,"relative":false,"name":"参数1_图片地址","desc":"","extractType":0,"relativeXPath":"/html/body/div[5]/div[1]/div[1]/div[1]/div[3]/div[1]/div[1]/div[1]/a[1]/img[1]","allXPaths":["/html/body/div[5]/div[1]/div[1]/div[1]/div[3]/div[1]/div[1]/div[1]/a[1]/img[1]","//img[contains(., '')]","/html/body/div[last()-5]/div/div[last()-4]/div/div/div/div[last()-6]/div[last()-1]/a/img"],"exampleValues":[{"num":0,"value":"//storage.360buyimg.com/i.imageUpload/31383731303839333234345f7031363633333436303135373830_mid.jpg"}],"unique_index":"dxuiztdeptblq13uco8","iframe":false,"default":"","paraType":"text","recordASField":1,"beforeJS":"","beforeJSWaitTime":0,"JS":"","JSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"downloadPic":1}]}}]}

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,8 @@
"justMyCode": false, "justMyCode": false,
// "args": ["--ids", "[7]", "--read_type", "remote", "--headless", "0"] // "args": ["--ids", "[7]", "--read_type", "remote", "--headless", "0"]
// "args": ["--ids", "[9]", "--read_type", "remote", "--headless", "0", "--saved_file_name", "YOUTUBE"] // "args": ["--ids", "[9]", "--read_type", "remote", "--headless", "0", "--saved_file_name", "YOUTUBE"]
"args": ["--ids", "[0]", "--headless", "0", "--user_data", "0", "--keyboard", "0"] "args": ["--ids", "[20]", "--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" // "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

@ -855,6 +855,7 @@ class BrowserThread(Thread):
self.recordLog(e) self.recordLog(e)
self.recordLog("Wait element not found") self.recordLog("Wait element not found")
self.recordLog("执行节点|Execute node:", node["title"]) self.recordLog("执行节点|Execute node:", node["title"])
try:
# 根据不同选项执行不同操作 # 根据不同选项执行不同操作
if node["option"] == 0 or node["option"] == 10: # root操作,条件分支操作 if node["option"] == 0 or node["option"] == 10: # root操作,条件分支操作
for i in node["sequence"]: # 从根节点开始向下读取 for i in node["sequence"]: # 从根节点开始向下读取
@ -889,6 +890,10 @@ class BrowserThread(Thread):
self.loopExecute(node, loopValue, loopPath, index) # 执行循环 self.loopExecute(node, loopValue, loopPath, index) # 执行循环
elif node["option"] == 9: # 条件分支 elif node["option"] == 9: # 条件分支
self.judgeExecute(node, loopValue, loopPath, index) self.judgeExecute(node, loopValue, loopPath, index)
except Exception as e:
self.print_and_log("执行节点<" + node["title"] + ">时出错,将继续执行,错误为:", e)
self.print_and_log("Error executing node <" + node["title"] + ">, will continue to execute, error is:", e)
# 执行完之后进行等待 # 执行完之后进行等待
if node["option"] != 0 and node["option"] != 2: # 点击元素操作单独定义等待时间操作 if node["option"] != 0 and node["option"] != 2: # 点击元素操作单独定义等待时间操作
@ -1637,7 +1642,7 @@ class BrowserThread(Thread):
downloadPic = 0 downloadPic = 0
if downloadPic == 1: if downloadPic == 1:
download_image(self, content, "Data/Task_" + download_image(self, content, "Data/Task_" +
str(self.id) + "/" + self.saveName + "/") str(self.id) + "/" + self.saveName + "/", element)
else: # 普通节点 else: # 普通节点
content = element.text content = element.text
elif p["contentType"] == 1: # 只采集当期元素下的文本,不包括子元素 elif p["contentType"] == 1: # 只采集当期元素下的文本,不包括子元素
@ -1662,7 +1667,7 @@ class BrowserThread(Thread):
downloadPic = 0 downloadPic = 0
if downloadPic == 1: if downloadPic == 1:
download_image(self, content, "Data/Task_" + download_image(self, content, "Data/Task_" +
str(self.id) + "/" + self.saveName + "/") str(self.id) + "/" + self.saveName + "/", element)
else: else:
command = 'var arr = [];\ command = 'var arr = [];\
var content = arguments[0];\ var content = arguments[0];\

View File

@ -16,6 +16,8 @@ from lxml import etree
import smtplib import smtplib
from email.mime.text import MIMEText from email.mime.text import MIMEText
from email.header import Header from email.header import Header
import urllib.request
import base64
def send_email(config): def send_email(config):
""" """
@ -151,18 +153,26 @@ def detect_optimizable(para, ignoreWaitElement=True, waitElement=""):
def download_image(browser, url, save_directory): def download_image(browser, url, save_directory, element=None):
# 定义浏览器头信息 # 定义浏览器头信息
headers = { headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
} }
if is_valid_url(url): if url.startswith("data:image"):
base64_data = url.split(",")[1]
image_data = base64.b64decode(base64_data)
# 提取文件名
file_name = str(uuid.uuid4()) + '.png'
# 构建保存路径
save_path = os.path.join(save_directory, file_name)
# 保存图片到本地
with open(save_path, 'wb') as file:
file.write(image_data)
browser.print_and_log("图片已成功下载到:", save_path)
browser.print_and_log(
"The image has been successfully downloaded to:", save_path)
elif is_valid_url(url):
try: try:
# 发送 GET 请求获取图片数据
response = requests.get(url, headers=headers)
# 检查响应状态码是否为成功状态
if response.status_code == requests.codes.ok:
# 提取文件名 # 提取文件名
file_name = url.split('/')[-1].split("?")[0] file_name = url.split('/')[-1].split("?")[0]
@ -172,18 +182,44 @@ def download_image(browser, url, save_directory):
# 构建保存路径 # 构建保存路径
save_path = os.path.join(save_directory, new_file_name) save_path = os.path.join(save_directory, new_file_name)
# 发送 GET 请求获取图片数据加载浏览器的cookies
s = requests.session()
cookies = browser.browser.get_cookies()
for cookie in cookies:
s.cookies.set(cookie['name'], cookie['value'])
response = s.get(url, headers=headers)
# 检查响应状态码是否为成功状态
if response.status_code == requests.codes.ok:
# 保存图片到本地 # 保存图片到本地
with open(save_path, 'wb') as file: with open(save_path, 'wb') as file:
file.write(response.content) file.write(response.content)
browser.print_and_log("图片已成功下载到:", save_path) browser.print_and_log("图片已成功下载到:", save_path)
browser.print_and_log( browser.print_and_log(
"The image has been successfully downloaded to:", save_path) "The image has been successfully downloaded to:", save_path)
else: else:
browser.print_and_log("下载图片失败,请检查此图片链接是否有效:", url) # browser.print_and_log(f"直接下载图片失败,状态码为:{response.status_code}尝试使用Selenium下载图片...")
# browser.print_and_log(
# f"Failed to download image directly, status code is: {response.status_code}, try to download image using Selenium...")
JS = "var xhr = new XMLHttpRequest(); xhr.open('GET', '" + url +"', true); xhr.responseType = 'blob'; xhr.onload = function() {var reader = new FileReader(); reader.readAsDataURL(xhr.response); reader.onloadend = function() { var base64data = reader.result;}}; xhr.send();"""
base64data = browser.browser.execute_script(JS)
if base64data:
image_data = base64data.b64decode(base64data.split(",")[1])
with open(save_path, 'wb') as file:
file.write(image_data)
browser.print_and_log("图片已成功下载到:", save_path)
browser.print_and_log( browser.print_and_log(
"Failed to download image, please check if this image link is valid:", url) "The image has been successfully downloaded to:", save_path)
else:
browser.print_and_log("下载图片失败,只能使用元素截图功能下载图片。")
browser.print_and_log("Failed to download image, can only download image using element screenshot function.")
# 使用元素截图功能下载图片
try:
element.screenshot(save_path)
browser.print_and_log("图片截图已保存到:", save_path)
browser.print_and_log(
"The image screenshot has been saved to:", save_path)
except Exception as e:
browser.print_and_log("下载图片失败|Error downloading image: ", e)
except Exception as e: except Exception as e:
browser.print_and_log("下载图片失败|Error downloading image: ", e) browser.print_and_log("下载图片失败|Error downloading image: ", e)
else: else:

View File

@ -0,0 +1,11 @@
body {
/*width: 400px; !* 设置popup的宽度 *!*/
}
h1 {
/*color: blue; !* 设置标题颜色 *!*/
}
button {
background-color: green; /* 设置按钮背景颜色 */
}

View File

@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<title>Popup 示例</title>
<link rel="stylesheet" type="text/css" href="popup.css">
</head>
<body>
<!-- <h2>EasySpider Extension</h2>-->
EasySpider Extension, please do not disable me.
</body>
</html>

View File

@ -0,0 +1,3 @@
document.getElementById('clickme').addEventListener('click', () => {
alert('Hello, World!');
});