Selenium 简介
百度百科介绍:
使用流程
- 根据平台下载需要的webdrive
- 下载依赖包
npm install selenium-webdriver
- 玩一下官方demo, 做适当的代码修改并根据代码进行注释
// 1. 引入selenium-webdriver包,解构需要的对象和方法
const {Builder, By, Key, until} = require('selenium-webdriver');
// 2. 将需要的代码包在一个自执行函数中
(async function example() {
// 实例化 driver 对象, chrome 代表使用的浏览器
let driver = await new Builder().forBrowser('chrome').build();
try {
// 需要打开的网站地址
await driver.get('https://www.baidu.com/');
// Key.RETURN enter回车
// By.id('id') 百度查询滴输入内容
// 找到元素 向里面发送一个关键字并按回撤
await driver.findElement(By.id('kw')).sendKeys('酒店', Key.RETURN);
// 等1秒之后,验证是否搜索成功
// await driver.wait(until.titleIs('酒店_百度搜索'), 1000);
} finally {
// 关闭浏览器
// await driver.quit();
}
})();
爬取掘金小册数据
- 实现的功能
- 自动打开掘金页面的首页
- 自动点击小册、前端进行路由切换
- 将前端的全部小册数据爬取
- 注意点
- 由于selenium内部都是基于promise进行的封装,所有的方法调用其实返回的都是一个promise对象,因此会大量的使用async语法
自动打开掘金页面的首页
// 自动打开掘金
await driver.get('https://juejin.im/timeline');
自动点击进行路由跳转
在浏览器中,查看页面的布局结构,找到小册的位置,若是使用jq进行dom选择,则是 $('.main-header-box .nav-item:nth-of-type(4)')
selenium中的By拥有很多的选择,其使用规则和jq非常相似,By.css('.main-header-box .nav-item:nth-of-type(4)')便可以找到对应的元素,调用click事件就可以模拟自动点击
// 点击小册子的navBar 切换路由到小册
await driver
.findElement(By.css('.main-header-box .nav-item:nth-of-type(4)'))
.click();
await driver.sleep(1000)
- 当点击小册之后,会调用 await driver.sleep(1000),因为当页面点击之后,页面会进行重新渲染,此时防止接下来的操作,获取不到数据或者获取的数据不是期望值,增加一个延迟确保数据的准确性
将前端的全部小册数据爬取
- 根据driver.findElements(By.css('.list-wrap .books-list .item'))获取前端小册列表,需要注意的是,当页面点击navBar之后,页面会进行重新渲染,但是此时若是直接去获取小测列表将会存在风险,因为在页面没有渲染完成之前获取不到期望值,而代码也会异常终止程序的运行
- 进行迭代取出希望获取的数据,根据itemInfo.findElement(By.css('.info .title')).getText()
while (true) {
let listViewError = true;
try {
// 获取小册列表
let _li = await driver.findElements(By.css('.list-wrap .books-list .item'));
console.log(_li.length);
for (let i = 0, _len = _li.length; i < _len; i++) {
const itemInfo = _li[i];
const title = await itemInfo.findElement(By.css('.info .title')).getText()
const desc = await itemInfo.findElement(By.css('.info .desc')).getText()
let price = await itemInfo.findElement(By.css('.info .price-text')).getText()
_result.push({
title,
desc,
price
})
}
console.log('_result',_result);
} catch (error) {
if (error) listViewError = false;
} finally {
if (listViewError) break;
}
}
总结
全部代码
/*
* @Author: nordon-wang
* @Date: 2019-08-13 11:05:36
* @Description: 爬取掘金数据
*/
const { Builder, By, Key, until } = require('selenium-webdriver');
let _result = []; // 用来收集获取的数据
(async function start() {
let driver = await new Builder().forBrowser('chrome').build();
try {
// 自动打开掘金
await driver.get('https://juejin.im/timeline');
// 点击小册子的navBar 切换路由到小册
await driver
.findElement(By.css('.main-header-box .nav-item:nth-of-type(4)'))
.click();
await driver.sleep(1000)
// 点击二级菜单
await clickViewNav(driver);
await driver.sleep(1000)
// 获取数据
await getList(driver);
} catch (error) {
console.log(error);
} finally {
let timer = setTimeout(async () => {
clearTimeout(timer);
await driver.quit();
}, 600000);
}
})();
// 获取渲染完成的按钮
async function clickViewNav(driver) {
while (true) {
let viewNavError = true;
try {
await driver
.findElement(By.css('.main-container .view-nav .nav-item:nth-of-type(2)'))
.click();
} catch (error) {
if (error) viewNavError = false;
} finally {
if (viewNavError) break;
}
}
}
// 获取列表数据
// 页面在渲染完成之前无法获取到页面的元素
async function getList(driver) {
while (true) {
let listViewError = true;
try {
// 获取小册列表
let _li = await driver.findElements(By.css('.list-wrap .books-list .item'));
console.log(_li.length);
for (let i = 0, _len = _li.length; i < _len; i++) {
const itemInfo = _li[i];
const title = await itemInfo.findElement(By.css('.info .title')).getText()
const desc = await itemInfo.findElement(By.css('.info .desc')).getText()
let price = await itemInfo.findElement(By.css('.info .price-text')).getText()
_result.push({
title,
desc,
price
})
}
console.log('_result',_result);
} catch (error) {
if (error) listViewError = false;
} finally {
if (listViewError) break;
}
}
}