12306抢票系统无界面版本——(1)登录(12306验证码问题破解)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/r244925932/article/details/81225884

引言

随着高速铁路时代的来临,世界发达地区高速铁路网络的不断完善与发展,高速铁路运输成为各国重要的交通方式之一。同时铁路的高速发展正在影响着人们的出行方式。
“铁路12306”是中国铁路客户服务中心推出的官方购票应用软件,与火车票务官方网站共享用户、订单和票额等信息,并使用统一的购票业务规则,软件具有车票预订、在线支付、改签、退票、订单查询、常用联系人管理、个人资料修改、密码修改等功能。于2013年12月8日正式上线试运行。而“铁路12306”虽然满足了我们基本的购票需要,但是对一些特殊群体并没有照顾到。为了改变这种情况,为此尝试使用python代码来实现抢票系统。

使用工具和库

开发环境是python3.6.5
开发工具是sublime Text

使用到的重要库:
http请求(requests库)

模拟登录流程

1.进入12306登录网站

首先导入requests包,利用requests中的Session()保持cookie。

import requests
session = requests.Session()
url = "https://kyfw.12306.cn/otn/login/init"
response = session.get(url)

2.获取验证码
这里写图片描述
在开发者工具中的Network可以很容易找到其中的验证码。从而我们可以得到验证码的 Request URL: https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.5516997730666673
(这个验证码是实时刷新的)
接着上面的的代码,我们通过发送get请求得到验证码图片并保存。

#下载验证码
captcha_url = "https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.3874691076542045"

cap_response = session.get(captcha_url)
#保存验证码 写文件
f = open('captcha.jpg','wb')
f.write(cap_response.content)  #二进制数据
f.close()

3.校验验证码
首先尝试选择验证码并点击。我们可以在Netwrok中看到一个新的响应。这里写图片描述
从名字也可以很容易理解这是一个校验验证码的响应。我们把Headers拉到最下面可以看到通过发送POST请求来校验验证码是否正确。
这里写图片描述
这其中的answer就是我们选择验证码之后的位置。
下面是代码实现。

#校验验证码
check_url = "https://kyfw.12306.cn/passport/captcha/captcha-check"

'''
Form Data
answer: 168,45
login_site: E
rand: sjrand
'''
point = {
    '1': '35,43',
    '2': '108,43',
    '3': '185,43',
    '4': '254,43',
    '5': '34,117',
    '6': '108,117',
    '7': '180,117',
    '8': '258,117',
}
#可以确定八个图片的位置

def get_point(nums):
    nums = nums.split(',')
    # print(nums)
    temp = []
    for item in nums:  # ['3', '6']
        temp.append(point[item])
    return ','.join(temp)


data = {
    'answer': get_point(input('请输入验证码坐标:')),   #'254,106'
    'login_site': 'E',
    'rand': 'sjrand'
}

check_response = session.post(check_url,data=data)
# print(check_response.text)
check_res = check_response.json()
# 判断校验结果
if not check_res['result_code'] == '4':
    exit('验证码校验失败,请重新登录')

最后我们通过验证”result_code”是否等于4来判断验证码是否选择正确。
4.登录,校验用户名和密码
假如我们尝试选择正确的验证码登录,会发现12306是先验证验证码是否正确再去校验用户名或者密码。
通过跟验证码同样的方式,我们首先选择正确的验证码,输入用户名和密码后点击登录会发现得到了新的响应。
这里写图片描述
这里写图片描述
这里也很容易理解,浏览器得到账号和密码之后发送了一个表单给Request URL: https://kyfw.12306.cn/passport/web/login
用与验证码类似的方法来写代码。

# 登录 ,校验用户名和密码
import config
login_url = 'https://kyfw.12306.cn/passport/web/login'

login_data = {
    "username": config.username,   
    "password": config.password,
    "appid": "otn"
}
login_response = session.post(login_url,data=login_data)

这里我新写了一个代码来调用用户名和密码。

到这里我们差不多完成了验证码问题和登录问题。

当时我也简单的认为这里已经登录全部完成。但是之后测试时发现得到的网页中没有显示个人的名字。通过再次检查发现登录完成后还发送了两个请求得到权限来完整的完成登录。
4.获取权限
经过反复测试之后发现需要两个请求来获取权限。
这里写图片描述
首先我们发送请求到uamk中,FortData为:
这里写图片描述

#获取权限
uamtk_url = 'https://kyfw.12306.cn/passport/web/auth/uamtk'

uamtk_data = {
    'appid': 'otn'
}
response = session.post(uamtk_url, data=uamtk_data)
print(response.text)

uantk_response = response.json()['newapptk']

我们尝试输出得到的返回值,发现其中的”newapptk”就是第二个请求发送的表单内容
这里写图片描述
所以我们定义一个变量来存储这个值。再将它发送给”uamauthclient”中。

# 获取权限
auth_url = 'https://kyfw.12306.cn/otn/uamauthclient'
auth_data = {
    'tk': uantk_response
}
response = session.post(auth_url, data=auth_data)
if response.json()['result_code'] == 0:
    print(response.text)

完成这两步之后我们输出返回值。可以看到这里输出了我们用户的姓名。
名字已打码
5.跳转页面

# 跳转登陆页面

login_redirect = 'https://kyfw.12306.cn/otn/login/userLogin'
response = session.get(login_redirect)

check_url = 'https://kyfw.12306.cn/otn/login/checkUser'
data = {
    '_json_att': ''
}
response = session.post(check_url,data=data)
# print(response.text)

假如我们尝试输出可以得到用户的姓名来验证是否已经获取权限并登录成功。

到此为止我们已经完整的完成了登录过程。之后便是购票等步骤。

下面附上代码运行的结果。

这里写图片描述

测试完成后我们将整个代码进行封装。

import requests
import random
import config


class TicketRob:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
        })
        self.index_url = 'https://kyfw.12306.cn/otn/login/init'
        self.captcha_url = 'https://kyfw.12306.cn/passport/captcha/captcha-image'
        self.login_url = 'https://kyfw.12306.cn/passport/web/login'
        self.check_captcha_url = 'https://kyfw.12306.cn/passport/captcha/captcha-check'
        self.uamtk_url = 'https://kyfw.12306.cn/passport/web/auth/uamtk'
        self.auth_url = 'https://kyfw.12306.cn/otn/uamauthclient'


        self.point = {
            '1': '35,43',
            '2': '108,43',
            '3': '185,43',
            '4': '254,43',
            '5': '34,117',
            '6': '108,117',
            '7': '180,117',
            '8': '258,117',
        }
        self.dict = {}

    def get_point(self, nums):
        nums = nums.split(',')
        # print(nums)
        temp = []
        for item in nums:  # ['3', '6']
            temp.append(self.point[item])
        return ','.join(temp)

    # 检查用户名密码
    def main(self, username, password):
        data = {
            'username': username,
            'password': password,
            'appid': 'otn'
        }
        self.session.get(self.index_url)  # 1
        self.get_captcha()  # 2
        check_res = self.check_captcha()  # 3
        if check_res:
            response = self.session.post(self.login_url, data=data)  # 4
            if response.json()['result_code'] == 0:
                tk = self.get_tk()  # 5
                auth_res = self.get_auth(tk)  # 6

        # 获取验证码
    def get_captcha(self):
        data = {
            'login_site': 'E',
            'module': 'login',
            'rand': 'sjrand',
            str(random.random()): ''
        }
        response = self.session.get(self.captcha_url, params=data)
        with open('captcha.jpg', 'wb') as f:
            f.write(response.content)

    # 校验验证码
    def check_captcha(self):
        data = {
            'answer': self.get_point(input('请输入正确的图片序号>>>:')),
            'login_site': 'E',
            'rand': 'sjrand'
        }
        response = self.session.post(self.check_captcha_url, data=data)
        if response.json()['result_code'] == '4':
            return True
        else:
            print('验证码选择错误,请重新选择')
        return False

    # 获取权限token
    def get_tk(self):
        uamtk_data = {
            'appid': 'otn'
        }
        response = self.session.post(self.uamtk_url, data=uamtk_data)
        return response.json()['newapptk']

    # 获取权限
    def get_auth(self, tk):
        auth_data = {
            'tk': tk
        }
        response = self.session.post(self.auth_url, data=auth_data)
        if response.json()['result_code'] == 0:
            print(response.text)
            return True
        return False

