Python實例--12306的搶票功能


基礎知識學習

目標: 通過python程序實現自動登錄下單功能

知識點: Selenium + 雲打碼 + Python

學習鏈接:

1. Python學習--Selenium模塊

2. Python學習--打碼平台

3. Python系統學習梳理_【All】

需求分析

Chrome瀏覽器:71.0.3578.98_chrome_installer.exe

12306官網: https://www.12306.cn/index/index.html

image

 

 

# 選擇時間,點擊確定,查詢列表,獲取列表頁的請求URL

image

# 通過F12,查看當前頁面的請求URL:

https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E4%B8%8A%E6%B5%B7,SHH&ts=%E6%88%90%E9%83%BD,CDW&date=2019-02-03&flag=N,N,Y

image

# 查看請求參數

image

 

# 選擇一條車次數據,查看該列表(一條tr一條數據)

image

# 獲取時間元素

image

# 查看預定按鈕

image

 

# 當根據時間選擇好了列車后,點擊預定按鈕就會提示我們登錄賬號(注意是Ajax請求,所以需要until查找)

image

# 查找用戶名和密碼登錄框的ID

image

image

 

# 截取驗證碼

安裝插件 pip3 install Pillow

image

# 獲取全屏圖片 –> 計算截圖位置(根據元素的寬和高確定大小) –> 截取所需要圖片的大小

image

# 利用雲打碼進行驗證碼解析

image

# 定義打點(模擬選中圖片)

image

# 點擊登錄

image

# 選擇需要買票的人

image

# 點擊提交

image

 

完整代碼

12306.py

# coding: utf-8

import os
import time
import json
# 圖片操作對象
from PIL import Image
# 將二進制文件轉換為IO流輸出
from io import BytesIO
import yundama

from selenium import webdriver
# 1. 導入模塊
from selenium import webdriver
# 1> 等待對象模塊
from selenium.webdriver.support.wait import WebDriverWait
# 2> 導入等待條件模塊
from selenium.webdriver.support import expected_conditions as EC
# 3> 導入查詢元素模塊
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains

browser = webdriver.Chrome("chromedriver")

# 12306是異步請求,所以使用selenium的顯性等待方案
wait = WebDriverWait(browser, 10, 0.5)    # 創建等待對象

# 請求參數
linktypeid = 'dc'
fs = '上海,SHH'
ts = '成都,CDW'
date ='2019-02-03'
flag = 'N,N,Y'

# 請求URL:
# https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=上海&ts=北京&date=2019-02-03&flag=N,N,Y
base_url = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid={}&fs={}&ts={}&date={}&flag={}"
url = base_url.format(linktypeid, fs, ts, date, flag)

# 訪問12306的列表頁面
browser.get(url)

# 通過時間判定,選擇點擊預訂
# 尋找tr標簽中,屬性id以ticket開頭的數據
tr_list = wait.until(EC.visibility_of_all_elements_located((By.XPATH, "//tr[starts-with(@id, 'ticket_')]")))  # 找到所有可見元素

for tr in tr_list:
    count = 0
    t_start = tr.find_element_by_class_name('start-t').text
    with open('start.txt', 'a') as f:
        f.write(t_start)

    # 判斷時間是否在符合條件的范圍內
    if count < 5 and t_start == "11:16":   # 這里以06:33為例
        tr.find_element_by_class_name('btn72').click()
        break
    else:
        count += 1
        continue


# 點擊賬號(注意因為是異步加載的所有需要顯性等待)
# browser.find_element_by_link_text("賬號登錄").click()   # 因為還沒有加載出來,因為是Ajax請求,所以要用until查找
wait.until(EC.visibility_of_element_located((By.LINK_TEXT, "賬號登錄"))).click()

# 打開json文件,
with open('account.json', 'r', encoding='utf-8') as f:
    account = json.load(f, encoding='utf-8')    


# 輸入用戶名和密碼
j_username = browser.find_element_by_id('J-userName').send_keys(account['username'])
j_password = browser.find_element_by_id('J-password').send_keys(account['password'])


# 獲取全屏圖片
full_img_data = browser.get_screenshot_as_png()

# 計算截圖位置
login_img_element = browser.find_element_by_id("J-loginImg")

# 獲取截圖元素對象
scale = 1.0
x1 = login_img_element.location["x"] - 155
y1 = login_img_element.location["y"] - 100
x2 = x1 + login_img_element.size["width"]
y2 = y1 + login_img_element.size["height"]
cut_info = (x1, y1, x2, y2)
print('cut_info', cut_info)

# 把全屏圖片構建成全屏圖片操作對象
full_img = Image.open(BytesIO(full_img_data))
# 通過截圖信息對象截圖圖片
cut_img = full_img.crop(cut_info)
# 把圖片保存到本地
cut_img.save('cut_img.png')
time.sleep(5)
# 利用雲打碼進行圖片解析
result = yundama.decode('cut_img.png', '6701')
print('Image Decode:', result)

