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'); })()