前言
不知道大家有沒有之前碰到這樣的情況,打算去某一個(gè)地方當(dāng)你規(guī)劃好了時(shí)間準(zhǔn)備去買票的時(shí)候宋梧,你想要的那一列往往沒有你想要的票了匣沼,尤其是國慶七天假和春節(jié)半月假,有時(shí)候甚至買不到規(guī)定計(jì)劃時(shí)間內(nèi)的票捂龄,真的是太煩躁了
為此我鉆研了一下释涛,現(xiàn)在科技如此發(fā)達(dá)加叁,想要實(shí)現(xiàn)自動(dòng)化還是比較簡(jiǎn)單的
1.導(dǎo)入需要的模塊
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() # 選擇合適的瀏覽器驅(qū)動(dòng),這里以Chrome為例
3.打開12306網(wǎng)站:
driver.get('https://www.12306.cn')
4.登錄12306賬號(hào):
首先手動(dòng)登錄一次唇撬,然后在瀏覽器中的開發(fā)者工具中找到登錄請(qǐng)求的相關(guān)信息它匕,提取出關(guān)鍵參數(shù)(比如cookies、token等)窖认,可以使用工具庫來自動(dòng)提取這些參數(shù)豫柬。
使用Python的requests庫等發(fā)送該登錄請(qǐng)求,將提取到的關(guān)鍵參數(shù)作為請(qǐng)求的header或body發(fā)送過去扑浸,模擬登錄烧给。
5.進(jìn)入車票查詢頁面:
# 這里可能需要等待一段時(shí)間,直到頁面加載完成
# 可以使用WebDriverWait等待特定的元素加載完成
wait = WebDriverWait(driver, 10) # 設(shè)置等待時(shí)間為10秒
query_input = wait.until(EC.presence_of_element_located((By.ID, 'query_input')))
query_input.clear()
query_input.send_keys('出發(fā)地點(diǎn)')
上述代碼將輸入出發(fā)地點(diǎn)喝噪,你需要根據(jù)自己的需求修改础嫡。
6.查詢車票:
search_btn = driver.find_element_by_id('search_btn')
search_btn.click()
# 這里可能需要等待一段時(shí)間,直到查詢結(jié)果加載完成
# 同樣可以使用WebDriverWait等待特定的元素加載完成
7.選擇車次和座位:
train_btn = driver.find_element_by_id('train_btn')
train_btn.click()
# 這里可能需要等待一段時(shí)間酝惧,直到車次詳情加載完成
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()
上述代碼將選擇指定的座位類型榴鼎,你需要根據(jù)自己的需求修改。
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('乘客身份證號(hào)碼')
# 填寫其他乘客信息晚唇,如果有多個(gè)乘客
submit_btn = driver.find_element_by_id('submit_btn')
submit_btn.click()
上述代碼將填寫乘客的姓名檬贰、身份證號(hào)碼等信息,你需要根據(jù)自己的需求修改缺亮。
9.處理驗(yàn)證碼:
12306網(wǎng)站可能會(huì)出現(xiàn)驗(yàn)證碼翁涤,你需要使用圖像處理庫(如PIL)來處理驗(yàn)證碼圖片并自動(dòng)識(shí)別驗(yàn)證碼。
如果遇到驗(yàn)證碼萌踱,你可以通過人工干預(yù)或使用一些自動(dòng)化技術(shù)(如OCR)處理葵礼,以便自動(dòng)填寫驗(yàn)證碼。
10.確認(rèn)訂單和支付:
confirm_btn = driver.find_element_by_id('confirm_btn')
confirm_btn.click()
# 這里可能需要等待一段時(shí)間并鸵,直到支付頁面加載完成
# 在該頁面處理支付鸳粉,根據(jù)你使用的支付方式進(jìn)行自動(dòng)化支付
以上只是一個(gè)基本的框架,實(shí)際實(shí)現(xiàn)可能需要根據(jù)12306網(wǎng)站的更新和變化進(jìn)行相應(yīng)的調(diào)整
那么根據(jù)具體時(shí)間2023年8月10號(hào)為例子园担,我們來具體操作一下届谈,以下是具體源碼:
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
# 用拋出異常來判斷一個(gè)元素存不存在太慢了,需要等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 flag
def isElementExist(driver):
flag=True
ele = driver.find_elements(by=By.CLASS_NAME, value='btn72')
if len(ele) == 0:
flag = False
return flag
if len(ele) == 1:
return flag
else:
flag = False
return flag
def get_ticket(conf, driver, url):
# 過網(wǎng)站檢測(cè)弯汰,沒加這句的話艰山,賬號(hào)密碼登錄時(shí)滑動(dòng)驗(yàn)證碼過不了,但二維碼登錄不受影響
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": """Object.defineProperty(navigator, 'webdriver', {
get: () => undefined})"""})
driver.maximize_window()
driver.get(url)
# 最多等待5秒使頁面加載進(jìn)來咏闪,隱式等待
driver.implicitly_wait(5)
# 獲取并點(diǎn)擊右上角登錄按鈕
login = driver.find_element(by=By.ID, value='J-btn-login')
login.click()
driver.implicitly_wait(10)
# 賬號(hào)密碼登錄
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)
# # 過滑動(dòng)驗(yàn)證碼
# picture_start = driver.find_element(by=By.ID, value='nc_1_n1z')
# # 移動(dòng)到相應(yīng)的位置曙搬,并左鍵鼠標(biāo)按住往右邊拖
# 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)
# 點(diǎn)提示框
# driver.find_element(by=By.XPATH, value='//div[@class="dzp-confirm"]/div[2]/div[3]/a').click()
# driver.implicitly_wait(5)
# 點(diǎn)擊車票預(yù)訂跳轉(zhuǎn)到預(yù)訂車票頁面
driver.find_element(by=By.XPATH, value='//*[@id="link_for_ticket"]').click()
driver.implicitly_wait(10)
# 輸入出發(fā)地和目的地信息
# 出發(fā)地
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)
# 出發(fā)日期
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)
# 點(diǎn)擊查詢
driver.execute_script("$(arguments[0]).click()", query_tag)
# 判斷頁面中有沒有“預(yù)訂”按鈕,如果沒有預(yù)訂按鈕就不斷查詢直到車票開售
if not isElementExist(driver):
# 車票處于待開售狀態(tài)
print(f"15點(diǎn)30分起售,現(xiàn)在是{time.strftime('%H:%M:%S', time.localtime())}纵装,還未開始售票")
# 每隔兩分鐘刷新一次征讲,否則3分鐘內(nèi)無購票操作12306系統(tǒng)會(huì)自動(dòng)登出
if time.time() - start >= 120:
driver.refresh()
start = time.time()
# 延時(shí)1秒防止過于快速地點(diǎn)擊導(dǎo)致查詢超時(shí),當(dāng)然偶爾還是會(huì)出現(xiàn)超時(shí)現(xiàn)象橡娄,不過超時(shí)也沒關(guān)系诗箍,一般等待6秒之后就會(huì)繼續(xù)自動(dòng)查詢
time.sleep(1)
continue
# 獲取所有車票
tickets = driver.find_elements(by=By.XPATH, value='//*[@id="queryLeftTable"]/tr')
# 每張車票有兩個(gè)tr,但是第二個(gè)tr沒什么用
tickets = [tickets[i] for i in range(len(tickets) - 1) if i % 2 == 0]
#print(tickets)
for ticket in tickets:
# 如果車票的車次等于想要的車次并且硬臥的狀態(tài)不是候補(bǔ)則點(diǎn)擊預(yù)訂
#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 != "候補(bǔ)":
# 點(diǎn)擊預(yù)訂
#print(ticket.find_element(by=By.CLASS_NAME,value='cdz').text)
#time.sleep(1)
ticket.find_element(by=By.CLASS_NAME, value='btn72').click()
# 這里之后就不能繼續(xù)使用ticket.find_element()了扳还,因?yàn)轫撁孢M(jìn)行了跳轉(zhuǎn),會(huì)出現(xiàn)stale element reference: element is not attached to the page document的錯(cuò)誤
# 我們可以使用driver.find_element()
# 選擇乘車人橱夭,如果是學(xué)生氨距,則需要確認(rèn)購買學(xué)生票
driver.find_element(by=By.XPATH, value='//*[@id="normalPassenger_0"]').click()
# 點(diǎn)擊確認(rèn)購買學(xué)生票,如果不是學(xué)生棘劣,把這行注釋了就行
#driver.find_element(by=By.XPATH, value='//*[@id="dialog_xsertcj_ok"]').click()
# 第二個(gè)乘車人
# driver.find_element(by=By.XPATH, value='//*[@id="normalPassenger_1"]').click()
# 如果第二個(gè)乘車人也是學(xué)生俏让,則需要點(diǎn)擊確認(rèn)第二個(gè)人也購買學(xué)生票
# 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()
# 確認(rèn)提交訂單首昔,然后這里和上面是一樣的
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}次列車搶票成功,請(qǐng)盡快在10分鐘內(nèi)支付糙俗!")
return
if __name__ == '__main__':
# 有關(guān)車票的配置信息保存在該類里
# 請(qǐng)事先在config.py里填好相關(guān)信息
conf = Config()
url = 'https://www.12306.cn/index/'
# chromedriver.exe版本為104勒奇,可以根據(jù)自己瀏覽器版本重新下載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()
有問題歡迎留言~~~~