Python爬虫实战——12306火车购票(附实例)

春运到了,你还在手动买火车票吗?

1、什么是selenium

在想要看懂代码之前一定要问问自己,啥是个selenium?它能干啥?为啥要用它?

答: selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。selenium 是一套完整的web应用程序测试系统,包含了测试的录制(selenium IDE),编写及运行(Selenium Remote Control)和测试的并行处理(Selenium Grid)。Selenium的核心Selenium Core基于JsUnit,完全由JavaScript编写,因此可以用于任何支持JavaScript的浏览器上。selenium可以模拟真实浏览器,自动化测试工具,支持多种浏览器,爬虫中主要用来解决JavaScript渲染问题。

2、selenium的常见库及其使用

在这里插入图片描述

我们可以观察到,selenium支持多种浏览器,本文中我将主要使用webdriver.Chrome进行自动化测试

小案例

这里构造一个简单的小案例方便你更好的了解它

from selenium import webdriver  # 导入库

browser = webdriver.Chrome()  # 声明浏览器
url = 'https:www.baidu.com'
browser.get(url)  # 打开浏览器预设网址
print(browser.page_source)  # 打印网页源代码
browser.close()  # 关闭浏览器

注: 如果想要进行一点完善的话可以添加time模块

查找

selenium最方便的地方在于,你可以通过多种方法来定位页面信息,这里我列举一些常用方法:

find_element_by_name
find_element_by_id
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector

至于每一种的效果如何或者是使用情况,请依照个人需求灵活选择。

获取文本值

browser.get("http://www.zhihu.com/explore")
logo = browser.find_element_by_css_selector('.zu-top-link-logo')
print(logo)
print(logo.text)

运行代码我们可以得到标签下的文本值:

<selenium.webdriver.remote.webelement.WebElement (session="ce8814d69f8e1291c88ce6f76b6050a2", element="0.9868611170776878-1")>
知乎

模拟点击

这里内容比较多,我们可以通过selenium下的 ActionChains 类,由于篇幅过长这里只截取部分

class ActionChains(object):
    """
    ActionChains are a way to automate low level interactions such as
    mouse movements, mouse button actions, key press, and context menu interactions.
    This is useful for doing more complex actions like hover over and drag and drop.
 
    Generate user actions.
       When you call methods for actions on the ActionChains object,
       the actions are stored in a queue in the ActionChains object.
       When you call perform(), the events are fired in the order they
       are queued up.
 
    ActionChains can be used in a chain pattern::
 
        menu = driver.find_element_by_css_selector(".nav")
        hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")
 
        ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform()
 
    Or actions can be queued up one by one, then performed.::
 
        menu = driver.find_element_by_css_selector(".nav")
        hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")
 
        actions = ActionChains(driver)
        actions.move_to_element(menu)
        actions.click(hidden_submenu)
        actions.perform()
 
    Either way, the actions are performed in the order they are called, one after
    another.
    """
 
    def __init__(self, driver):
        """
        Creates a new ActionChains.
 
        :Args:
         - driver: The WebDriver instance which performs user actions.
        """
        self._driver = driver
        self._actions = []
        if self._driver.w3c:
            self.w3c_actions = ActionBuilder(driver)
 
    def perform(self):
        """
        Performs all stored actions.
        """
        if self._driver.w3c:
            self.w3c_actions.perform()
        else:
            for action in self._actions:
                action()
 
    def reset_actions(self):
        """
            Clears actions that are already stored on the remote end.
        """
        if self._driver.w3c:
            self._driver.execute(Command.W3C_CLEAR_ACTIONS)
        else:
            self._actions = []
            
      ....

3、操作实战

在实现登录过程中,首先需要逐步解析页面,通过xpath工具、页面审查等方式定位到相应的标签下,通过模拟点击、输入文本等方式进行信息输入、表单选择等过程。

在这里插入图片描述

例如我们从上图中,定位到了下拉表单中的始发地/目的地信息,只有这样我们才可以对其中的数据进行修改,直到达到我们所预期的效果

测试登录过程

美中不足的是,12306特有的图片验证码还未找到合适的方法实现自动破解,只能通过人工识别完成这一步骤。

在这里插入图片描述

至此我们的测试就已经结束,可以看到效果还是很不错的,避免了反复通过输入密码、选择站点等操作,极大程度的减少了不必要的操作!只需将常用的站点xpath路径保存好即可。

4、代码上场

下面附赠本次实验中的部分代码,希望能给各位读者一些帮助!

from selenium import webdriver
import time


def main():
    login_text()
    search_ticket()
    book_ticket()


def login_text():
    d.get(url)
    d.implicitly_wait(30)
    # 登陆账号
    username_ele = d.find_element_by_id('username')
    username_ele.clear()
    username_ele.send_keys(username)
    # 登陆密码
    pwd_ele = d.find_element_by_id('password')
    pwd_ele.clear()
    pwd_ele.send_keys(pwd)

    while True:  # 手动进行图片验证,并登录
        curpage_url = d.current_url
        if curpage_url != url:
            if curpage_url[:-1] != url:
                print('.......登陆成功........')
                break
        else:
            time.sleep(3)
            print('------细心点老铁!慢慢来!-----')


