当前位置:首页 » 《随便一记》 » 正文

28. 实战:基于selenium实现12306自动购票

27 人参与  2023年03月07日 14:41  分类 : 《随便一记》  评论

点击全文阅读


目录

前言

目的

思路

代码实现

1. 进入登录界面,输入账号密码

2. 点击登录按钮,完成滑块验证

3. 在个人中心点击购票,跳转

4. 输入出发地、目的地,从控制台输入得到

5. 文本框输入出发日

6. 若是学生票则切换票型

7. 点击查询

8. 定位预定按钮,点击跳转购票页面

9. 选择学生乘客,并在弹窗中确认购买学生票

10. 提交订单,等待付款

完整代码

运行效果

总结


前言

我们已经学会了selenium的基本操作,并且学会了用它处理验证码、跳转网页、处理内联框架等操作,现在可以进行实战:本节选取12306火车购票作为案例,用自动化测试工具selenium实现自动访问网页并下单等待购票。

2023-01-20更新:完善了全部功能并可以完整运行


目的

手动在控制台输入乘车人(新增), 出发地、目的地、出发日、是否购买学生票,确认后自动跳转12306网站购票。


思路

1. 首先获取登陆页面的URL,随后定位账号密码的输入框,用sendkey接口输入个人信息;

2. 获取登录控件的XPATH地址,点击发现弹窗出现滑块验证,使用drag_and_drop_by_offset接口实现拖拽滑块到终点的操作;

3. 登陆以后默认在个人中心,获取购票按钮XPATH地址,点击访问;

4. 分析购票界面,点击文本框以后可以清空当前文本框,所以动作链应当为:点击 -> 输入 -> 按下回车。因为输入以后会弹出选项,所以我们还得点一下回车,直接切换其他文本框会清空;

5. 出发日文本框点击的时候不会清空,所以用clear接口清空文本框,然后输入正确的日期格式yyyy-mm-dd形式;

6. 如果是学生票,切换学生票;

7. 点击查询,拿到可预定车票列表;

8. 用显示等待定位预定按钮,点击跳转购票页面;

9. 用条件等待选择学生乘客,并在弹窗中确认购买学生票 ; 或直接选择一般乘客购票;

10. 提交订单,等待付款。


代码实现

1. 进入登录界面,输入账号密码

opt = Options()# option.add_experimental_option('excludeSwitches', ['enable-automation'])opt.add_argument('--disable-blink-features=AutomationControlled')opt.add_experimental_option('detach', True)opt.add_argument('--start-maximized')  # 浏览器窗口最大web = Chrome(options=opt)web.get("https://kyfw.12306.cn/otn/resources/login.html")web.find_element(By.XPATH, '//*[@id="toolbar_Div"]/div[2]/div[2]/ul/li[1]/a').click()time.sleep(1)# TODO 输入用户名和密码web.find_element(By.XPATH, '//*[@id="J-userName"]').send_keys("username")web.find_element(By.XPATH, '//*[@id="J-password"]').send_keys("password")

2. 点击登录按钮,完成滑块验证

# 点击登录web.find_element(By.XPATH, '//*[@id="J-login"]').click()time.sleep(3)# 拖拽btn = web.find_element(By.XPATH, '//*[@id="nc_1_n1z"]')ActionChains(web).drag_and_drop_by_offset(btn, 300, 0).perform()time.sleep(3)

ActionChains里面有许多动作序列,可以帮助我们完成许多仿人类动作,记得在最后加perform,不然动作序列是不会执行的。

3. 在个人中心点击购票,跳转

# 车票预定web.find_element(By.XPATH, '//*[@id="link_for_ticket"]').click()

4. 输入出发地、目的地,从控制台输入得到

从控制台获得信息:

更新 : 增加了乘车人,便于定位后续购票人

# 初始化购票信息print("***欢迎使用自动购票系统***")print("请依次输入购票信息...")print("=" * 30)fromStationText = input("请输入出发地(示例:介休东)...\n")toStationText = input("请输入目的地(示例:成都东)...\n")train_date = input("请输入出发日(示例:2023-01-19)...\n")is_student = input("是否购买学生票?(y/n)\n")print("=" * 30)print("处理信息中...\n", "处理完毕,请检查您输入的信息...\n", fromStationText, toStationText, train_date)confirm = input("是否确认上述信息?(y/n)\n")if confirm == 'y':    print("=" * 30)    print("初始化完毕,开始运行系统...")if confirm == 'n':    print("=" * 30)    print("请重新运行程序!")    exit(1)

修改:可以精简sendkeys操作,将它们放入一行:将信息输入文本框:

# 输入信息(出发地、目的地、出发日)# 出发地web.find_element(By.XPATH, '//*[@id="fromStationText"]').click()web.find_element(By.XPATH, '//*[@id="fromStationText"]').send_keys(fromStationText, Keys.ENTER)time.sleep(1)# 目的地web.find_element(By.XPATH, '//*[@id="toStationText"]').click()web.find_element(By.XPATH, '//*[@id="toStationText"]').send_keys(toStationText, Keys.ENTER)time.sleep(1)

5. 文本框输入出发日

# 出发日# date = web.find_element(By.XPATH, '//*[@id="train_date"]')# ActionChains(web).drag_and_drop_by_offset(date, 175, 0).perform()web.find_element(By.XPATH, '//*[@id="train_date"]').clear()web.find_element(By.XPATH, '//*[@id="train_date"]').send_keys(train_date)time.sleep(1)

6. 若是学生票则切换票型

# 点击查询if is_student == 'y':    web.find_element(By.XPATH, '//*[@id="sf2_label"]').click()    time.sleep(1)

7. 点击查询

# 点击查询web.find_element(By.XPATH, '//*[@id="query_ticket"]').click()print("=" * 30)print("查询完毕...")time.sleep(1)

8. 定位预定按钮,点击跳转购票页面

定位思路有两种,一个是直接找到控件,另一个是相对查找,这里我选用第二种。由于第一种的控件地址隐藏较深,无法直接在源代码定位,藏在js里面,所以我们直接用标头的最后一项相对查找就行了。

# 点击预定# TODO 待办:定位预订控件get_ticket = web.find_element(By.XPATH, '//*[@id="float"]/th[16]')ActionChains(web).move_to_element_with_offset(get_ticket, 55, 70).click().perform()

但是这样很难精准定位,不能适用所有网页,所以还是实践第一种方法:找到控件:

最终确认方法:显示等待

# 点击预定WebDriverWait(web, 1000).until(EC.presence_of_element_located((By.XPATH, '//*[@id="queryLeftTable"]/tr')))tr_list = web.find_elements(By.XPATH, '//*[@id="queryLeftTable"]/tr[not(@datatran)]')  # 每一列列车整行信息列表,列车号元素是tr的子元素if not tr_list:    print("=" * 30)    print(f"很抱歉,按您的查询条件,当前未找到从{fromStationText}到{toStationText}的列车。")    exit(1)for tr in tr_list:    train_num = tr.find_element(By.XPATH, './td[1]/div/div[1]/div/a').text  # 取出元素tr里的列车号    # 动车二等座余票信息    text_1 = tr.find_element(By.XPATH, "./td[4]").text    # 火车二等座余票信息    text_2 = tr.find_element(By.XPATH, "./td[8]").text    if (text_1 == "有" or text_1.isdigit()) or (text_2 == "有" or text_2.isdigit()):        # 点击预订按钮        order_btn = tr.find_element(By.CLASS_NAME, "btn72")        order_btn.click()        # 等待订票页面        WebDriverWait(web, 1000).until(EC.url_to_be('https://kyfw.12306.cn/otn/confirmPassenger/initDc'))        print("=" * 30)        print(train_num, "二等座有票!")        break    else:        print("=" * 30)        print(train_num, "二等座无票!")        continue

