feat: 飞猪脚本完善
This commit is contained in:
@@ -91,96 +91,113 @@ const toggleRoomByDateIndex = async (page, container, { roomType, dateIndex, ope
|
|||||||
};
|
};
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await chromium.connectOverCDP(`http://127.0.0.1:9222`);
|
let browser;
|
||||||
const context = browser.contexts()[0];
|
|
||||||
|
|
||||||
await context.addInitScript(() => {
|
try {
|
||||||
Object.defineProperty(navigator, 'webdriver', { get: ()=> undefined });
|
browser = await chromium.connectOverCDP(`http://127.0.0.1:9222`);
|
||||||
});
|
const context = browser.contexts()[0];
|
||||||
|
|
||||||
const pages = await context.pages();
|
|
||||||
|
|
||||||
const page = pages.length ? pages[0] : await context.newPage();
|
await context.addInitScript(() => {
|
||||||
|
Object.defineProperty(navigator, 'webdriver', { get: ()=> undefined });
|
||||||
await page.goto('https://hotel.fliggy.com/ebooking/hotelBaseInfoUv.htm#/ebk/homeV1');
|
});
|
||||||
|
|
||||||
const isLogin = await checkLoginStatus(page);
|
|
||||||
|
|
||||||
if(!isLogin) {
|
|
||||||
await page.getByRole('textbox', { name: '请输入账号' }).dblclick();
|
|
||||||
await page.getByRole('textbox', { name: '请输入账号' }).click();
|
|
||||||
await page.getByRole('textbox', { name: '请输入账号' }).fill('hoteltmwq123', { delay: 80 + Math.random() * 120 });
|
|
||||||
await page.waitForTimeout(1000 + Math.random() * 1000);
|
|
||||||
await page.getByRole('button', { name: '下一步' }).click();
|
|
||||||
|
|
||||||
const frame_1 = await page.frameLocator('#alibaba-login-box');
|
|
||||||
await page.locator('#alibaba-login-box').contentFrame().getByRole('textbox', { name: '请输入登录密码' }).dblclick();
|
|
||||||
await page.locator('#alibaba-login-box').contentFrame().getByRole('textbox', { name: '请输入登录密码' }).fill('Tmwq654321', { delay: 80 + Math.random() * 120 });
|
|
||||||
await page.waitForTimeout(1000 + Math.random() * 1000);
|
|
||||||
await page.locator('#alibaba-login-box').contentFrame().getByRole('button', { name: '登录' }).click();
|
|
||||||
|
|
||||||
// 等待滑块真正出现在 DOM 并可见
|
|
||||||
await page.waitForTimeout(4000 + Math.random() * 1000);
|
|
||||||
const frame_2 = await frame_1.frameLocator('#baxia-dialog-content');
|
|
||||||
const container = await frame_2.locator('#nc_1_nocaptcha');
|
|
||||||
const slider = await frame_2.locator('#nc_1_n1z');
|
|
||||||
const isVisible = await slider.isVisible();
|
|
||||||
|
|
||||||
if (isVisible) {
|
|
||||||
// 重新获取滑块按钮(可能嵌套在 iframe 里)
|
|
||||||
const containerBox = await container.boundingBox();
|
|
||||||
const sliderBox = await slider.boundingBox();
|
|
||||||
|
|
||||||
const startX = sliderBox.x + sliderBox.width / 2;
|
const pages = await context.pages();
|
||||||
const startY = sliderBox.y + sliderBox.height / 2;
|
|
||||||
const distance = containerBox.width - sliderBox.width; // 适当拉长拖动距离
|
const page = pages.length ? pages[0] : await context.newPage();
|
||||||
const steps = 20; // 分多步模拟人手拖动
|
|
||||||
|
await page.goto('https://hotel.fliggy.com/ebooking/hotelBaseInfoUv.htm#/ebk/homeV1');
|
||||||
await page.mouse.move(startX, startY);
|
|
||||||
// 等待随机时间再开始滑动(模拟人类反应)
|
const isLogin = await checkLoginStatus(page);
|
||||||
await page.waitForTimeout(200 + Math.random() * 300);
|
|
||||||
await page.mouse.down();
|
if(!isLogin) {
|
||||||
await page.waitForTimeout(100 + Math.random() * 200);
|
await page.getByRole('textbox', { name: '请输入账号' }).dblclick();
|
||||||
|
await page.getByRole('textbox', { name: '请输入账号' }).click();
|
||||||
// 按轨迹滑动
|
await page.getByRole('textbox', { name: '请输入账号' }).fill('hoteltmwq123', { delay: 80 + Math.random() * 120 });
|
||||||
for (let i = 0; i < steps; i++) {
|
await page.waitForTimeout(1000 + Math.random() * 1000);
|
||||||
await page.mouse.move(
|
await page.getByRole('button', { name: '下一步' }).click();
|
||||||
sliderBox.x + sliderBox.width / 2 + (distance * (i + 1) / steps),
|
|
||||||
sliderBox.y + sliderBox.height / 2 + (Math.random() * 5 - 2.5), // 模拟轻微Y轴抖动
|
const frame_1 = await page.frameLocator('#alibaba-login-box');
|
||||||
{ steps: 5 }
|
await page.locator('#alibaba-login-box').contentFrame().getByRole('textbox', { name: '请输入登录密码' }).dblclick();
|
||||||
);
|
await page.locator('#alibaba-login-box').contentFrame().getByRole('textbox', { name: '请输入登录密码' }).fill('Tmwq654321', { delay: 80 + Math.random() * 120 });
|
||||||
|
await page.waitForTimeout(1000 + Math.random() * 1000);
|
||||||
|
await page.locator('#alibaba-login-box').contentFrame().getByRole('button', { name: '登录' }).click();
|
||||||
|
|
||||||
|
// 等待滑块真正出现在 DOM 并可见
|
||||||
|
await page.waitForTimeout(4000 + Math.random() * 1000);
|
||||||
|
const frame_2 = await frame_1.frameLocator('#baxia-dialog-content');
|
||||||
|
const container = await frame_2.locator('#nc_1_nocaptcha');
|
||||||
|
const slider = await frame_2.locator('#nc_1_n1z');
|
||||||
|
const isVisible = await slider.isVisible();
|
||||||
|
|
||||||
|
if (isVisible) {
|
||||||
|
// 重新获取滑块按钮(可能嵌套在 iframe 里)
|
||||||
|
const containerBox = await container.boundingBox();
|
||||||
|
const sliderBox = await slider.boundingBox();
|
||||||
|
|
||||||
|
const startX = sliderBox.x + sliderBox.width / 2;
|
||||||
|
const startY = sliderBox.y + sliderBox.height / 2;
|
||||||
|
const distance = containerBox.width - sliderBox.width; // 适当拉长拖动距离
|
||||||
|
const steps = 20; // 分多步模拟人手拖动
|
||||||
|
|
||||||
|
await page.mouse.move(startX, startY);
|
||||||
|
// 等待随机时间再开始滑动(模拟人类反应)
|
||||||
|
await page.waitForTimeout(200 + Math.random() * 300);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.waitForTimeout(100 + Math.random() * 200);
|
||||||
|
|
||||||
|
// 按轨迹滑动
|
||||||
|
for (let i = 0; i < steps; i++) {
|
||||||
|
await page.mouse.move(
|
||||||
|
sliderBox.x + sliderBox.width / 2 + (distance * (i + 1) / steps),
|
||||||
|
sliderBox.y + sliderBox.height / 2 + (Math.random() * 5 - 2.5), // 模拟轻微Y轴抖动
|
||||||
|
{ steps: 5 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.mouse.up();
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.mouse.up();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
await page.waitForTimeout(4000 + Math.random() * 300);
|
await page.waitForTimeout(4000 + Math.random() * 300);
|
||||||
|
|
||||||
await page.getByRole('menuitem', { name: '房价房量管理' }).click();
|
await page.getByRole('menuitem', { name: '房价房量管理' }).click();
|
||||||
await page.waitForTimeout(4000 + Math.random() * 1000);
|
await page.waitForTimeout(4000 + Math.random() * 1000);
|
||||||
await page.getByText('房价房量日历').click();
|
await page.getByText('房价房量日历').click();
|
||||||
|
|
||||||
const roomType = process.env.ROOM_TYPE;
|
const roomType = process.env.ROOM_TYPE;
|
||||||
const startDate = process.env.START_DATE;
|
const startDate = process.env.START_DATE;
|
||||||
const endDate = process.env.END_DATE;
|
const endDate = process.env.END_DATE;
|
||||||
const operation = process.env.OPERATION === 'close' ? 'close' : 'open';
|
const operation = process.env.OPERATION === 'close' ? 'close' : 'open';
|
||||||
|
|
||||||
if (!roomType || !startDate) {
|
if (!roomType || !startDate) {
|
||||||
log.info('ROOM_TYPE/START_DATE not provided, skip room toggle.');
|
log.info('ROOM_TYPE/START_DATE not provided, skip room toggle.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = page.locator('#price-reserve-table-container');
|
const container = page.locator('#price-reserve-table-container');
|
||||||
await container.waitFor({ state: 'visible' });
|
await container.waitFor({ state: 'visible' });
|
||||||
|
|
||||||
const dateList = buildDateList(startDate, endDate);
|
const dateList = buildDateList(startDate, endDate);
|
||||||
for (const mmdd of dateList) {
|
for (const mmdd of dateList) {
|
||||||
const dateIndex = await findHeaderDateIndex(container, mmdd);
|
const dateIndex = await findHeaderDateIndex(container, mmdd);
|
||||||
if (dateIndex < 0) {
|
if (dateIndex < 0) {
|
||||||
throw new Error(`Date not found in header: ${mmdd}`);
|
throw new Error(`Date not found in header: ${mmdd}`);
|
||||||
|
}
|
||||||
|
await toggleRoomByDateIndex(page, container, { roomType, dateIndex, operation });
|
||||||
|
await page.waitForTimeout(600 + Math.random() * 600);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log.error(error);
|
||||||
|
process.exitCode = 1;
|
||||||
|
}finally {
|
||||||
|
if (browser) {
|
||||||
|
try {
|
||||||
|
if (typeof browser.disconnect === 'function') {
|
||||||
|
await browser.disconnect();
|
||||||
|
} else {
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
await toggleRoomByDateIndex(page, container, { roomType, dateIndex, operation });
|
|
||||||
await page.waitForTimeout(600 + Math.random() * 600);
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user