• 自动化抢票 12306


    注意!!!代码仅供学习和参考,不要用做非法用途!!!

    自动化抢票 12306

    1. 明确需求

    明确采集的网站以及数据内容

    • 网址: https://kyfw.12306.cn/otn/leftTicket/init
    • 数据: 车次相关信息
    2. 抓包分析

    通过浏览器开发者工具分析对应的数据位置

    • 打开开发者工具
      • F12 或鼠标右键点击检查
    • 刷新网页
      • 点击下一页/下滑网页页面/点击搜索/查询按钮
      • 让网页相关数据内容加载出来 (整个网站数据内容重新加载一遍)
    • 通过关键字搜索找到对应数据位置
      • 需要什么数据就搜什么

    数据包地址: https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2024-09-06&leftTicketDTO.from_station=IZQ&leftTicketDTO.to_station=SNQ&purpose_codes=ADULT

    3. 代码实现步骤
    1. 发送请求

    模拟浏览器对于 url 地址发送请求

    • 模拟浏览器

      • 可以直接复制,使用请求标头中参数内容
      • 去哪里找: 开发者工具 -> 网络 -> 点击对应数据包 -> 标头 -> 请求标头(参数)
      • 怎么写: 使用字典接受数据内容 (构建完整的键值对)
    • 请求网址

      • 通过抓包分析找到链接地址,直接复制即可
    • 发送请求

      • 使用第三方模块: requests
        • 安装 requests 模块
          • win+r 输入 cmd 点击确定,输入安装命令: pip install requests
        • 导入 requests 模块
        • 请求方法: 开发者工具 -> 网络 -> 点击对应数据包 -> 标头 -> 常规
        • GET 请求参数: 查询参数 (直接在链接中就有)
    2. 获取数据

    获取服务器返回响应数据

    • 12306 的请求参数并不是简单的中文字符,而是对应的三字编码,我们需要找到对应的编码
      • 对网页分析发现,在一个 js 文件中可以获取
      • 在页面最后有 https://kyfw.12306.cn/otn/resources/js/framework/station_name.js 链接
    3. 解析数据

    提取我们需要的数据内容: 车次相关内容

    4. 保存数据
    • 字典取值
      • 键值对取值: 根据冒号左边的内容 [“键”, 提取冒号右边的内容 [“值”]]

    当然,下面我将重点介绍每个步骤的关键点,并附上相应的代码片段。

    1. 获取站点编码

    关键点:从 12306 的 JS 文件中解析站点的三字码。

    代码片段:

    def get_station_codes():
        code_url = "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js"
        response = requests.get(code_url)
        code_data = response.text[20:-2]  # 优化:去除尾部的双引号和换行符
        list_code = code_data.split("|")
        station_codes = dict(zip(list_code[1::5], list_code[2::5]))  # 优化:直接跳过索引获取站点名称和代码
        return station_codes
    

    2. 用户输入

    关键点:提示用户输入起始站、终点站和出发日期,然后转换为 12306 需要的编码。

    代码片段:

    def get_user_input(code_dic):
        from_station = input("输入起始站:\n")
        to_station = input("输入终点站:\n")
        time = input("输入时间,例如:2024-09-18:\n")
        return code_dic.get(from_station, ""), code_dic.get(to_station, ""), time
    

    3. 获取火车票信息

    关键点:构建请求 URL,模拟浏览器发送 HTTP 请求获取数据。

    代码片段:

    def get_train_info(from_station, to_station, time):
        train_url = f"https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={time}&leftTicketDTO.from_station={from_station}&leftTicketDTO.to_station={to_station}&purpose_codes=ADULT"
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
        }
        response = requests.get(url=train_url, headers=headers)
        return response.json()
    

    4. 打印火车票信息

    关键点:解析服务器返回的 JSON 数据,并使用PrettyTable格式化输出。

    代码片段:

    def print_train_info(json_data):
        table = PrettyTable()
        table.field_names = ['车次', '出发时间', '到达时间', '历时', '一等座', '二等座', '特等座']
        if json_data['httpstatus'] == 200:
            result = json_data['data']['result']
            for item in result:
                details = item.split('|')
                table.add_row([details[3], details[8], details[9], details[10], details[30], details[31], details[32]])
            print(table)
        else:
            print(f"获取响应数据失败,状态码为{json_data['httpstatus']}")
    

    5. Selenium 自动化

    关键点:使用 Selenium 模拟用户在网页上的操作,如填写表单、点击按钮等。

    代码片段:

    def main():
        # 获取站点编码
        code_dic = get_station_codes()
    
        # 获取用户输入
        from_station_code, to_station_code, time = get_user_input(code_dic)
    
        # 使用Selenium打开网页
        browser = webdriver.Edge()
        browser.get('https://kyfw.12306.cn/otn/leftTicket/init')
    
        # 填写查询表单
        start_station = browser.find_element(By.CSS_SELECTOR, '#fromStationText')
        start_station.send_keys("广州南")
        start_station.send_keys(Keys.ENTER)
    
        end_station = browser.find_element(By.CSS_SELECTOR, '#toStationText')
        end_station.send_keys("韶关")
        end_station.send_keys(Keys.ENTER)
    
        date = browser.find_element(By.CSS_SELECTOR, '#train_date')
        date.send_keys("2024-09-18")
        date.send_keys(Keys.ENTER)
    
        # 点击查询
        browser.find_element(By.CSS_SELECTOR, '#query_ticket').click()
    
        # 等待查询结果
        t.sleep(5)  # 优化:使用更明确的等待条件
    
        # 处理查询结果
        # 省略:根据实际情况处理查询结果
    
        # 关闭浏览器
        browser.quit()
    

    6. 完整代码

    # coding=gbk
    import time as t
    import requests
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    from prettytable import PrettyTable
    import re
    
    # 获取站点对应的三字码
    def get_station_codes():
        code_url = "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js"
        response = requests.get(code_url)
        code_data = response.text[20:-2]  # 优化:去除尾部的双引号和换行符
        list_code = code_data.split("|")
        station_codes = dict(zip(list_code[1::5], list_code[2::5]))  # 优化:直接跳过索引获取站点名称和代码
        return station_codes
    
    # 用户输入起始站、终点站和时间,转化为编码
    def get_user_input(code_dic):
        from_station = input("输入起始站:\n")
        to_station = input("输入终点站:\n")
        time = input("输入时间,例如:2024-09-18:\n")
        return code_dic.get(from_station, ""), code_dic.get(to_station, ""), time
    
    # 获取火车票信息
    def get_train_info(from_station, to_station, time):
        train_url = f"https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={time}&leftTicketDTO.from_station={from_station}&leftTicketDTO.to_station={to_station}&purpose_codes=ADULT"
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
        }
        response = requests.get(url=train_url, headers=headers)
        return response.json()
    
    # 打印火车票信息
    def print_train_info(json_data):
        table = PrettyTable()
        table.field_names = ['车次', '出发时间', '到达时间', '历时', '一等座', '二等座', '特等座']
        if json_data['httpstatus'] == 200:
            result = json_data['data']['result']
            for item in result:
                details = item.split('|')
                table.add_row([details[3], details[8], details[9], details[10], details[30], details[31], details[32]])
            print(table)
        else:
            print(f"获取响应数据失败,状态码为{json_data['httpstatus']}")
    
    # 主函数
    def main():
        # 获取站点编码
        code_dic = get_station_codes()
    
        # 获取用户输入
        from_station, to_station, time = get_user_input(code_dic)
    
        # 获取火车票信息
        json_data = get_train_info(from_station, to_station, time)
    
        # 打印火车票信息
        print_train_info(json_data)
    
        # 使用Selenium打开网页
        browser = webdriver.Edge()
        browser.get('https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc')
    
        # 填写查询表单
        start_station = browser.find_element(By.CSS_SELECTOR, '#fromStationText')
        start_station.clear()
        start_station.send_keys("广州南")
        start_station.send_keys(Keys.ENTER)
    
        end_station = browser.find_element(By.CSS_SELECTOR, '#toStationText')
        end_station.clear()
        end_station.send_keys("韶关")
        end_station.send_keys(Keys.ENTER)
    
        date = browser.find_element(By.CSS_SELECTOR, '#train_date')
        date.clear()
        date.send_keys("2024-09-18")
        date.send_keys(Keys.ENTER)
    
        # 点击查询
        browser.find_element(By.CSS_SELECTOR, '#query_ticket').click()
    
        # 等待查询结果
        t.sleep(5)  # 优化:使用更明确的等待条件
    
        # 处理查询结果
        elements = browser.find_elements(By.CSS_SELECTOR, '#queryLeftTable tr:nth-child(7) .btn72')
        if elements:
            elements[0].click()
    
        # 扫码登陆
        saoma = browser.find_element(By.CSS_SELECTOR, '#login > div.login-box > ul > li.login-hd-account > a')
        saoma.click()
    
        t.sleep(5)  # 优化:使用更明确的等待条件
    
        # 登录操作
        J_userName = browser.find_element(By.CSS_SELECTOR, '#J-userName')
        J_userName.clear()
        J_userName.send_keys("aaa")
    
        J_password = browser.find_element(By.CSS_SELECTOR, '#J-password')
        J_password.clear()
        J_password.send_keys("password")
    
        J_loginmodalBtn = browser.find_element(By.CSS_SELECTOR, '#J-login')
        J_loginmodalBtn.click()
    
        # 点击预订
        browser.find_element(By.CSS_SELECTOR, '#normalPassenger_0').click()
        t.sleep(1)
        browser.find_element(By.CSS_SELECTOR, '#dialog_xsertcj_cancel').click()
    
        # 修改成人票
        ticket_type_select = browser.find_element(By.CSS_SELECTOR, '#ticketType_1')
        ticket_type_select.click()
    
        # 选择成人票
        adult_ticket_option = browser.find_element(By.CSS_SELECTOR, '#ticketType_1 > option[value="1"]')
        adult_ticket_option.click()
    
        t.sleep(1)
        browser.find_element(By.CSS_SELECTOR, '#submitOrder_id').click()
    
        qr_submit_id = browser.find_element(By.CSS_SELECTOR, '#qr_submit_id')
        if qr_submit_id:
            qr_submit_id.click()
        input("输入任意字符后回车继续...")
    
        # 关闭浏览器
        browser.quit()
    
    if __name__ == "__main__":
        main()
    
  • 相关阅读:
    上传ipa到appstore最简单的方法
    【项目方案】巧用质数实现优惠组合快速决策
    js onclick 父级 子节点 onclick 冲突 屏蔽 拦截
    SpringMVC-SSM整合/分页插件PageHelper案例
    动手学深度学习(1)—— 基础知识
    Python3 笔记:字符串的 encode() 和 bytes.decode()
    软考52-上午题-【数据库】-关系模式2
    Swift 其他
    从锁的类别角度讲,MySQL都有哪些锁
    3.2 基于vexpress-a9 arm平台 的QEMU仿真的rootfs镜像环境搭建
  • 原文地址:https://blog.csdn.net/x1343676/article/details/142000854