9. 选择学生乘客,并在弹窗中确认购买学生票

修改:用expected condition(EC)选取处理弹窗事件。

# 跳转页面提交订单# 选定乘车人web.find_element(By.XPATH, f'//*[@id="normal_passenger_id"]/li/label[contains(text(),"{passenger}")]').click()# 如果乘客是学生,对提示点击确定if EC.presence_of_element_located((By.XPATH, '//div[@id="dialog_xsertcj"]')):    web.find_element(By.ID, 'dialog_xsertcj_ok').click()    # 提交订单    web.find_element(By.ID, 'submitOrder_id').click()    time.sleep(2)else:    # 提交订单    web.find_element(By.ID, 'submitOrder_id').click()    time.sleep(2)

10. 提交订单,等待付款

修改:更新了座位ID无法找到的问题,向上定位XPATH再往后确认

# 选座print("=" * 30)seat = input("请尽快进行选座操作([窗]A/B/C/[过道]/D/F[窗])\n")if seat == 'A':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[1]/li[1]/a').click()if seat == 'B':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[1]/li[2]/a').click()if seat == 'C':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[1]/li[3]/a').click()if seat == 'D':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[2]/li[1]/a').click()if seat == 'F':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[2]/li[2]/a').click()# 最终确认web.find_element(By.XPATH, '//*[@id="qr_submit_id"]').click()print("=" * 30)print("*"*40, "\n---***<|订单创建完成,请于10分钟内付款|>***---")print("*"*40)


完整代码

