12306預售車票 時間是2019年的哈 (記筆記)
阿新 • • 發佈:2021-02-01
先說明一下哈,這個是從視訊上面我搬運的哈,視訊時間2019的,程式碼應該沒用
我只是單純的做筆記哈,寫一哈自己的感受哈
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver. support.select import Select
from selenium.common.exceptions import NoSuchElementException,ElementNotVisibleException #這是匯入一個異常哦
import csv
driver=webdriver.Chrome() #將driver 設定為全域性變數 是因為 如果放在裡面 driver會隨著 物件的銷燬 而 被銷燬
class TrainSpider(object):
login_url='登陸介面url' #定義在這裡 可以隨時 改變 url
presonal_url='這裡是登陸後跳轉的url 來判斷是否登陸成功'
left_ticket_url='車次餘票url'
confirm_passengner_url='確認乘客頁面的url'
def __init__(self,from_station,to_station,train_date,trains,passengers):
'''
:param from_station: 起始站
:param to_station: 目的站
:param train_date: 出發目標
:param trains: 需要購買車次 是一個字典 傳入例項:{'G529':['M','O'],'G403':{'M','O'}}
:param passengers: 乘客的姓名 是一個列表 傳入例項: ['乘客名','乘客名','乘客名',]
'''
self.from_station=from_station
self.to_station=to_station
self.train_date=train_date
self.trains=trains
self.passengers=passengers #將 傳過來的 變數 儲存在物件上
self.selected_seat=None
self.selected_number=None #自己定義類中全域性變數 定義了 車號 和 席位
#self.driver=webdriver.Chrome() 放在裡面可以方便 輸出 有聯想
self.station_codes={} #定義(在外面而非 函式裡裡面) 變數 因為 車票 查詢 需要
self.init_station_code() #初始化站點所對應的代號 一開始自動執行函式
def init_station_code(self): #這個是得到站點的代號 為了輸入車票的起始點
with open('z stations.csv', 'r', encoding='utf-8') as fp:
reader = csv.DictReader(fp)
for line in reader:
name = line['name']
code = line['code']
self.station_codes[name] = code
def login(self):
driver.get(self.login_url)
#等待url 是否變為個人中心的url 來判斷 是否登陸成功 (設定顯示等待)
WebDriverWait(driver,1000).until(
#判斷條件
#EC.url_to_be(self.presonal_url) 變成這個url
EC.url_contains(self.presonal_url) #包含這個url
)
print('登陸成功!')
def search_left_ticket(self):
driver.get(self.left_ticket_url)
#1.起始站代號設定
from_station_input = driver.find_element_by_id("fromStation")
from_station_code=self.station_codes[self.from_station]
'''self.station_codes[name] = code 因為這個 所以可以 返回 地點 代號'''
driver.execute_script(f"arguments[0].value='{from_station_code}'" , from_station_input) #設定值到框框
#這種josnscript 設定值 一切情況 都 適用
'''因為type 是 hidden 被隱藏 所以需要採用josnscript程式碼來實現
arguments 代表 你給函式 傳的引數 是一個列表
"arguments[0].value='%s'" % from_code 是 json程式碼
from_station_input 傳的引數 如果有其他的引數 都 放在 arguments中'''
#2.終點站代號設定
to_station_input = driver.find_element_by_id("toStation")
to_station_code = self.station_codes[self.to_station]
driver.execute_script(f"arguments[0].value='{to_station_code}'", to_station_input)
#3.設定時間
train_date_input = driver.find_element_by_id("train_date")
driver.execute_script(f'arguments[0].value={self.train_date}',train_date_input)
#4.執行查詢操作
search_btn=driver.find_element_by_id('query_ticket')
search_btn.click()
#5.解析車次資訊
WebDriverWait(driver,1000).until(
EC.presence_of_element_located((By.XPATH,"//tbody[@id='queryLeftTable']/tr"))
)
train_trs=driver.find_elements_by_xpath('//tbody[@id="queryLeftTable"]/tr[not(@datatran)]')
#tr[not(@datatran)] 可以排除 含有 datatran 條件的 tr
is_searched=False
while True: #這個 死迴圈 是為了多次查詢 因為有些票時間沒到 還在準備預售 之前不加這個 就只查詢一次就沒了
for train_tr in train_trs:
#print(train_tr.text)
infos=train_tr.text.repalce('\n',' ').split(' ')
#print(infos)
number=infos[0]
if number in self.trains: #判斷key(車號)是否在 字典 trains
seat_types=self.trains[number] #如果有key(車號) 獲得車號需要的 seat_types
for seat_type in seat_types: #遍歷這個車號的所有 資訊
#是否有二等座
if seat_type == 'O': #遍歷是否有 座位為二等座的 就可以進行下面的判斷二等座是否有
count=infos[9]
if count.isdigit() or count == '有':
is_searched=True
break
#是否有一等座
elif seat_type == 'M':
count=infos[8]
if count.isdigit() or count == '有':
is_searched=True
break
if is_searched:
self.selected_number=number
order_btn = train_tr.find_element_by_xpath('.//a[@class="btn72"]')
order_btn.click()
return
def confirm_passengers(self):
#1.判斷是否變為確認乘客的url頁面 和 等待 確認購買乘客資訊的出現
WebDriverWait(driver,1000).until(
EC.url_contains(self.confirm_passengner_url)
)
#先等待乘客標籤顯示出來
WebDriverWait(driver,1000).until(
EC.presence_of_element_located((By.XPATH,'//ul[@id="normal_passenger_id"]/li/label'))
)
#2.確認需要購買車票的乘客
passenger_labels=driver.find_elements_by_xpath('//ul[@id="normal_passenger_id"]/li/label')
for passenger_label in passenger_labels:
name=passenger_label.text
if name in self.passengers:
passenger_label.click()
#3.確認需要購買的席位資訊
seat_select=Select(driver.find_element_by_id('seatType_1'))
seat_types=self.trains[self.selected_number] #是一個列表
'''self.trains[self.selected_number] 因為傳過來的trains 是一個字典 所以可以通過key索引'''
for seat_type in seat_types:
try:
self.selected_seat =seat_type
seat_select.select_by_value(seat_type)
except NoSuchElementException: #捕捉異常
continue #continue 就是 下次迴圈下一個座次
else:
break #如果第一次就找到啦 就退出迴圈
#等待 提交訂單按鈕可以被點選
WebDriverWait(driver,1000).until(
EC.element_to_be_clickable((By.ID,'submintOrder_id'))
)
submit_btn=driver.find_element_by_id('submintOrder_id')
submit_btn.click()
#等待 模擬對話框出現 和 確認按鈕可以點選
WebDriverWait(driver,1000).until(
EC.presence_of_element_located((By.CLASS_NAME,'dhtmlx_window_active'))
)
#確認按鈕可以點選
WebDriverWait(driver,1000).until(
EC.element_to_be_clickable((By.ID,"qr_submit_id"))
)
submit_btn=driver.find_element_by_id("qr_submit_id")
#submit_btn.click() #有可能 點選一次並不會出來 所以採用下面的 暴力迴圈點選
while submit_btn:
try:
submit_btn.click()
submit_btn = driver.find_element_by_id("qr_submit_id")
except ElementNotVisibleException:
# 會報這個錯 ElementNotVisibleException 說明頁面已經換了 即代表成功了 就可以退出迴圈
break
print(f"恭喜!成功搶{self.selected_number}次列車{self.selected_seat}席位,請在30分鐘內完成付款!" )
def run(self): # 一切相關的就放在這 相當於總控制者
#1.登入 () 定義一個 函式一個
self.login()
#2.車次餘票查詢
self.search_left_ticket()
#3.確認乘客和車次資訊
self.confirm_passengers()
def main():
# 9:商務座,M:一等座,O:二等座,3:硬臥,4:軟臥,1:硬座
from_station=input('請輸入出發地:')
to_station=input('請輸入目的地:')
train_date=str(input('請輸入出發時間(請按照如下格式輸入時間: 2020-13-14):'))
train_number=input('你準備乘坐的車號(車號例項:G520):')
train_seat=input ("請輸入座位級別(輸入例項:m o):").split()
train={train_number:train_seat}
passenger_names=input ("輸入名字(輸入例項:小明 小紅):").split()
spider=TrainSpider(from_station,to_station,train_date,train,passenger_names)
spider.run()
if __name__ == '__main__':
main()
- 這裡對 類中的 引數的傳入和設定 有了新的 理解 之前沒麼怎麼使用過類
對引數的 傳入 和 設定
傳入 我自己的理解 就相當於 c語言中 對函式傳值一樣那種 只不過在類中使用 需要初始化(self.from_station=from_station
我也不知道這個是不是叫初始化)
設定嘛 就是平時一樣的 就使用的時候 加個self
def __init__(self,from_station,to_station,train_date,trains,passengers):
'''
:param from_station: 起始站
:param to_station: 目的站
:param train_date: 出發目標
:param trains: 需要購買車次 是一個字典 傳入例項:{'G529':['M','O'],'G403':{'M','O'}}
:param passengers: 乘客的姓名 是一個列表 傳入例項: ['乘客名','乘客名','乘客名',]
'''
self.from_station=from_station
self.to_station=to_station
self.train_date=train_date
self.trains=trains
self.passengers=passengers #將 傳過來的 變數 儲存在物件上
self.selected_seat=None
self.selected_number=None #自己定義類中全域性變數 定義了 車號 和 席位
#self.driver=webdriver.Chrome() 放在裡面可以方便 輸出 有聯想
self.station_codes={} #定義(在外面而非 函式裡裡面) 變數 因為 車票 查詢 需要
self.init_station_code() #初始化站點所對應的代號 一開始自動執行函式
對於 driver.execute_script(f"arguments[0].value='{from_station_code}'" , from_station_input)
這個認識
driver.execute_script(f"arguments[0].value='{from_station_code}'" , from_station_input) #設定值到框框
#這種josnscript 設定值 一切情況 都 適用
'''因為type 是 hidden 被隱藏 所以需要採用josnscript程式碼來實現
arguments 代表 你給函式 傳的引數 是一個列表
"arguments[0].value='%s'" % from_code 是 json程式碼
from_station_input 傳的引數 如果有其他的引數 都 放在 arguments中'''
3.以及 處處都需要顯示等待的條件 思維
#1.判斷是否變為確認乘客的url頁面 和 等待 確認購買乘客資訊的出現
WebDriverWait(driver,1000).until(
EC.url_contains(self.confirm_passengner_url)
)
#先等待乘客標籤顯示出來
WebDriverWait(driver,1000).until(
EC.presence_of_element_located((By.XPATH,'//ul[@id="normal_passenger_id"]/li/label'))
)
4.以及輸出列表的方式
passenger_names=input ("輸入名字(輸入例項:小明 小紅):").split()