puppeteer 是浏览器自动化的产品,它可以实现谷歌浏览器的自动化操作。上一篇文章讲解了利用 puppeteer 爬取网页图片,这是 puppeteer 最基本的用法,本文将进一步探索 puppeteer 的高阶玩法:编写脚本实现12306平台的自动化购票。
前置相关文章:Node.js使用puppeteer+axios爬取页面图片链接并下载图片到本地
1.自动登录
自动输入登录账号和密码并点击登录,输入身份证后四位后获取验证码,手动输入验证码后自动点击登录:
// 打开浏览器并设置浏览器为有头模式,并且减慢操作时间为150ms
const browser = await puppeteer.launch({headless: false,slowMo: 150,});
const page = await browser.newPage(); // 创建一个新的页面
await page.goto('https://kyfw.12306.cn/otn/resources/login.html'); // 12306登录页
await page.type('#J-userName', user) // 输入账号
await page.type('#J-password', pass) // 输入密码
await page.click('#J-login'); // 点击立即登录
await page.type('#id_card', id) // 输入身份证后四位
await page.click('#verification_code'); // 点击获取验证码
// 等待手动输入验证码
await page.waitForFunction(() => {
const captcha = document.querySelector('#code').value; // 获取验证码
return captcha.length >= 6; // 验证码长度大于等于 6 时继续执行后面代码
});
await page.click('#sureClick'); // 点击确认
2.自动检测有票列车并预定
自动输入出发地、目的地、出发时间后自动点击查询按钮,检测到有票列车后自动预定。此部分为购票核心逻辑所在,如果需要指定车次或座位,需要添加或修改相关代码。如需实现循环检票和抢票的功能,也可自行修改。这里给出的只是最简单的示例代码。
await page.waitForSelector('#link_for_ticket'); // 等待登录成功后出现跳转链接
await page.click('#link_for_ticket'); // 进入选票页面
await page.waitForSelector('#query_ticket'); // 等待页面加载
//填写出发地、目的地、出发时间
await page.evaluate((fromstation, tostation, time) => {
document.querySelector('#fromStation').value = fromstation;
document.querySelector('#toStation').value = tostation;
document.querySelector('#train_date').value = time;
}, fromstation, tostation, time)
// 以下代码可以加上循环检测代码 实现抢票功能
await page.click('#query_ticket'); // 点击查询
// 获取所有的车次信息
await page.$$eval('tr', async trs => {
for (let i = 0; i < trs.length; i++) {
let tr = trs[i];
// 长度 13 即为车次信息
if (tr.childElementCount == 13) {
// 筛选出有票的列车 children[3]为二等票 如果需要其他票可以修改此部分
if (tr.children[3].innerText != '候补' && tr.children[3].innerText != '--') {
// console.log(tr.children[3].innerText);
await tr.lastChild.firstChild.click(); // 点击预购车票
break; // 找到有票的列车立即预购 即所抢车票为当天最早的有票列车
}
}
}
});
3.自动添加乘坐人并锁定车票
自动添加乘坐人,默认只添加第一位,如需多位可自行添加自动点击事件。勾选后自动点击提交订单并确认订单。车票自动锁定。10分钟之内可以使用pc端或移动端查看并进行付款。学生票需要额外添加一步确认操作。
await page.waitForSelector('#normalPassenger_0'); // 等待normalPassenger_0元素加载
await page.click('#normalPassenger_0'); // 勾选第一位乘车人
// await page.click('#dialog_xsertcj_ok'); // 学生票需要点击确认
await page.click('#submitOrder_id'); // 提交订单
await page.click('#qr_submit_id'); // 再次确认
完整代码
const puppeteer = require('puppeteer');
const axios = require('axios');
const user = `您的账号`;
const pass = `您的密码`;
const id = `您的身份证后四位`;
const fromstation = `TNN`; // 出发地 天门南
const tostation = `WHN`; // 目的地 武汉南
const time = `2023-08-03`; // 出发日期 格式 YYYY-MM-DD
(async () => {
const browser = await puppeteer.launch({
headless: false,
slowMo: 150,
});
const page = await browser.newPage();
await page.goto('https://kyfw.12306.cn/otn/resources/login.html');
await page.type('#J-userName', user)
await page.type('#J-password', pass)
await page.click('#J-login');
await page.type('#id_card', id);
await page.click('#verification_code');
await page.waitForFunction(() => {
const captcha = document.querySelector('#code').value;
return captcha.length >= 6;
});
await page.click('#sureClick');
await page.waitForSelector('#link_for_ticket');
await page.click('#link_for_ticket');
await page.waitForSelector('#query_ticket');
await page.evaluate((fromstation, tostation, time) => {
document.querySelector('#fromStation').value = fromstation;
document.querySelector('#toStation').value = tostation;
document.querySelector('#train_date').value = time;
}, fromstation, tostation, time)
await page.click('#query_ticket');
await page.$$eval('tr', async trs => {
for (let i = 0; i < trs.length; i++) {
let tr = trs[i];
if (tr.childElementCount == 13) {
if (tr.children[3].innerText != '候补' && tr.children[3].innerText != '--') {
await tr.lastChild.firstChild.click();
break;
}
}
}
});
await page.waitForSelector('#normalPassenger_0');
await page.click('#normalPassenger_0');
await page.click('#submitOrder_id');
await page.click('#qr_submit_id');
})()






