Compare commits

...

10 Commits

Author SHA1 Message Date
LingandRX
e521f3b028 test 2024-12-19 00:05:29 +08:00
LingandRX
1d4797ece3 feat: 添加 HLK 平台数据拉取脚本
- 新增 HLK 平台登录和数据拉取脚本
- 使用 Puppeteer 实现自动化登录和数据抓取
- 添加打印 IndexedDB 数据的功能
2024-12-18 09:11:31 +08:00
wenpeng
cc1251eda9 add on response 2024-08-07 16:28:49 +08:00
wenpeng
427cc0d929 fix 2024-08-05 18:18:38 +08:00
wenpeng
1f315fe199 fix 2024-08-02 14:31:49 +08:00
wenpeng
b3f96ddddd fix 2024-08-01 17:30:36 +08:00
wenpeng
9a9b4646ca fix 2024-08-01 16:07:17 +08:00
wenpeng
468a252f21 add timedimension 2024-07-30 18:09:24 +08:00
wenpeng
17d6dd7cf6 fix 2024-07-26 18:48:55 +08:00
241c39523b fix 2024-07-25 23:16:20 +08:00
13 changed files with 4227 additions and 65 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
node_modules/
*.png
businessData.json
data/user.json
data/technicianReport.json

View File

@ -1,24 +1,36 @@
import { launch } from 'puppeteer';
import fs from 'fs';
import 'dotenv/config.js'
import { setLocalStorage } from './utils/userUtils.js';
import { getDate } from './utils/businessDataUtil.js';
import { setMonthTime } from './utils/commnUtils.js'
(async () => {
const browser = await launch({ headless: true, args: [`--window-size=1920,1080`], defaultViewport: { width: 1920, height: 1080 } });
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
page.on('response', async(response) => {
console.log(response.url());
});
setLocalStorage(page, '.\\data\\user.json');
await page.goto('https://dev-dmp.meiguanjia.net/report/businessData');
// await page.goto('https://boss.aizhb.net/report/businessData');
console.log(current);
console.log(process.env.ACCOUNT);
await page.waitForSelector('.menu-list-second');
const childElements = await page.$$('.menu-list-second .menu-list-second-item');
await new Promise(r => setTimeout(r, 1000));
await childElements[1].click();
const str = await page.evaluate(node => node.textContent.trim(), childElements[1]);
console.log(str);
const item = await page.$eval('.item.cur', node=>node.innerHTML);
console.log(item);
// const arcoMessage = await page.$eval('.arco-message .arco-message-content', node => node.innerHTML);
// await page.screenshot({ path: 'resetPassword.png' });
console.log(arcoMessage);
const file = './data/businessData.json';
page.on('console', msg => {
console.log('PAGE:', msg.text())
})
fs.writeFileSync(file, '', 'utf-8');
await setMonthTime(page, '.arco-picker-start-time', '2024-05');
await getDate(page, fs, file);
console.log('========');
await browser.close();
})();

View File

@ -0,0 +1,30 @@
import { launch } from 'puppeteer';
import fs from 'fs';
import 'dotenv/config.js'
import { setLocalStorage } from './utils/userUtils.js';
import { setMonthTime, getDate, changeTimeDimension } from './utils/commnUtils.js'
(async () => {
const browser = await launch({ headless: false, args: [`--window-size=1920,1080`], defaultViewport: { width: 1920, height: 1080 } });
const page = await browser.newPage();
setLocalStorage(page, '.\\data\\user.json');
await page.goto('https://dev-dmp.meiguanjia.net/report/technicianReport');
// await page.goto('https://boss.aizhb.net/report/businessData');
await page.waitForSelector('.menu-list-second');
const childElements = await page.$$('.menu-list-second .menu-list-second-item');
await new Promise(r => setTimeout(r, 1000));
await childElements[2].click();
const str = await page.evaluate(node => node.textContent.trim(), childElements[2]);
console.log(str);
const file = './data/technicianReport.json';
fs.writeFileSync(file, '', 'utf-8');
await changeTimeDimension(page, 2);
await setMonthTime(page, '.arco-picker-start-time', '2024-05');
await getDate(page, fs, file);
await browser.close();
})();