def search_ticket():
    time.sleep(2)
    d.find_element_by_link_text('车票').click()
    d.find_element_by_link_text('单程').click()
    time.sleep(3)

    # 输入出发地
    d.add_cookie({"name": "_jc_save_fromStation", "value": '%u547C%u548C%u6D69%u7279%2CHHC'})  # 鄂尔多斯

    # 选择目的地
    d.add_cookie({"name": "_jc_save_toStation", "value": '%u9102%u5C14%u591A%u65AF%2CEEC'})  # 呼和浩特

    # 选择出发日期
    d.add_cookie({"name": "_jc_save_fromDate", "value": '2019-11-06'})
    d.refresh()


def book_ticket():
    query_time = 0
    time_begin = time.time()
    # 循环查询
    while True:
        time.sleep(1)
        search_btn = d.find_element_by_link_text('查询')
        search_btn.click()

        # 扫描查询结果
        try:
            d.implicitly_wait(3)
            # 鄂尔多斯到呼和浩特的D6963的PATH:(//*[@id="ZE_33000D696300"])
            ticket_ele = d.find_element_by_xpath('//*[@id="ZE_33000D696300"]')  # 所抢车次对应的座位等级的xpath,这里是二等座
            ticket_info = ticket_ele.text
        except:
            search_btn.click()
            d.implicitly_wait(3)
            ticket_ele = d.find_element_by_xpath('//*[@id="ZE_33000D696300"]')
            ticket_info = ticket_ele.text
            print('可能您的xpath选择错误')

        if ticket_info == '无' or ticket_info == '*':
            query_time += 1
            cur_time = time.time()
            print('第%d次查询,用时%s秒' % (query_time, cur_time - time_begin))
        else:
            d.find_element_by_xpath('//*[@id="ticket_33000D696300"]/td[13]/a').click()
            break

    cust_url = 'https://kyfw.12306.cn/otn/confirmPassenger/initDc'
    while True:
        if (d.current_url == cust_url):
            print('页面跳至选择乘客信息 成功')
            break
        else:
            time.sleep(1)
            print('等待页面跳转')

    while True:
        try:
            time.sleep(2)
            d.find_element_by_xpath('//*[@id="normalPassenger_0"]').click()  # _0是联系人列表里的第一个 ,依此类推
            break
        except:
            print('等待常用联系人列表')

    # 提交订单
    d.find_element_by_xpath('//*[@id="submitOrder_id"]').click()

    # 确认订票信息
    while True:
        try:
            d.switch_to.frame(d.find_element_by_xpath('/html/body/iframe[2]'))

            d.find_element_by_xpath('//*[@id="qr_submit_id"]')
            print('pass')
        except:
            print('请手动选座和点击确认信息')
            time.sleep(5)
            print('请完成购票,再见。')
            break


if __name__ == '__main__':
    d = webdriver.Chrome()
    url = 'https://kyfw.12306.cn/otn/login/init'
    username = '这里是你的用户名'
    pwd = '这里是你的密码'
    main()

★希望各位读者可以学到一些什么,而不是单纯地复制粘贴。Python的魅力在于,你可以使用它实现很多有趣而实用的案例,如有不足或错误之处还请各位读者指出,谢谢!

  • 13
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
#这只是一个半成品,只是实现了,一个完整的订票过程,对于,如果刷票,自己研究 #简单过程 # 第一、getRandAndCookie() 获得cook 和一个随机数用于登录 # 第二、getEnterRandCode() 得到登录时的识别码 # 第三、setuseandpassword(randcode,use,password) 发送随机数、识别码和用户及密码。由于随机数只在内部使用,所以定义成了全局变量, # 第四、GetTrainList() 得到所有车站列表,'@bjb|北京北|VAP|beijingbei|bjb|0' 其中有中文、拼音、拼音缩写、所一个ID(唯一),其主要是可以,通过上面的列表,找到它的唯一ID,TranCityToId('南昌') # 第五、GetTrainNumList(date,fromstationid,tostationid,starttime) 得到哪到哪的所在车次,消息格式如下,其中所以,一下车次的的ID:"id":"650000K1060I" # {"end_station_name":"北京西","end_time":"16:18","id":"650000K1060I","start_station_name":"深圳","start_time":"10:54","value":"K106"} # 通过ChangeToTrainNumId('K106')得到车次ID # 第六、QueryTrain(fromstationid,tostationid,date,stationNum,starttime) 就是点击查询按键,得到是否有能预订,格式如下 #       南昌         20:12,    北京西        07:38,11:26,--,--,--,--,10,有,有,--,有,有,--,<a name='btn130_2' class='btn130_2' # 通过choiceSubmitNum(stationNum,trainsubmitinfo)提取出getSelected()消息 # 第七、submitRequest(choiceSubmitNum(stationNum,trainsubmitinfo),date,starttime) 就是点击预订按钮 # 第八、getrandCheckCode()得到提交订单的识别码 # 第十、CheckInMyTicket(info,randcode,peoples)点击提交,如果成功的话,就会返回{"errMsg":"Y"} # 出于,网络是UTF8格式,所以,必须# -*- coding: utf-8 -*-,(当然,自己转换也是可以的) # 出于这一个控制台信息,所以,识别码的图片在脚本同一目录 #得到头信息
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值