nodejs操作selenium-webdriver
一、安装依赖
npm install selenium-webdriver
二、下载chormdriver 文件,并配置全局环境变量
注意:需下载和电脑安装的谷歌浏览器版本相匹配的
ChromeDriver官网下载地址:https://sites.google.com/chromium.org/driver/downloads
ChromeDriver官网最新版下载地址:https://googlechromelabs.github.io/chrome-for-testing/
ChromeDriver国内镜像下载地址:https://registry.npmmirror.com/binary.html?path=chromedriver/
ChromeDriver国内镜像最新版下载地址:https://registry.npmmirror.com/binary.html?path=chrome-for-testing/
三、具体使用方法总结
- 基本配置---包括禁止浏览器弹出通知
const { Builder, By, Key, until, ActionChains } = require('selenium-webdriver')
const chrome = require('selenium-webdriver/chrome');
let options = new chrome.Options();
// 禁止浏览器弹出通知
options.addArguments('--disable-notifications');
let driver = await new Builder().forBrowser('chrome').setChromeOptions(options).build()
- 打开访问网址
driver.get('https://www.instagram.com?locale=zh_CN')
- 浏览器设置cookie
let cookie = { name:'key', value:'value' } driver.manage().addCookie(cookie)
- 设置休眠时间、最大化浏览器窗口、刷新页面、获取页面源码
// 休眠时间 毫秒
driver.sleep(1000);
// 最大化浏览器窗口
driver.manage().window().maximize();
// 刷新页面
driver.navigate().refresh()
// 获取页面源码
driver.getPageSource()
- 浏览器打开多个窗口,切换、关闭浏览器窗口
// 用来处理页面需要打开新页面操作情况,切换操作页面,关闭当前操作页面;
// 获取当前窗口句柄
let originalWindow = await driver.getWindowHandle();
// 获取了所有打开的窗口句柄
let allWindows = await driver.getAllWindowHandles();
for(let windowHandle of allWindows) {
if(windowHandle !== originalWindow) {
// 切换到窗口句柄
await driver.switchTo().window(windowHandle);
}
}
// 关闭单个窗口句柄
await driver.close()
//关闭所有页面
await driver.quit();
- 查找元素的几种方法
1, 等待第一个查找元素类型出现,可以设置等待时间
注意: 在使用过程中需要 try{}catch(e){} 进行包裹,如果没有找到元素会报错,不再执行之后的代码片段
此方法两个参数都为必填,如果第二个等待时间参数未填写,那么页面会一直等待,直到元素出现,这样就会导致页面出现错误时,未
获取到元素,也无法抓取异常信息
await driver.wait(until.elementLocated(By.xpath("//a[contains(@href,'/followers/')]")), 10000)
拓展:By.xpath的基本语法总结
1、By.xpath('//a[contains(@href,"/followers/")]') // 查找a标签属性值href中包含'/followers/'的元素
2、By.xpath('//a[@href,"https://www.baidu.com"]') // 查找a标签属性值href为"https://www.baidi.com"
3、By.xpath('//span[contains(text(),"更多")]') // 查找文本内容包含"更多"的span标签
4、By.xpath('//a[text()="更多内容"]') // 查找文本内容等于'更多内容'
5、By.xpath('//a[@href="***" and text()="***"]') // 多条件查询
6、By.xpath('..') // 父节点
7、By.xpath('./following-sibling::*[1]') // 获取下一个兄弟节点
By.xpath('./preceding-sibling::*[1]') // 获取上一个兄弟节点
8、By.xpath('child::div') // 查找当前元素的所有直接子 div 元素
By.xpath("//input[@type='file'][@data-testid='fileInput']")
By.css的基本语法总结
1、By.css('#username') // 通过id获取元素
2、By.css('.password') // 通过类名选择元素
3、By.css('button') // 通过标签名选择元素
4、By.css('a[href="/about"]') // 通过属性选择元素
5、By.css('.acan.acao') // 通过多个类名选择器选择元素
6、By.css('form input[type="text"]') // 通过组合选择器选择元素
By.css('ul > div:nth-child(3) > div:first-child > div:first-child')
By.css("div[aria-label='发帖'][role='button']")
2,查找元素
2.1 查找单个元素
注意:使用过程中需要 try{}catch(e){} 进行包裹,如果没有找到元素会报错,不再执行之后的代码片段
let divDom = await driver.findElement(By.xpath('//div[@data-id, "div"]'))
// 查找divDom 元素下的特定元素
let childrenDivDom = await divDom.findElement(By.xpath('.//span[contains(text(),"更多")]'))
let
2.2 查找多个元素
注意:返回的内容是一个数组,如果没有找到元素不会报错,返回一个空数组,通过数组的长短来定位自己要找的元素位置
await driver.findElements(By.css("button._acan._acao._acas._aj1-._ap30"))
- 元素点击
let settingNode = await driver.findElement(By.css('.settings-button'));
// 两种点击方法
// 第一种:适用于大多数常规情况,会触发所有浏览器的默认情况
// 优点:自然触发事件:会触发所有浏览器的默认事件,如 mousedown、mouseup、click 等。
// 简单易用:代码更简洁,更容易理解和调试。
// 缺点:限制性:如果元素被其他元素遮挡或不可见,可能会抛出 ElementNotInteractableError 异常。
// 依赖于元素状态:元素必须是可见且可交互的。
await settingNode.click()
// 第二种
// 优点: 兼容性:有些情况下,直接调用 settingNode.click(); 可能会失败,特别是当元素被其他元素遮挡或不可见时。使用 executeScript 可以绕过这些限制,强制执行点击操作。
// 灵活性:可以执行更复杂的 JavaScript 操作,而不仅仅是点击。
// 缺点: 不触发所有事件:通过 JavaScript 执行的点击操作可能不会触发所有浏览器的默认事件(如 mousedown、mouseup 等)。
// 调试困难:如果点击操作出现问题,调试起来可能会更复杂,因为它是通过 JavaScript 执行的。
await driver.executeScript("arguments[0].click();", settingNode);
- 输入框、上传文件框输入内容
let destinationPath = "添加内容";
await uploadInput.sendKeys(destinationPath);
// 输入内容并且按下enter按钮
await uploadInput.sendKeys(code, Key.RETURN);
- 获取文本
let fansEle = await driver.findElement(By.xpath("//div[@data-id='following']"));
await fansEle.getText()
- 页面浏览器滚动或者某个区域滚动
// 主要用途:在获取页面信息时, 遇到含有滚动条的页面,需要滚动动态加载网页数据,抓取全部数据内容
// 控制网页滚动到底部
await driver.executeScript('window.scrollTo(0, document.body.scrollHeight)')
// 等待滚动完成
await driver.wait(until.elementLocated(By.css('body')), 1000);
// 获取 scrollTop 的值
let scrollTop = await driver.executeScript('return window.pageYOffset;');
// 针对某个元素内部的滚动
let ulElement = await commonBox.findElement(By.css('ul'));
// 控制ulElement 区域
await driver.executeScript(`arguments[0].scrollTop = arguments[0].scrollHeight;`,ulElement);
// 获取页面当前滚动的距离
let scrollTop = await driver.executeScript(`return arguments[0].scrollTop`, ulElement);
- 模拟鼠标悬浮
// 主要用途:网页中有些元素是隐藏的,比如时间、消息发送人等,需要鼠标悬浮到指定元素之后,才会显示内容
// 创建actions实例
let actions = driver.actions({ async: false });
// 移动到元素
await actions.move({ origin: timeEle, x: 1, y: 1}).pause(1000).perform();
注意: 移动到元素获取到tooltip元素内容之后需要将鼠标移出元素,不然有可能会导致下次鼠标移动到其他元素时
出现两个tooltip元素,导致获取数据错误
- 日期时间选择框操作
// 主要分为两种情况
// 第一种情况:
// 日期年月日input框是分开的 分别获取年月日的input输入框,填入相对应的数据即可
var monthSelectEle = await driver.wait(until.elementLocated(By.css('select[title="月:"]')), 10000);
await driver.sleep(1000);
await monthSelectEle.click();
await monthSelectEle.sendKeys(month);
await driver.sleep(1000);
// 获取日期选择框
var daySelectEle = await driver.wait(until.elementLocated(By.css('select[title="日:"]')), 10000);
await daySelectEle.click();
await driver.sleep(1000);
await daySelectEle.sendKeys(day);
await driver.sleep(1000);
// 获取年份选择框
var yearSelectEle = await driver.wait(until.elementLocated(By.css('select[title="年:"]')), 10000);
await driver.sleep(1000);
await yearSelectEle.sendKeys(year);
await driver.sleep(1000);
// 第二种情况:
// 日期年月日input框是一个大的组件,无法分别获取input输入框,获取元素后需要使用sendKeys(Key.TAB) 移动到下一个虚拟输入位
var timeEle = await driver.wait(until.elementLocated(By.id('birthdate')), 10000);
await driver.sleep(5000);
await timeEle.sendKeys(year);
await driver.sleep(1000)
await timeEle.sendKeys(Key.TAB); // 移动到下一个虚拟输入位
await driver.sleep(1000);
await timeEle.sendKeys(month);
await driver.sleep(1000);
await driver.sleep(1000);
await timeEle.sendKeys(day);
await driver.sleep(1000);
- 备注-下载图片数据流,通过formData,上传到服务器
// 引用form-data 第三方插件
const FormData = require('form-data');
exports.downLoadMedia = async (mediaUrl, proxyObj) => {
return new Promise((resolve, reject) => {
axios({
method: 'GET',
url: mediaUrl,
responseType: 'stream',
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
proxy: {
host:'127.0.0.1',
port: 7890,
protocol: "http",
}
}).then(res => {
resolve(res.data)
}).catch(e => {
reject(e)
})
})
}
/**
* 下载资源并上传
*
* @param {*} imgUrl
* @param {*} targetData
* @param {*} data
*/
async function downloadSource(imgUrl, targetData, data) {
let str = "";
try {
await utils.downLoadMedia(imgUrl).then(async res => {
let imgType = "";
let suffix = "";
if(imgUrl.indexOf('jpg') > -1 || imgUrl.indexOf('jpeg') > -1) {
imgType = "image/jpeg";
suffix = 'jpg';
}else if(imgUrl.indexOf('png') > -1) {
imgType = "image/png";
suffix = 'png';
}else if(imgUrl.indexOf('gif') > -1) {
imgType = "image/gif"
suffix = 'gif';
}else if(imgUrl.indexOf('bmp') > -1) {
imgType = "image/bmp";
suffix = 'bmp'
}else{
imgType = "image/jpeg";
suffix= 'jpg';
}
const form = new FormData();
let timeStr = new Date().getTime();
// // 将 Buffer 转换为 ArrayBuffer
// const arrayBuffer = imageBytes.buffer;
// 从 ArrayBuffer 创建 Blob
// const blob = new Blob([arrayBuffer], { type: imgType });
form.append('file', res, {
filename: `${timeStr}.${suffix}`,
contentType: 'application/octet-stream',
});
await utils.uploadRes(form, targetData, data.serverUrlPrefix).then(resUrl => {
str = resUrl
})
})
} catch (e) {
console.log('e',e);
str = ""
}
return str;
}
- 下载图片到本地
// 引用form-data 第三方插件
const FormData = require('form-data');
exports.downLoadMedia = async (mediaUrl, proxyObj) => {
return new Promise((resolve, reject) => {
axios({
method: 'GET',
url: mediaUrl,
responseType: 'stream',
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
proxy: {
host:'127.0.0.1',
port: 7890,
protocol: "http",
}
}).then(res => {
resolve(res.data)
}).catch(e => {
reject(e)
})
})
}
/**
* 下载资源并上传
*
* @param {*} imgUrl
* @param {*} targetData
* @param {*} data
*/
async function downloadSource(imgUrl, targetData, data) {
let str = "";
try {
await utils.downLoadMedia(imgUrl).then(async res => {
let imgType = "";
let suffix = "";
if(imgUrl.indexOf('jpg') > -1 || imgUrl.indexOf('jpeg') > -1) {
imgType = "image/jpeg";
suffix = 'jpg';
}else if(imgUrl.indexOf('png') > -1) {
imgType = "image/png";
suffix = 'png';
}else if(imgUrl.indexOf('gif') > -1) {
imgType = "image/gif"
suffix = 'gif';
}else if(imgUrl.indexOf('bmp') > -1) {
imgType = "image/bmp";
suffix = 'bmp'
}else{
imgType = "image/jpeg";
suffix= 'jpg';
}
const form = new FormData();
let timeStr = new Date().getTime();
// // 将 Buffer 转换为 ArrayBuffer
// const arrayBuffer = imageBytes.buffer;
// 从 ArrayBuffer 创建 Blob
// const blob = new Blob([arrayBuffer], { type: imgType });
form.append('file', res, {
filename: `${timeStr}.${suffix}`,
contentType: 'application/octet-stream',
});
await utils.uploadRes(form, targetData, data.serverUrlPrefix).then(resUrl => {
str = resUrl
})
})
} catch (e) {
console.log('e',e);
str = ""
}
return str;
}