3670
allData.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +0,0 @@
export async function getDate(page, fs) {
await new Promise(r => setTimeout(r, 2000));
await page.waitForSelector('.ai_custome');
const aiCustome = await page.$$('.ai_custome .ai_custome_item .amount');
let ai = new Set();
for (const e of aiCustome) {
let str = await page.evaluate(element => element.textContent, e);
ai.add(str);
}
let importStorage = '\n' + JSON.stringify(Array.from(ai)) + '\n';
const file = 'businessData.json';//获取file
fs.appendFileSync(file, importStorage, 'utf-8');
const elementHandle = await page.waitForSelector('.bi_warp iframe');
const frame = await elementHandle.contentFrame();
await frame.waitForSelector('#label-content');
const items = await frame.$$('#label-content'); // Use $$ to get all matching elements
let its = new Set();
if (items.length > 0) { // Check if items array is not empty
for (const e of items) {
let str = await frame.evaluate(element => element.textContent, e); // Use frame.evaluate() here
its.add(str.replace(/\s+/g, ' '));
}
} else {
console.log('#label-content not found in the iframe');
}
importStorage = '\n' + JSON.stringify(Array.from(its)) + '\n';
fs.appendFileSync(file, importStorage, 'utf-8');
// console.log(its);
}

View File

@ -2,13 +2,15 @@ import { launch } from 'puppeteer';
import fs from 'fs';
import 'dotenv/config.js'
import { setLocalStorage } from './utils/userUtils.js';
import { getDate } from './businessDataUtil.js';
import { getDate } from './utils/businessDataUtil.js';
import { setMonthTime } from './utils/commnUtils.js'
(async () => {
const browser = await launch({ headless: true, args: [`--window-size=1920,1080`], defaultViewport: { width: 1920, height: 1080 } });
const page = await browser.newPage();
setLocalStorage(page, 'user.json');
setLocalStorage(page, '.\\data\\user.json');
await page.goto('https://dev-dmp.meiguanjia.net/report/businessData');
// await page.goto('https://boss.aizhb.net/report/businessData');
await page.waitForSelector('.menu-list-second');
const childElements = await page.$$('.menu-list-second .menu-list-second-item');
@ -17,25 +19,12 @@ import { getDate } from './businessDataUtil.js';
const str = await page.evaluate(node => node.textContent.trim(), childElements[1]);
console.log(str);
const file = 'businessData.json';//获取file
const file = './data/businessData.json';
fs.writeFileSync(file, '', 'utf-8');
await getDate(page, fs);
console.log('========');
const time = await page.$('.arco-picker-start-time');
await time.click({ clickCount: 3 });
await time.type('2024-06', { delay: 100 });
await getDate(page, fs);
console.log('========');
await time.click({ clickCount: 3 });
await time.type('2024-05', { delay: 100 });
await getDate(page, fs);
console.log('========');
await time.click({ clickCount: 3 });
await time.type('2024-04', { delay: 100 });
await getDate(page, fs);
await setMonthTime(page, '.arco-picker-start-time', '2024-05');
await getDate(page, fs, file);
console.log('========');
await browser.close();

135
hlk.spec.js Normal file
View File

@ -0,0 +1,135 @@
import { launch, Locator } from 'puppeteer'; // v23.0.0 or later
import fs from 'fs';
(async () => {
const browser = await launch({
headless: false,
});
const page = await browser.newPage();
const timeout = 5000;
page.setDefaultTimeout(timeout);
{
const targetPage = page;
await targetPage.goto('https://hlk.meiguanjia.net/#/login');
}
{
const targetPage = page;
await Locator.race([targetPage.locator('input[placeholder="请输入您的手机号码')])
.setTimeout(timeout)
.click();
}
{
const targetPage = page;
await Locator.race([targetPage.locator('input[placeholder="请输入您的手机号码')])
.setTimeout(timeout)
.fill('17770720274');
}
{
const targetPage = page;
await Locator.race([targetPage.locator('input[placeholder="请输入登录密码')])
.setTimeout(timeout)
.click();
}
{
const targetPage = page;
await Locator.race([targetPage.locator('input[placeholder="请输入登录密码')])
.setTimeout(timeout)
.fill('a123456');
}
{
const targetPage = page;
await Locator.race([targetPage.locator('#agreement')])
.setTimeout(timeout)
.click();
}
{
const targetPage = page;
await Locator.race([targetPage.locator('.login-form-button')])
.setTimeout(timeout)
.click();
await new Promise((resolve) => setTimeout(resolve, 3000));
}
{
const targetPage = page;
targetPage.on('console', (msg) => {
for (let i = 0; i < msg.args().length; ++i) {
console.log(`${i}: ${msg.args()[i]}`);
}
});
const allData = await targetPage.evaluate(() => {
return new Promise((resolve, reject) => {
// 获取所有数据库列表
indexedDB
.databases()
.then((databases) => {
const allDatabasesData = [];
// 遍历所有数据库
const dbPromises = databases.map((db) => {
return new Promise((dbResolve, dbReject) => {
const request = indexedDB.open(db.name);
request.onsuccess = () => {
const database = request.result;
const transaction = database.transaction(database.objectStoreNames, 'readonly');
const dbData = { name: db.name, stores: {} };
// 遍历数据库中的每个对象存储
for (let storeName of database.objectStoreNames) {
const storeData = [];
const store = transaction.objectStore(storeName);
const cursorRequest = store.openCursor();
cursorRequest.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
storeData.push(cursor.value);
cursor.continue();
} else {
// 当遍历完所有数据时,将数据保存到 dbData 中
dbData.stores[storeName] = storeData;
if (
Object.keys(dbData.stores).length === database.objectStoreNames.length
) {
// 完成一个数据库的所有对象存储数据
dbResolve(dbData);
}
}
};
cursorRequest.onerror = (error) => {
dbReject(error);
};
}
};
request.onerror = (error) => {
dbReject(error);
};
});
});
// 等待所有数据库的数据获取完成
Promise.all(dbPromises)
.then((results) => {
resolve(results);
})
.catch(reject);
})
.catch(reject);
});
});
fs.writeFileSync('allData.json', JSON.stringify(allData, null, 2));
// console.log(allData[0].stores.keyvaluepairs); // 打印所有数据库的数据
}
// await browser.close();
})().catch((err) => {
console.error(err);
process.exit(1);
});