if __name__ == '__main__':
    ticket = TicketRob()
    ticket.main(config.username, config.password)
展开阅读全文

12306自动抢票-python自动识别验证码并登陆12306源码

04-25

以下代码可自动登录12306 - 包括输入用户名密码以及自动识别验证码并点击验证码登陆。该源码需要稍作修改:rn把 username.send_keys('xxxxxxx') 中的 xxxxxx 改为 你自己的12306账号。rn把 password.send_keys('yyyyyy') 中的 yyyyy 改为自己的 12306 密码。rnrn即可运行。rn该源码把自动抢票的核心功能:识别验证码并点击验证码登陆实现了。rn把代码稍作加工,即可变为自己的自动抢票代码。rnrn运行环境 - 需要安装python运行环境,selenium,requests,浏览器默认为chrome。rnrn运行时 程序会自动分析并识别验证码并点击验证码,完成登陆过程。。。rnrn详细代码如下:rnrn#12306 自动打开12306网站,并输入用户名、密码和验证码,并登录12306,rn#author bigluorn#email: 3490699170@qq.comrnrn#coding=utf-8rnrnfrom selenium import webdriverrnimport timernfrom PIL import Imagernfrom selenium.webdriver.common.action_chains import ActionChainsrnimport osrnimport requestsrnimport numpyrnrnrn#指定button id和button文本值,并点击,连续点击5次rn#return:rn#0 click successfullyrn#-1 连续5次均failedrn#1 txt != dest_text,所以不点击rndef click_button(b,id,dest_text,j): #在当前页面查找并点击指定text,错误返回 -1.连续5次,错误时延时1秒rn txt=''rn for i in range(0,5):rn try:rn txt=b.find_element_by_id(id).textrn if txt == dest_text:rn b.find_element_by_id(id).click()rn return 0 rn else:rn return 1rn rn except:rn time.sleep(1)rn continuernrn return -1 #5次都失败了rnrnrn#给定button id和text,find a given textrn#0 foundrn#-1 not foundrndef find_button(b,id,dest_text):rn txt=''rn try:rn txt=b.find_element_by_id(id).textrn if txt == dest_text:rn return 0rn except: rn #print("find_button Error --page txt is "+txt+" input text is "+dest_text)rn return -1rn rn return -1rnrn#click refresh pic buttonrndef click_refresh(b):rn try:rn b.find_element_by_xpath("//*[@id='loginForm']/div/ul[2]/li[4]/div/div/div[1]").click()rn except:rn print("click_refresh:exception!!!!")rnrn#初始化浏览器 rndef init_browser(b):rn b.maximize_window()rnrn#进入登录页,必须是未登录状态rn# 0 : 成功rn#-1 : 出错了rndef visit_login_page(b):rn url = 'https://kyfw.12306.cn/otn/index/init'rn b.get(url)rn if find_button(b,u"login_user",u"登录") != 0: #没退出rn click_button(b,u"regist_out",u"退出",0) #点击退出rn time.sleep(5) #休息5秒再查看是否退出rn rn if click_button(b,u"login_user",u"登录",0) != 0: #点击登陆按钮rn return -1 #Error happened!!rn rn time.sleep(10) #访问login page后休息10秒,等待验证码图片加载完成 rn return 0rnrn#截取一张验证码图片,保存为aa.pngrndef get_a_verify_pic(b):rn imgelement=b.find_element_by_xpath("//*[@id='loginForm']/div/ul[2]/li[4]/div/div/div[3]")rn location = imgelement.location #获取验证码x,y轴坐标 rn size=imgelement.size #获取验证码的长宽rn rangle=(int(location['x']),int(location['y']),int(location['x']+size['width']),int(location['y']+size['height'])) #写成我们需要截取的位置坐标rn b.save_screenshot('aa.png')rn i=Image.open("aa.png") #打开截图rn pic_name='verify_code'+".jpg" #标准12306验证图片rn frame4=i.crop(rangle) #使用Image的crop函数,从截图中再次截取我们需要的区域rn frame4.save(pic_name)rn return pic_namernrnrn#破解图片验证码rndef ana_pic(b,pic_name):rn rn body_list=[]rn url='''http://littlebigluo.qicp.net:47720/'''rn files='file':(pic_name,open(pic_name,'rb'),'image/png')rn res=requests.post(url,files=files) #post picrnrn if res.status_code == 200: #return okrn try:rn #print(res.text)rn if u"文字应该" in res.text: #识别验证码成功 rn body_str_1=res.text.split(u'''''')rn body_str=body_str_1[2].split(u'<')[0].split() rn for index in body_str:rn body_list.append(int(index))rn return 0,numpy.array(body_list)rn rn except:rn print("ana pic failed!!!!")rn return -1,Nonern rn return -1,None #验证码解析失败rnrnrn#按输入的下标,点击一张验证码图片rndef click_one_pic(b,i):rn try:rn imgelement=b.find_element_by_xpath("//*[@id='loginForm']/div/ul[2]/li[4]/div/div/div[3]")rn if i<=4:rn ActionChains(b).move_to_element_with_offset(imgelement,40+72*(i-1),73).click().perform()rn else:rn i -= 4rn ActionChains(b).move_to_element_with_offset(imgelement,40+72*(i-1),145).click().perform()rn except:rn print("Wa -- click one pic except!!!")rn rn#按bodylist 指示,点击指定验证图片rndef click_pic(b,body_list):rn for i in range(len(body_list)):rn click_one_pic(b,body_list[i])rn time.sleep(1)rn rn#输入用户名密码,并点击验证码登陆rn#0:login successfullyrn#1:verify code failed,rn#-1 error happenedrndef login(b):rn pic_name=Nonern try:rn rn pic_name=get_a_verify_pic(b) #截取12306验证码图片rn ret_val,body_list=ana_pic(b,pic_name) #破解12306验证码rn rn username=b.find_element_by_id('username')rn username.clear()rn username.send_keys('xxxxxx')rn password=b.find_element_by_id('password')rn password.clear()rn password.send_keys('yyyyyyy') rn time.sleep(2) rn rn if ret_val != 0:rn #print("login : what??? predict return error!!")rn print("login -- no verified pic!!! !!")rn os.remove(pic_name) #exception occuredrn #click_refresh(b)rn return -1rn rn if len(body_list) == 0: #no pic recognizedrn click_refresh(b)rn print("login : what??? body list is null!!!")rn os.remove(pic_name) #exception occuredrn return 1 #verified failedrn rn click_pic(b,body_list)rn time.sleep(1) #休息1秒再点击登陆按钮rn if click_button(b,u"loginSub",u"登录",0) != 0:rn print("login : what??? click button exception!!!")rn return -1 #Error happened!! rn except:rn if None != pic_name:rn os.remove(pic_name) #exception occuredrn print("login:exception!!")rn return -1rn rn time.sleep(5) #查看验证码是否正确??rn ret_val=find_button(b,u"error_msgmypasscode1",u"请点击正确的验证码")rn if ret_val == 0: #验证码错误rn print("login--Verified code error!!!")rn return 1rn rn os.remove(pic_name)rn print("login--successfully!!!")rn return 0rnrnrn#循环loginrn#返回rn#0:登陆成功-正常返回rn#-1:登陆失败或异常返回rn#1 :验证码未识别出来rndef try_login(b):rn rn for k in range(0,5): #连续尝试5次rn rt_val=login(b)rn if rt_val < 0: #error happenedrn print("verify got exception!!")rn time.sleep(10)rn continuern elif rt_val == 1: #verified code errorrn print("verify - code error!!")rn time.sleep(5)rn continue #login againrn else: #login successfullyrn print("login successfully!!!")rn return 0 rn rn return -1 #login failedrnrnif __name__ == "__main__": rn rn b = webdriver.Chrome()rn init_browser(b)rn visit_login_page(b)rn rn ret_val = try_login(b) #尝试登录rn if ret_val<0: rn print("main -- try_login failed!!!") rn else:rn print("main -- try_login successfully!!!") rn rnrn print("Good job!bigluo!!")rn 论坛

没有更多推荐了,返回首页