from selenium.webdriver import Chromefrom selenium.webdriver.common.action_chains import ActionChainsfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keysfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECimport time# 初始化购票信息print("***欢迎使用自动购票系统***")print("请依次输入购票信息...")print("=" * 30)passenger = input("请输入乘车人(示例:蔡徐坤)...\n")fromStationText = input("请输入出发地(示例:介休东)...\n")toStationText = input("请输入目的地(示例:成都东)...\n")train_date = input("请输入出发日(示例:2023-01-19)...\n")is_student = input("是否购买学生票?(y/n)\n")print("=" * 30)print("处理信息中...\n", "处理完毕,请检查您输入的信息...\n", fromStationText, toStationText, train_date)confirm = input("是否确认上述信息?(y/n)\n")if confirm == 'y':    print("=" * 30)    print("初始化完毕,开始运行系统...")if confirm == 'n':    print("=" * 30)    print("请重新运行程序!")    exit(1)# 2.chrome的版本大于等于88opt = Options()# option.add_experimental_option('excludeSwitches', ['enable-automation'])opt.add_argument('--disable-blink-features=AutomationControlled')opt.add_experimental_option('detach', True)opt.add_argument('--start-maximized')  # 浏览器窗口最大web = Chrome(options=opt)web.get("https://kyfw.12306.cn/otn/resources/login.html")web.find_element(By.XPATH, '//*[@id="toolbar_Div"]/div[2]/div[2]/ul/li[1]/a').click()time.sleep(1)# TODO 输入用户名和密码web.find_element(By.XPATH, '//*[@id="J-userName"]').send_keys("18306825490")web.find_element(By.XPATH, '//*[@id="J-password"]').send_keys("lk020511")# 点击登录web.find_element(By.XPATH, '//*[@id="J-login"]').click()time.sleep(3)# 拖拽btn = web.find_element(By.XPATH, '//*[@id="nc_1_n1z"]')ActionChains(web).drag_and_drop_by_offset(btn, 300, 0).perform()time.sleep(3)# 车票预定web.find_element(By.XPATH, '//*[@id="link_for_ticket"]').click()# 输入信息(出发地、目的地、出发日)# 出发地web.find_element(By.XPATH, '//*[@id="fromStationText"]').click()web.find_element(By.XPATH, '//*[@id="fromStationText"]').send_keys(fromStationText, Keys.ENTER)time.sleep(1)# 目的地web.find_element(By.XPATH, '//*[@id="toStationText"]').click()web.find_element(By.XPATH, '//*[@id="toStationText"]').send_keys(toStationText, Keys.ENTER)time.sleep(1)# 出发日web.find_element(By.XPATH, '//*[@id="train_date"]').clear()web.find_element(By.XPATH, '//*[@id="train_date"]').send_keys(train_date)time.sleep(1)# 点击查询if is_student == 'y':    web.find_element(By.XPATH, '//*[@id="sf2_label"]').click()    time.sleep(1)web.find_element(By.XPATH, '//*[@id="query_ticket"]').click()print("=" * 30)print("查询完毕...")time.sleep(1)# 点击预定WebDriverWait(web, 1000).until(EC.presence_of_element_located((By.XPATH, '//*[@id="queryLeftTable"]/tr')))tr_list = web.find_elements(By.XPATH, '//*[@id="queryLeftTable"]/tr[not(@datatran)]')  # 每一列列车整行信息列表,列车号元素是tr的子元素if not tr_list:    print("=" * 30)    print(f"很抱歉,按您的查询条件,当前未找到从{fromStationText}到{toStationText}的列车。")    exit(1)for tr in tr_list:    train_num = tr.find_element(By.XPATH, './td[1]/div/div[1]/div/a').text  # 取出元素tr里的列车号    # 动车二等座余票信息    text_1 = tr.find_element(By.XPATH, "./td[4]").text    # 火车二等座余票信息    text_2 = tr.find_element(By.XPATH, "./td[8]").text    if (text_1 == "有" or text_1.isdigit()) or (text_2 == "有" or text_2.isdigit()):        # 点击预订按钮        order_btn = tr.find_element(By.CLASS_NAME, "btn72")        order_btn.click()        # 等待订票页面        WebDriverWait(web, 1000).until(EC.url_to_be('https://kyfw.12306.cn/otn/confirmPassenger/initDc'))        print("=" * 30)        print(train_num, "二等座有票!")        break    else:        print("=" * 30)        print(train_num, "二等座无票!")        continue# 跳转页面提交订单# 选定乘车人web.find_element(By.XPATH, f'//*[@id="normal_passenger_id"]/li/label[contains(text(),"{passenger}")]').click()# 如果乘客是学生,对提示点击确定if EC.presence_of_element_located((By.XPATH, '//div[@id="dialog_xsertcj"]')):    web.find_element(By.ID, 'dialog_xsertcj_ok').click()    # 提交订单    web.find_element(By.ID, 'submitOrder_id').click()    time.sleep(2)else:    # 提交订单    web.find_element(By.ID, 'submitOrder_id').click()    time.sleep(2)# 选座print("=" * 30)seat = input("请尽快进行选座操作([窗]A/B/C/[过道]/D/F[窗])\n")if seat == 'A':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[1]/li[1]/a').click()if seat == 'B':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[1]/li[2]/a').click()if seat == 'C':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[1]/li[3]/a').click()if seat == 'D':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[2]/li[1]/a').click()if seat == 'F':    web.find_element(By.XPATH, '//*[@id="erdeng1"]/ul[2]/li[2]/a').click()# 最终确认web.find_element(By.XPATH, '//*[@id="qr_submit_id"]').click()print("=" * 30)print("*"*40, "\n---***<|订单创建完成,请于10分钟内付款|>***---")print("*"*40)


运行效果

 

 

 

 

 


总结

本节是基于selenium的浏览器自动化操作的实例,较为综合,涉及的知识点也比较多,仅供小伙伴们参考学习,请勿用于其他用途!

不太明白的小伙伴可以移步我之前发布过的selenium基础和简单的验证码实战


点击全文阅读


本文链接:http://zhangshiyu.com/post/54130.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1