View File

@ -6,6 +6,7 @@ import 'dotenv/config.js';
const browser = await launch({ headless: true, args: [`--window-size=1920,1080`], defaultViewport: { width: 1920, height: 1080 } });
const page = await browser.newPage();
await page.goto('https://dev-dmp.meiguanjia.net/login');
// await page.goto('https://boss.aizhb.net/login');
await page.locator('#userName input').fill(process.env.ACCOUNT);
await page.locator('#passWord input').fill(process.env.PASSWORD);
await page.locator('[type="submit"]').click();
@ -18,7 +19,7 @@ import 'dotenv/config.js';
const localStorage = await page.evaluate(() => Object.assign({}, window.localStorage));
const importStorage = JSON.stringify(localStorage);
const file = 'user.json';//获取file
const file = './data/user.json';
fs.writeFileSync(file, importStorage, 'utf-8');
await browser.close();
})();

120
sethlk.spec.js Normal file
View File

@ -0,0 +1,120 @@
import { launch } from 'puppeteer';
import { readFileSync } from 'fs';
(async () => {
// 读取 JSON 文件
const jsonData = JSON.parse(readFileSync('allData.json', 'utf8'));
const browser = await launch({ headless: false });
const page = await browser.newPage();
// 打开目标网页
await page.goto('https://hlk.meiguanjia.net'); // 替换为你的目标 URL
// console.log(JSON.stringify(jsonData, null, 2));
page.on('console', (msg) => {
for (let i = 0; i < msg.args().length; ++i) {
console.log(`${i}: ${msg.args()[i]}`);
}
});
// 在页面上下文中插入数据
await page.evaluate(async (jsonData) => {
console.log('传递到浏览器端的 JSON 数据:', jsonData); // 打印传递的数据,调试用
// 遍历 jsonData 数组
for (const dbData of jsonData) {
const dbName = dbData.name; // 获取数据库名称
const stores = dbData.stores; // 获取数据库中的对象存储
console.log(`正在创建数据库:${dbName}`); // 确认 dbName 是否正确
if (!dbName) {
console.error('数据库名称不存在!');
return;
}
// 打开数据库(如果数据库不存在,会创建它)
const request = indexedDB.open(dbName, 1);
// 数据库创建或升级时触发
request.onupgradeneeded = function (event) {
const db = event.target.result;
console.log(`数据库 ${dbName} 创建或升级中...`);
// 动态创建对象存储
for (const storeName in stores) {
if (!db.objectStoreNames.contains(storeName)) {
console.log(`创建对象存储:${storeName}`);
db.createObjectStore(storeName, { keyPath: 'id', autoIncrement: true });
}
}
};
// 数据库成功打开时触发
request.onsuccess = function (event) {
const db = event.target.result;
console.log(`数据库 ${dbName} 打开成功`);
// 开始一个事务
const transaction = db.transaction(Object.keys(stores), 'readwrite');
// 对每个对象存储插入数据
for (const storeName in stores) {
const store = transaction.objectStore(storeName);
const data = stores[storeName];
console.log(`插入数据到 ${storeName} 对象存储`);
// 递归处理多层嵌套的对象
insertNestedData(store, data);
}
// 事务完成时触发
transaction.oncomplete = function () {
console.log(`${dbName} 数据写入完成`);
};
// 事务错误时触发
transaction.onerror = function (event) {
console.error(`${dbName} 数据写入失败:`, event.target.error);
};
};
// 打开数据库失败时触发
request.onerror = function (event) {
console.error(`打开数据库 ${dbName} 失败:`, event.target.error);
};
}
// 递归插入数据的函数,处理多层嵌套
function insertNestedData(store, data) {
data.forEach((item) => {
if (typeof item === 'object' && !Array.isArray(item)) {
// 如果是对象且不是数组,递归处理
for (const key in item) {
// 生成一个包含 id 的新对象
const nestedData = item[key];
const id = key || Date.now(); // 使用键作为 ID或者时间戳作为备用
store.put({ id, ...nestedData }); // 插入数据
console.log(`插入数据:${id}`, nestedData);
// 如果有更深层的嵌套,递归调用
if (typeof nestedData === 'object' && !Array.isArray(nestedData)) {
insertNestedData(store, [nestedData]); // 深层嵌套递归
}
}
} else {
// 如果是普通数据(如数组项)
store.put({ id: item });
console.log(`插入数据:`, item);
}
});
}
}, jsonData); // 将 jsonData 传递到浏览器上下文中
// 等待一段时间,确保数据写入完成
await new Promise((resolve) => setTimeout(resolve, 5000));
// await browser.close();
})();

103
testhlk.spec.js Normal file
View File

@ -0,0 +1,103 @@
import puppeteer, { launch, Locator } from 'puppeteer'; // v23.0.0 or later
import fs from 'fs';
import { resolve } from 'path';
(async () => {
const browser = await launch({
headless: false,
});
const page = await browser.newPage();
const timeout = 5000;
page.setDefaultTimeout(timeout);
{
const targetPage = page;
await targetPage.goto('https://hlk.meiguanjia.net/#/login');
}
{
const targetPage = page;
await Locator.race([targetPage.locator('input[placeholder="请输入您的手机号码')])
.setTimeout(timeout)
.click();
}
{
const targetPage = page;
await Locator.race([targetPage.locator('input[placeholder="请输入您的手机号码')])
.setTimeout(timeout)
.fill('17770720274');
}
{
const targetPage = page;
await Locator.race([targetPage.locator('input[placeholder="请输入登录密码')])
.setTimeout(timeout)
.click();
}
{
const targetPage = page;
await Locator.race([targetPage.locator('input[placeholder="请输入登录密码')])
.setTimeout(timeout)
.fill('a123456');
}
{
const targetPage = page;
await Locator.race([targetPage.locator('#agreement')])
.setTimeout(timeout)
.click();
}
{
const targetPage = page;
await Locator.race([targetPage.locator('.login-form-button')])
.setTimeout(timeout)
.click();
await new Promise((resolve) => setTimeout(resolve, 3000));
}
{
const targetPage = page;
targetPage.on('console', (msg) => {
for (let i = 0; i < msg.args().length; ++i) {
console.log(`${msg.args()[i]}`);
}
});
// 获取浏览器中的 IndexedDB 数据
const result = await page.evaluate(async () => {
return new Promise((resolve, reject) => {
const request = indexedDB.open('hlk_touch_test_14920');
request.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction(db.objectStoreNames, 'readonly');
const objectStore = transaction.objectStore(db.objectStoreNames[0]);
// resolve('success');
const cursorRequest = objectStore.openCursor();
cursorRequest.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log(cursor.key);
const value = cursor.value;
console.log(JSON.stringify(value));
cursor.continue();
} else {
resolve('success');
}
};
};
request.onerror = (event) => {
reject(event.target.error);
};
});
});
console.log(result);
}
// await browser.close();
})().catch((err) => {
console.error(err);
process.exit(1);
});

