headless
最近看了些關於谷歌 headless的介紹,簡單的說就是一個無介面的瀏覽器的,可用於前端自動化測試和爬蟲抓取 因為headless是模擬使用者行為操作的,所以爬蟲也是完全模擬使用者的行為,且截圖也非常方便。
puppeteer
是一個基於headless的封裝,提供了很多非常方便實用的api,截個圖如下
詳細文件可以檢視官網練練手
文件也看啦,是該練練手的時候啦,恰好公司叫車報銷,需要到hr系統中,將每晚下班的打卡截圖出來,往常都是手動截圖,效率非常低,且都是重複工作,是時候解放生產力啦~ 先說下大體的流程
- 登陸
- 跳轉到考勤記錄介面
- 輸入查詢日期和結束日期,因為每一張截圖只要當天的記錄,所以只能一天天的查
- 點選查詢按鈕
- 獲取當天最晚打卡記錄的時間,判斷時間是否在報銷時段內,若是,則截圖儲存
程式碼如下,因為是內部系統,關鍵引數已打碼
const puppeteer = require('puppeteer');
var moment = require('moment');
// 引數配置
const config = {
name: '***',
password: '***',
targetTime: '21:00:00',
canbuTime: '19:30:00',
startTime: '2017-12-01',
endTime: '2018-04-01',
searchCanbu: true,
searchDache: true
}
let resultData = {
canbuCounts: 0
}
async function run(params) {
const browser = await puppeteer.launch({ headless: true }); //啟動瀏覽器,headless為false,可以開啟模擬器,預設為true
const page = await browser.newPage();
await page.goto('***'); //跳轉目標地址
const loginDom = {
name: '#username',
password: '#password',
loginBtn: '#btnSubmit'
}
const searchDom = {
createDateStart: '#createDateStart',
createDateEnd: '#createDateEnd',
searchDom: '#btnSubmit'
}
await page.type(loginDom.name, config.name); // 將文字寫入輸入框
await page.type(loginDom.password, config.password);
await page.click(loginDom.loginBtn); //點選按鈕
await page.waitForNavigation(); // 等待頁面跳轉返回
await page.goto('***');
const days = moment.duration(new Date(config.endTime).getTime() - new Date(config.startTime).getTime()).asDays();
let searchTime = config.startTime;
for (let i = 0; i < days; i++) {
// 可以看作在瀏覽器中執行的片段
await page.evaluate(() => {
const startDom = document.querySelector('#createDateStart');
const endDom = document.querySelector('#createDateEnd');
startDom.removeAttribute("readOnly");
endDom.removeAttribute("readOnly");
startDom.value = '';
endDom.value = '';
})
await page.type(searchDom.createDateStart, searchTime);
await page.type(searchDom.createDateEnd, searchTime);
await page.click(searchDom.searchDom);
await page.waitFor(800);
const afterTime = await page.evaluate(() => {
try {
return document.querySelector('#contentTable tbody tr:nth-child(1) td:last-child').innerHTML;
} catch (error) {
return null;
}
})
if (config.searchDache && checkApplyTime(afterTime)) {
//截圖
await page.screenshot({ path: `screenshot/clock-${searchTime}.png`, clip: { x: 0, y: 0, width: 800, height: 217 } });
console.log(`${searchTime}可以報銷的士票哦~`);
}
if (config.searchCanbu && checkCanbuTime(afterTime)) {
console.log(`${searchTime}可以報銷餐補啦~`);
resultData.canbuCounts++;
}
searchTime = moment(searchTime).add(1, 'day').format('YYYY-MM-DD');
}
await browser.close();
};
function checkApplyTime(time) {
if (!time) {
return false;
}
return new Date(time) >= new Date(`${moment(time).format('YYYY-MM-DD')} ${config.targetTime}`)
};
function checkCanbuTime(time) {
if (!time) {
return false;
}
return new Date(time) >= new Date(`${moment(time).format('YYYY-MM-DD')} ${config.canbuTime}`)
}
run()
複製程式碼
其中會涉及到一些dom節點的獲取與修改,比如輸入時間的input,時候通過外掛來選擇時間的,且input是readonly的狀態,所以就需要些小操作,多嘗試幾次還是可以解決的
###待提升 執行效率上,還是比較慢,有空會在優化下~