先说明一下哈,这个是从视频上面我搬运的哈,视频时间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()