File diff suppressed because one or more lines are too long

54
utils/businessDataUtil.js Normal file
View File

@ -0,0 +1,54 @@
/**
*
* @param {Page} page
* @param {fs} fs
* @param {String} file 存储文件位置
*/
export async function getDate(page, fs, file) {
const its = new Map();
await new Promise(r => setTimeout(r, 2000));
await page.waitForSelector('.ai_custome');
const aiCustome = await page.$$('.ai_custome .ai_custome_item');
if (aiCustome.length > 0) {
for (const iterator of aiCustome) {
const key = await iterator.$('span');
const value = await iterator.$('div');
const keyStr = await page.evaluate(element => element.textContent.trim(), key);
const valueStr = await page.evaluate(element => element.textContent.trim(), value);
its.set(keyStr, valueStr);
}
} else {
console.log('.ai_custome_item not found');
}
const elementHandle = await page.waitForSelector('.bi_warp iframe');
const frame = await elementHandle.contentFrame();
await frame.waitForSelector('#label-content');
const items = await frame.$$('#label-content');
if (items.length > 0) {
for (const e of items) {
const key = await e.$('span p');
const value = await e.$('span span');
const keyStr = await frame.evaluate(element => element.textContent.trim(), key);
const valueStr = await frame.evaluate(element => element.textContent.trim(), value);
its.set(keyStr, valueStr);
}
} else {
console.log('#label-content not found in the iframe');
}
const convert = {};
for (const [key, value] of its) {
convert[key] = value;
}
console.log(convert);
const importStorage = JSON.stringify(convert, null, 2);
fs.appendFileSync(file, importStorage, 'utf-8');
}

80
utils/commnUtils.js Normal file
View File

@ -0,0 +1,80 @@
/**
* 设置月报时间
* @param {Page} page
* @param {String} element
* @param {String} timeContent
*/
export async function setMonthTime(page, element, timeContent) {
await new Promise(r => setTimeout(r, 2000));
const time = await page.$(element);
await time.click({ clickCount: 3 });
await time.type(timeContent, { delay: 100 });
}
/**
*
* @param {Page} page
* @param {Number} dismensionNumber
*/
export async function changeTimeDimension(page, dismensionNumber) {
const timeDimension = [
'日报',
'周报',
'月报',
'自定义'
];
let rb = await page.$('.arco-space-item .arco-select-view-value');
let s = await page.evaluate(node => node.textContent.trim(), rb);
await rb.click();
const timeMap = new Map();
rb = await page.$$('.arco-select-dropdown-list .arco-select-option');
for (const iterator of rb) {
s = await page.evaluate(node => node.textContent.trim(), iterator)
timeMap.set(s, iterator);
}
console.log(timeDimension[dismensionNumber]);
await timeMap.get(timeDimension[dismensionNumber]).click();
}
/**
*
* @param {Page} page
* @param {fs} fs
* @param {String} file 存储文件位置
*/
export async function getDate(page, fs, file) {
const its = new Map();
await new Promise(r => setTimeout(r, 2000));
const elementHandle = await page.waitForSelector('.bi_warp iframe');
const frame = await elementHandle.contentFrame();
await frame.waitForSelector('#label-content');
const items = await frame.$$('#label-content');
if (items.length > 0) {
for (const e of items) {
const key = await e.$('span p');
const value = await e.$('span span');
const keyStr = await frame.evaluate(element => element.textContent.trim(), key);
const valueStr = await frame.evaluate(element => element.textContent.trim(), value);
its.set(keyStr, valueStr);
}
} else {
console.log('#label-content not found in the iframe');
}
const convert = {};
for (const [key, value] of its) {
convert[key] = value;
}
console.log(convert);
const importStorage = JSON.stringify(convert, null, 2);
fs.appendFileSync(file, importStorage, 'utf-8');
}