让我们先解释一下。这是我从视频的。视频时间是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()