前言
不知道大家有没有之前碰到这样的情况,打算去某一个地方当你规划好了时间准备去买票的时候,你想要的那一列往往没有你想要的票了,尤其是国庆七天假和春节半月假,有时候甚至买不到规定计划时间内的票,真的是太烦躁了
为此我钻研了一下,现在科技如此发达,想要实现自动化还是比较简单的
1.导入需要的模块
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
2.初始化WebDriver:
driver = webdriver.Chrome() # 选择合适的浏览器驱动,这里以Chrome为例
3.打开12306网站:
driver.get('https://www.12306.cn')
4.登录12306账号:
首先手动登录一次,然后在浏览器中的开发者工具中找到登录请求的相关信息,提取出关键参数(比如cookies、token等),可以使用工具库来自动提取这些参数。
使用Python的requests库等发送该登录请求,将提取到的关键参数作为请求的header或body发送过去,模拟登录。
5.进入车票查询页面:
# 这里可能需要等待一段时间,直到页面加载完成
# 可以使用WebDriverWait等待特定的元素加载完成
wait = WebDriverWait(driver, 10) # 设置等待时间为10秒
query_input = wait.until(EC.presence_of_element_located((By.ID, 'query_input')))
query_input.clear()
query_input.send_keys('出发地点')
上述代码将输入出发地点,你需要根据自己的需求修改。
6.查询车票:
search_btn = driver.find_element_by_id('search_btn')
search_btn.click()# 这里可能需要等待一段时间,直到查询结果加载完成
# 同样可以使用WebDriverWait等待特定的元素加载完成
7.选择车次和座位:
train_btn = driver.find_element_by_id('train_btn')
train_btn.click()# 这里可能需要等待一段时间,直到车次详情加载完成seat_type = driver.find_element_by_id('seat_type')
seat_type.send_keys('座位类型')buy_btn = driver.find_element_by_id('buy_btn')
buy_btn.click()
上述代码将选择指定的座位类型,你需要根据自己的需求修改。
8.填写乘客信息和提交订单:
passenger_name = driver.find_element_by_id('passenger_name')
passenger_name.send_keys('乘客姓名')id_number = driver.find_element_by_id('id_number')
id_number.send_keys('乘客身份证号码')# 填写其他乘客信息,如果有多个乘客submit_btn = driver.find_element_by_id('submit_btn')
submit_btn.click()
上述代码将填写乘客的姓名、身份证号码等信息,你需要根据自己的需求修改。
9.处理验证码:
12306网站可能会出现验证码,你需要使用图像处理库(如PIL)来处理验证码图片并自动识别验证码。
如果遇到验证码,你可以通过人工干预或使用一些自动化技术(如OCR)处理,以便自动填写验证码。
10.确认订单和支付:
confirm_btn = driver.find_element_by_id('confirm_btn')
confirm_btn.click()# 这里可能需要等待一段时间,直到支付页面加载完成# 在该页面处理支付,根据你使用的支付方式进行自动化支付
以上只是一个基本的框架,实际实现可能需要根据12306网站的更新和变化进行相应的调整
那么根据具体时间2023年8月10号为例子,我们来具体操作一下,以下是具体源码:
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from config import Config
from selenium.webdriver.common.keys import Keys
import time
import select# 用抛出异常来判断一个元素存不存在太慢了,需要等5秒钟
# def isElementExist(ele):
# flag = True
# result = EC.presence_of_element_located((By.XPATH, '//tbody[@id="queryLeftTable"]/tr[1]/td[13]/a'))
# try:
# # ele.find_element(by=By.CLASS_NAME, value='btn72')
# result(ele)
# return flag
# except:
# flag = False
# return flagdef isElementExist(driver):flag=Trueele = driver.find_elements(by=By.CLASS_NAME, value='btn72')if len(ele) == 0:flag = Falsereturn flagif len(ele) == 1:return flagelse:flag = Falsereturn flagdef get_ticket(conf, driver, url):# 过网站检测,没加这句的话,账号密码登录时滑动验证码过不了,但二维码登录不受影响driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"""})driver.maximize_window()driver.get(url)# 最多等待5秒使页面加载进来,隐式等待driver.implicitly_wait(5)# 获取并点击右上角登录按钮login = driver.find_element(by=By.ID, value='J-btn-login')login.click()driver.implicitly_wait(10)# 账号密码登录username_tag = driver.find_element(by=By.ID, value='J-userName')username_tag.send_keys(conf.username)password_tag = driver.find_element(by=By.ID, value='J-password')password_tag.send_keys(conf.password)login_now = driver.find_element(by=By.ID, value='J-login')login_now.click()time.sleep(20)# # 过滑动验证码# picture_start = driver.find_element(by=By.ID, value='nc_1_n1z')# # 移动到相应的位置,并左键鼠标按住往右边拖# ActionChains(driver).move_to_element(picture_start).click_and_hold(picture_start).move_by_offset(300, 0).release().perform()### # 扫码登录# scan_QR = driver.find_element(by=By.XPATH, value='//*[@id="toolbar_Div"]/div[2]/div[2]/ul/li[2]/a')# scan_QR.click()# driver.implicitly_wait(10)# 点提示框# driver.find_element(by=By.XPATH, value='//div[@class="dzp-confirm"]/div[2]/div[3]/a').click()# driver.implicitly_wait(5)# 点击车票预订跳转到预订车票页面driver.find_element(by=By.XPATH, value='//*[@id="link_for_ticket"]').click()driver.implicitly_wait(10)# 输入出发地和目的地信息# 出发地driver.find_element(by=By.XPATH, value='//*[@id="fromStationText"]').click()driver.find_element(by=By.XPATH, value='//*[@id="fromStationText"]').clear()driver.find_element(by=By.XPATH, value='//*[@id="fromStationText"]').send_keys(conf.fromstation)time.sleep(1)driver.find_element(by=By.XPATH, value='//*[@id="fromStationText"]').send_keys(Keys.ENTER)# 目的地destination_tag = driver.find_element(by=By.XPATH, value='//*[@id="toStationText"]')destination_tag.click()destination_tag.clear()destination_tag.send_keys(conf.destination)time.sleep(1)destination_tag.send_keys(Keys.ENTER)driver.implicitly_wait(5)# 出发日期date_tag = driver.find_element(by=By.XPATH, value='//*[@id="train_date"]')date_tag.click()date_tag.clear()date_tag.send_keys(conf.date)time.sleep(1)query_tag = driver.find_element(by=By.XPATH, value='//*[@id="query_ticket"]')start = time.time()while True:driver.implicitly_wait(5)# 点击查询driver.execute_script("$(arguments[0]).click()", query_tag)# 判断页面中有没有“预订”按钮,如果没有预订按钮就不断查询直到车票开售if not isElementExist(driver):# 车票处于待开售状态print(f"15点30分起售,现在是{time.strftime('%H:%M:%S', time.localtime())},还未开始售票")# 每隔两分钟刷新一次,否则3分钟内无购票操作12306系统会自动登出if time.time() - start >= 120:driver.refresh()start = time.time()# 延时1秒防止过于快速地点击导致查询超时,当然偶尔还是会出现超时现象,不过超时也没关系,一般等待6秒之后就会继续自动查询time.sleep(1)continue# 获取所有车票tickets = driver.find_elements(by=By.XPATH, value='//*[@id="queryLeftTable"]/tr')# 每张车票有两个tr,但是第二个tr没什么用tickets = [tickets[i] for i in range(len(tickets) - 1) if i % 2 == 0]#print(tickets)for ticket in tickets:# 如果车票的车次等于想要的车次并且硬卧的状态不是候补则点击预订#if ticket.find_element(by=By.CLASS_NAME,value='cdz').text== conf.fromstation:#print(ticket.find_element(by=By.CLASS_NAME,value='number').text)# value = '//td[8]'表示硬卧,td[10]表示硬座if ticket.find_element(by=By.CLASS_NAME,value='number').text == conf.trainnumber and ticket.find_element(by=By.XPATH, value='//td[8]').text != "候补":# 点击预订#print(ticket.find_element(by=By.CLASS_NAME,value='cdz').text)#time.sleep(1)ticket.find_element(by=By.CLASS_NAME, value='btn72').click()# 这里之后就不能继续使用ticket.find_element()了,因为页面进行了跳转,会出现stale element reference: element is not attached to the page document的错误# 我们可以使用driver.find_element()# 选择乘车人,如果是学生,则需要确认购买学生票driver.find_element(by=By.XPATH, value='//*[@id="normalPassenger_0"]').click()# 点击确认购买学生票,如果不是学生,把这行注释了就行#driver.find_element(by=By.XPATH, value='//*[@id="dialog_xsertcj_ok"]').click()# 第二个乘车人# driver.find_element(by=By.XPATH, value='//*[@id="normalPassenger_1"]').click()# 如果第二个乘车人也是学生,则需要点击确认第二个人也购买学生票# driver.find_element(by=By.XPATH, value='//*[@id="dialog_xsertcj_ok"]').click()# 提交订单driver.find_element(by=By.XPATH, value='//*[@id="submitOrder_id"]').click()# 选座 F座#time.sleep(1)#move = driver.find_element(By.ID, value='1F')#ActionChains(driver).move_to_element(move).perform()# time.sleep(1)#这里直接使用id和xpath定位不到,所以直接加上他的路径,可以不用这么长,但是懒得删driver.find_element(by=By.XPATH, value='//html/body/div[5]/div/div[5]/div[1]/div/div[2]/div[2]/div[3]/div[2]/div[2]/ul[2]/li[2]/a[@id="1F"]').click()# 确认提交订单,然后这里和上面是一样的driver.find_element(by=By.XPATH, value='//html/body/div[5]/div/div[5]/div[1]/div/div[2]/div[2]/div[8]/a[2][@id="qr_submit_id"]').click()print(f"{conf.trainnumber}次列车抢票成功,请尽快在10分钟内支付!")returnif __name__ == '__main__':# 有关车票的配置信息保存在该类里# 请事先在config.py里填好相关信息conf = Config()url = 'https://www.12306.cn/index/'# chromedriver.exe版本为104,可以根据自己浏览器版本重新下载chromedriver.exe替换# chromedriver.exe下载地址:http://chromedriver.storage.googleapis.com/index.html# s = Service(r'chromedriver.exe')driver = webdriver.Chrome()get_ticket(conf, driver, url)time.sleep(10)driver.quit()