# 定義8個點擊坐標點
positions = [
    (80, 140),
    (230, 140),
    (380, 140),
    (530, 140),
    (80, 280),
    (230, 280),
    (380, 280),
    (530, 280)
]

# 模擬點擊坐標
for num in result:
    position = positions[int(num) - 1]
    # ActionChains 動作對象
    ActionChains(browser).move_to_element_with_offset(login_img_element,position[0] / 2,position[1] / 2).click().perform()
    print(position[0], position[1], "點擊圖片完成")
    time.sleep(5)


# 點擊登錄
browser.find_element_by_id("J-login").click()


# 點擊選擇人物
wait.until(EC.visibility_of_element_located((By.ID, "normalPassenger_1"))).click()

# 點擊提交訂單
browser.find_element_by_id('submitOrder_id').click()

time.sleep(2)
# 點擊確認訂單
wait.until(EC.visibility_of_element_located((By.ID, 'qr_submit_id'))).click()

print("搶票成功,請支付")



time.sleep(5)
browser.quit()

account.json

{
	"username":"18XXXXXXXXXX",
	"password":"18XXXXXXXXXX"
}

yundama.json

import http.client, mimetypes, urllib, json, time, requests


######################################################################

class YDMHttp:
    apiurl = 'http://api.yundama.com/api.php'
    username = ''
    password = ''
    appid = ''
    appkey = ''

    def __init__(self, username, password, appid, appkey):
        self.username = username
        self.password = password
        self.appid = str(appid)
        self.appkey = appkey

    def request(self, fields, files=[]):
        response = self.post_url(self.apiurl, fields, files)
        response = json.loads(response)
        return response

    def balance(self):
        data = {'method': 'balance', 'username': self.username, 'password': self.password, 'appid': self.appid,
                'appkey': self.appkey}
        response = self.request(data)
        if (response):
            if (response['ret'] and response['ret'] < 0):
                return response['ret']
            else:
                return response['balance']
        else:
            return -9001

    def login(self):
        data = {'method': 'login', 'username': self.username, 'password': self.password, 'appid': self.appid,
                'appkey': self.appkey}
        response = self.request(data)
        if (response):
            if (response['ret'] and response['ret'] < 0):
                return response['ret']
            else:
                return response['uid']
        else:
            return -9001

    def upload(self, filename, codetype, timeout):
        data = {'method': 'upload', 'username': self.username, 'password': self.password, 'appid': self.appid,
                'appkey': self.appkey, 'codetype': str(codetype), 'timeout': str(timeout)}
        file = {'file': filename}
        response = self.request(data, file)
        if (response):
            if (response['ret'] and response['ret'] < 0):
                return response['ret']
            else:
                return response['cid']
        else:
            return -9001

    def result(self, cid):
        data = {'method': 'result', 'username': self.username, 'password': self.password, 'appid': self.appid,
                'appkey': self.appkey, 'cid': str(cid)}
        response = self.request(data)
        return response and response['text'] or ''

    def decode(self, filename, codetype, timeout):
        cid = self.upload(filename, codetype, timeout)
        if (cid > 0):
            for i in range(0, timeout):
                result = self.result(cid)
                if (result != ''):
                    return cid, result
                else:
                    time.sleep(1)
            return -3003, ''
        else:
            return cid, ''

    def report(self, cid):
        data = {'method': 'report', 'username': self.username, 'password': self.password, 'appid': self.appid,
                'appkey': self.appkey, 'cid': str(cid), 'flag': '0'}
        response = self.request(data)
        if (response):
            return response['ret']
        else:
            return -9001

    def post_url(self, url, fields, files=[]):
        for key in files:
            files[key] = open(files[key], 'rb');
        res = requests.post(url, files=files, data=fields)
        return res.text


def decode(filename, codetype):
    ######################################################################
    # 用戶名
    username = 'XXXXXXXXX'

    # 密碼
    password = 'XXXXXXXX'
    # 軟件ID,開發者分成必要參數。登錄開發者后台【我的軟件】獲得!
    appid = 6795

    # 軟件密鑰,開發者分成必要參數。登錄開發者后台【我的軟件】獲得!
    appkey = '62a6760f83a91f818be141d9a77463c5'

    # 圖片文件
    # filename = 'getimage.jpg'

    # 驗證碼類型,# 例:1004表示4位字母數字,不同類型收費不同。請准確填寫,否則影響識別率。
    # 在此查詢所有類型 http://www.yundama.com/price.html
    # codetype = 3000

    # 超時時間,秒
    timeout = 60

    # 檢查
    if (username == 'username'):
        print('請設置好相關參數再測試')
    else:
        # 初始化
        yundama = YDMHttp(username, password, appid, appkey)

        # 登陸雲打碼
        uid = yundama.login();
        print('uid: %s' % uid)

        # 查詢余額
        balance = yundama.balance();
        print('balance: %s' % balance)

        # 開始識別,圖片路徑,驗證碼類型ID,超時時間(秒),識別結果
        cid, result = yundama.decode(filename, codetype, timeout);
        print('cid: %s, result: %s' % (cid, result))
        return result

    ######################################################################


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM