爬虫(12,13)selenium练习 12306案例

本文详细介绍了使用selenium进行12306登录、车次及余票查询、预定车次、确认乘客和车次信息、提交订单的全过程,通过面向对象编程实现自动化购票。在实践中遇到的显示等待、元素定位等问题逐一解决,最终成功生成订单。
摘要由CSDN通过智能技术生成

第十二、十三章 selenium练习12306案例

0. 前言

这个案例旨在练习selenium方法,以及面向对象编程的代码敲打。本案例的功能是(按顺序罗列):

  • 打开12306登录界面
  • 窗口最大化 (登录需要自己扫码,后续可以尝试获得cookie)
  • 跳转个人中心界面
  • 填写出发地
  • 填写目的地
  • 填写出发日期
  • 跳转到车次及余票查询页面
  • 点击通告窗口确定按钮
  • 点击查询按钮
  • 查询我们想要的车次一等座二等座是否有票
  • 点击预定按钮
  • 跳转购票确认页面
  • 勾选购票人
  • 选择座位类别
  • 点击提交按钮
  • 跳转到核对信息窗口
  • 点击确认购买按钮
  • 系统生成订单
  • 自行操作支付购买
  • 购票成功
    这里许多点击动作selenium的操作不响应,有的需要设置显示等待。老师的最后提交按钮是用循环不断点击,我是用execut_script()方法代替,亲测该方法屡试不爽。就是没有不成功的。
    后面可以丰富该案例的功能,使它更实用。比如可以在程序执行后跳出交互界面,输入想要查询的车次信息,输入出发地,目的地,出发日期就可以打印出对应的车次信息,自己可以查看打印出的车次信息然后决定购买哪一趟,交互界面点击输入车次信息,输入回车后,程序继续运行后面的代码,然后交互让你填写购票人姓名,然后就是自动帮你生成订单。你只需要手机扫码支付就可以了。有时间可以整整。

1. 登录的实现

我们这一步先研究登录网站。我们用面向对象编程,这一步我们实现的目标是,定义项目框架,执行程序后,登录网站,并且提示已经登录成功。注意看代码中的注释:

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait # 条件等待需要用
from selenium.webdriver.support import expected_conditions as EC  # 设置等待条件时要用到
driver = webdriver.Chrome() # 类放在全局里面,避免类调用后销毁的时候,连同驱动一同销毁。
class TrainSpider():

    login_url = 'https://kyfw.12306.cn/otn/resources/login.html' # 登录界面
    personal_url = 'https://kyfw.12306.cn/otn/view/index.html' # 登陆后个人界面

    def __init__(self, from_station, to_station, train_data):  
        self.from_station = from_station
        self.to_station = to_station
        self.train_data = train_data

    def login(self):
        driver.get(self.login_url)   
        driver.maximize_window() # 窗口最大化
        WebDriverWait(driver,300).until(		# 这一行传入的第一个是驱动,第二个是等待时间
            EC.url_contains(self.personal_url)	 #  这一行传入的是包含跟人界面的url条件,条件满足就不再继续等待
        )
        print('已经登录成功')

    def run(self):  # 用来封装项目的基本功能,比如买票,只要调用这个方法就可以实现相应功能
        # 登录
        self.login()

def main():  # 用来调用各个方法
    spider = TrainSpider('西安', '郑州', '2021-01-30')  # 实例化类
    spider.run()

if __name__ == '__main__':  # 主入口,调用主函数开始执行
    main()   

程序执行后,先跳出第一个页面,登录界面。用app扫码登录后,跳出个人界面。然后程序执行结束,打印出登录成功提醒。

执行结果:
在这里插入图片描述
注意:

  • 不要把驱动写入类里面,因为类调用后就会销毁,而驱动也会随着类销毁而失去,导致浏览器打开后又迅速消失。我们要放在全局变量里面。
  • 登录时,我们设置了显示等待,条件是个人中心的界面url。

2. 车次及余票查询

第二步就到了一个重点了,是车次和余票的查询。我们登录后,就到了一个“单程”的界面了,这个界面对应得地址是:

https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc

下面我要做一个逻辑了,是车次与余票查询的逻辑。我们定义一个方法,叫“leftTicket”,在登录的方法下面。并且在run方法里面调用一下这个方法:

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait # 条件等待需要用
from selenium.webdriver.support import expected_conditions as EC  # 设置等待条件时要用到
driver = webdriver.Chrome() # 类放在全局里面,避免类调用后销毁的时候,连同驱动一同销毁。
class TrainSpider():

    login_url = 'https://kyfw.12306.cn/otn/resources/login.html' # 登录界面
    personal_url = 'https://kyfw.12306.cn/otn/view/index.html' # 登陆后个人界面
    left_ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc'  # 车次余票的url

    def __init__(self, from_station, to_station, train_data):
        self.from_station = from_station
        self.to_station = to_station
        self.train_data = train_data

    def login(self):
        driver.get(self.login_url)
        driver.maximize_window() # 窗口最大化
        WebDriverWait(driver,300).until(
            EC.url_contains(self.personal_url)
        )
        print('已经登录成功')

    def search_left_ticket(self):
        pass

    def run(self):  # 用来封装项目的基本功能,比如买票,只要调用这个方法就可以实现相应功能
        # 1. 登录
        self.login()
        # 2. 车次以及余票查询
        self.search_left_ticket()

def main():  # 用来调用各个方法
    spider = TrainSpider('西安', '郑州', '2021-01-30')
    spider.run()

if __name__ == '__main__':
    main()

里面的逻辑怎么写呢?我们需要打开车次以及余票的页面,所以要把这个页面的url放在上面。

left_ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc'  # 车次余票的url

余票查询后,我们需要跳转到买票的界面。跳转之前,我们需要填写“出发地”,“目的地”,“出发日期”等信息。上次课我们是手动填写的,这一次我们用代码来解决。
我们右键检查一下网页源码:
在这里插入图片描述
我们发现有两个input标签,第一个input标签的type是hidden隐藏的意思。说明出发地和目的地的获取并不是通过文本,而是通过value值,就是城市的代号得到的。如果你通过Send_keys(‘长沙’)是没有用的。所以,你需要获取全国车站对应的代号,通过这个代号来传递车站信息。我已经准备好了一个csv文件,专门存储车站信息的,如图:
在这里插入图片描述

2.1 车站信息读取

接下来我们要做的事是把这些车站的信息读取出来,然后用于后面的车次查询操作。我们回顾一下读取文件的步骤:

import csv
with open('stations.csv','r',encoding='utf-8') as f:
    reader = csv.DictReader(f)
    info_dict = {}
    info_lst = []
    for line in reader:
        name = line['name']
        code = line['code']
        print('name:{},code:{}'.format(name,code))

打印结果

name:太原,code:TYV
name:武汉,code:WHN
name:王家营西,code:KNM
name:乌鲁木齐,code:WAR
name:西安北,code:EAY
name:西安,code:XAY
name:西安南,code:CAY
name:西宁,code:XNO
name:银川,code:YIJ
name:郑州,code:ZZF
name:阿尔山,code:ART
... ...

太占篇幅,后面的省略了。

2.2 车站信息添加方法

我们可以定义一个方法,读取文件,把读取到的信息新键一个字典,把车站名作为键,车站代号作为值,这样我们调用的时候就方便多了。我们把这个逻辑写入代码,注意看注释:

import csv
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait # 条件等待需要用
from selenium.webdriver.support import expected_conditions as EC  # 设置等待条件时要用到
driver = webdriver.Chrome() # 类放在全局里面,避免类调用后销毁的时候,连同驱动一同销毁。
class TrainSpider():

    login_url = 'https://kyfw.12306.cn/otn/resources/login.html' # 登录界面
    personal_url = 'https://kyfw.12306.cn/otn/view/index.html' # 登陆后个人界面
    left_ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc'  # 车次余票的url

    def __init__(self, from_station, to_station, train_data):
        self.from_station = from_station
        self.to_station = to_station
        self.train_data = train_data
        self.init_station_code()  # 在初始化方法里面就调用车站信息读取方法,这样当这个类一旦初始化完成的时候,所有的站点也都初始化好待用了

        self.station_codes_dict = {} # 把车站信息字典放在初始化方法里,方便调用
    def init_station_code(self):    # 读取stations.csv文件,并新键字典,把车站名作为建,车站代号作为值
        # station_codes_dict = {}  # 假如我们把这个字典放在这里,那么其他方法相使用就使用不了,所以我们可以把它放到初始化方法里。
        with open('stations.csv', 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for line in reader:
                name = line['name']
                code = line['code']
                self.station_codes_dict[name] = code  # 这里我们只需要调用一下初始化方法里的字典

    def login(self):
        driver.get(self.login_url)
        driver.maximize_window() # 窗口最大化
        WebDriverWait(driver,300).until(
            EC.url_contains(self.personal_url)
        )
        print('已经登录成功')

    def search_left_ticket(self):
        pass

    def run(self):  # 用来封装项目的基本功能,比如买票,只要调用这个方法就可以实现相应功能
        # 1. 登录
        self.login()
        # 2. 车次以及余票查询
        self.search_left_ticket()

def main():  # 用来调用各个方法
    spider = TrainSpider('西安', '郑州', '2021-01-30')
    spider.run()

if __name__ == '__main__':
    main()

这段代码需要说明的是:

  • 车站信息读取方法我们在初始化方法里面直接调用了,这样当这个类初始化时,车站信息就被读取好了,方便使用。
  • 车站信息读取方法的最后一步是把读取的内容添加到一个空字典里,这个空字典我们放在了初始化方法里面,这样的其他的方法也可以调用。而同样的,车站信息读取方法要用也需要
    "self.station_codes_dict[name] = code"这样调用。

2.3 车站信息导入输入框

我们怎样把这个车站信息填入网站上的出发站和到达站输入框呢,因为这里的input标签是隐藏的类型。所以我们并不能直接用selenium的send_keys方法去操作。这里需要selenium提供的一个叫着"execute_script()"方法,这个方法的主要作用是它可以调用一些JavaScript()方法的操作,例如拖动网页窗口的滚动条这样的操作,在selenium里面并没有提供这样的方法,但是提供了execute_script(),可以调用JavaScript()里的相关操作方法来实现。

execute_script()方法可以调用JavaScript()方法

下面看代码,注意看注释:

import csv
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait # 条件等待需要用
from selenium.webdriver.support import expected_conditions as EC  # 设置等待条件时要用到
driver = webdriver.Chrome() # 类放在全局里面,避免类调用后销毁的时候,连同驱动一同销毁。
class TrainSpider():

    login_url = 'https://kyfw.12306.cn/otn/resources/login.html' # 登录界面
    personal_url = 'https://kyfw.12306.cn/otn/view/index.html' # 登陆后个人界面
    left_ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc'  # 车次余票的url

    def __init__(self, from_station, to_station, train_data):
        self.from_station = from_station
        self.to_station = to_station
        self.train_data = train_data
        self.init_station_code()  # 在初始化方法里面就调用车站信息读取方法,这样当这个类一旦初始化完成的时候,所有的站点也都初始化好待用了

        self.station_codes_dict = {} # 把车站信息字典放在初始化方法里,方便调用
    def init_station_code(self):    # 读取stations.csv文件,并新键字典,把车站名作为建,车站代号作为值
        # station_codes_dict = {}  # 假如我们把这个字典放在这里,那么其他方法相使用就使用不了,所以我们可以把它放到初始化方法里。
        with open('stations.csv', 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            info_dict = {}
            info_lst = []
            for line in reader:
                name = line['name']
                code = line['code']
                self.station_codes_dict[name] = code  # 这里我们只需要调用一下初始化方法里的字典

    def login(self):
        driver.get(self.login_url)
        driver.maximize_window() # 窗口最大化
        WebDriverWait(driver,300).until(
            EC.url_contains(self.personal_url)
        )
        print('已经登录成功')

    def search_left_ticket(self):
        driver.get(self.left_ticket_url)
        # 出发地
        from_station_input = driver.find_element_by_id('fromStation')  # 找到出发地输入框元素
        from_station_code = self.station_codes_dict[self.from_station]  # 从字典中提取车站代号
        driver.execute_script('arguments[0].value="%s"'%from_station_code,from_station_input) # 把实例出发地传入输入框
        # 上面这行代码的解释:"arguments[0].value"这是Java里的占位符,"%s"这是python里的占位符
        # %from_station_code,from_station_input这句意思是用后面的from_station_input被前面的from_station_code代替
        # 目的地
        to_station_input = driver.find_element_by_id('toStation')  # 找到目的地输入框元素
        to_station_code = self.station_codes_dict[self.to_station]  # 从字典中提取车站代号
        driver.execute_script('arguments[0].value="%s"' % to_station_code, to_station_input) # 把实例目的地传入输入框

    def run(self):  # 用来封装项目的基本功能,比如买票,只要调用这个方法就可以实现相应功能
        # 1. 登录
        self.login()
        # 2. 车次以及余票查询
        self.search_left_ticket()

def main():  # 用来调用各个方法
    spider = TrainSpider('西安', '郑州', '2021-01-30')
    spider.run()

if __name__ == '__main__':
    main()

注意,这里执行后,虽然输入框里面并没有显示我们输入的城市名西安,但是实际上我们的代号“XAY”已经成功传入,可以右键查看。在查看前千万不要用鼠标点击输入框,因为点击有清除输入框内容的功能,点击后,你再右键查看,会发现value值是空的。但尽管如此,我们的车站代号还是成功传入了的。
在这里插入图片描述

2.4 出发日期与查询按钮

下面我们定义日期和点击查询按钮的代码:

import csv
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait # 条件等待需要用
from selenium.webdriver.support import expected_conditions as EC  # 设置等待条件时要用到
driver = webdriver.Chrome() # 类放在全局里面,避免类调用后销毁的时候,连同驱动一同销毁。
class TrainSpider():

    login_url = 'https://kyfw.12306.cn/otn/resources/login.html' # 登录界面
    personal_url = 'https://kyfw.12306.cn/otn/view/index.html' # 登陆后个人界面
    left_ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc'  # 车次余票的url

    def __init__(self, from_station, to_station, train_data):
        self.from_station = from_station
        self.to_station = to_station
        self.train_data = train_data
        self.init_station_code()  # 在初始化方法里面就调用车站信息读取方法,这样当这个类一旦初始化完成的时候,所有的站点也都初始化好待用了

        self.station_codes_dict = {} # 把车站信息字典放在初始化方法里,方便调用
    def init_station_code(self):    # 读取stations.csv文件,并新键字典,把车站名作为建,车站代号作为值
        # station_codes_dict = {}  # 假如我们把这个字典放在这里,那么其他方法相使用就使用不了,所以我们可以把它放到初始化方法里。
        with open('stations.csv', 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            info_dict = {}
            info_lst = []
            for line in reader:
                name = line['name']
                code = line['code']
                self.station_codes_dict[name] = code  # 这里我们只需要调用一下初始化方法里的字典

    def login(self):
        driver.get(self.login_url)
        driver.maximize_window() # 窗口最大化
        WebDriverWait(driver,300).until(
            EC.url_contains(self.personal_url)
        )
        print('已经登录成功')

    def search_left_ticket(self):
        driver.get(self.left_ticket_url
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值