Python-requests-12306-登陆

好久没有更新了,今天周一困得要命,更新篇文章提提神。
本来想用requests写个12306购票,但是感觉写起来会很长,想想还是分开写好点
想想把12306分为2部分:
1.登陆
2.购票

  • 首先,我们先来看下12306网站
    Python-requests-12306-登陆_第1张图片

  • 问题1:网站连接不安全 可能要跳过ssl验证
    Python-requests-12306-登陆_第2张图片

  • 问题2:登陆时的验证码问题

  • 然后,我们来手动真正的登陆一次12306 看看登陆时需要提交哪些data
    Python-requests-12306-登陆_第3张图片
    通过chrome浏览器抓包后能发现一共需要3个参数
    1)username: 你的账号
    2)password:你的密码
    3)appid:otn

  • 但是我们没发现任何关于验证码有关的参数,可见是在我们登陆之前网站就提前验证了我们提交的验证码参数

  • 在login上面有个captcha_check应该就是验证码的验证,我们来看下
    Python-requests-12306-登陆_第4张图片

  • 也是3个参数
    1)answer:就是你登陆时选择的答案的对应的坐标
    2)login_site:E
    3)rand:sjrand

  • 这里我介绍两种方法破解这个验证码:
    A)手动输入
    B)打码平台:超级鹰

  • 先来说下手动输入的这种方法,这种方法就需要我们去测试这个坐标怎么得来的。

  • 先来看下我登陆时验证的验证码图片
    Python-requests-12306-登陆_第5张图片

  • 找出图片中的仪表盘和中国结
    提交的正确坐标有3个都用逗号隔开
    32,42,256,46,35,122
    我们来测试下看看这3个坐标都是哪里
    我们以图中箭头标记处为起点 范围为图片部分
    Python-requests-12306-登陆_第6张图片

  • 分别来看下这3个坐标(微信的ALT+A 或者QQ的CTRL+ALT+A截图)
    1)32,42
    Python-requests-12306-登陆_第7张图片

2)256,46
Python-requests-12306-登陆_第8张图片

3)35,122
Python-requests-12306-登陆_第9张图片

  • 不用说大家也都明白了,坐标该怎么得到了,而且答案也不是唯一的
    按照格式提交就OK了 每个数字用逗号隔开32,42,256,46,35,122

  • B)第二种方法我这里使用超级鹰打码平台,说实话,我写代码测试的时候这个打码平台,简直没法说,准确率太低…(http://www.chaojiying.com)
    大家去超级鹰官网上去下载python的对应文件然后进行修改就OK了 这里不再介绍打码平台的配置,直接贴上代码

#!/usr/bin/env python
# coding:utf-8

import requests
from hashlib import md5


class Chaojiying_Client(object):

    def __init__(self, username, password, soft_id):
        #平台账号
        self.username = username
        #平台密码
        self.password = md5(password.encode('utf-8')).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }

    def PostPic(self, im, codetype):
        """
        im: 图片字节
        codetype: 题目类型 参考 http://www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
        return r.json()

    def ReportError(self, im_id):
        """
        im_id:报错题目的图片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()
  • 我们只需要把验证码图片传到平台对应链接就OK
    他会返回一个json数据
    {‘err_no’: 0, ‘err_str’: ‘OK’, ‘pic_id’: ‘6032817371507400001’, ‘pic_str’: ‘107,92’, ‘md5’:‘2797d0254014d9a136cabf4888fdf380’}
    我们要提交的参数就是pic_str 取它的值就OK 坐标多的情况下,每个坐标之间是用 | 隔开的我们replace为逗号即可。

  • 接下来我们就用打码平台得到参数然后登陆12306
    在这之前我们先整理下思路:
    1)下载验证码图片
    https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand
    2)把图片提交到打码平台
    3)返回坐标
    4)构造登陆参数,进行登陆
    https://kyfw.12306.cn/passport/web/login

  • 不罗嗦 直接上代码

# -*- coding: utf-8 -*-
import json
import re
import time
import requests
import logging
logging.captureWarnings(True)
#my_txt我的账号密码等信息文件
from ticket_12306.my_txt import *
from urllib.parse import unquote
#验证码平台文件
from chaojiying.chaojiying_Python.chaojiying import Chaojiying_Client


class order_ticket_12306:

    def __init__(self):
        #创建session会话
        self.sess = requests.Session()
        #跳过ssl验证
        self.sess.verify = False
        #验证码图片地址
        self.captcha_url = 'https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand'
        #验证码图片存储路径
        self.captcha_file_path = 'captcha.jpg'
        #打码平台
        self.chaojiying = Chaojiying_Client(chaojiying_user, chaojiying_passwd, '96001')

    def captcha_download(self):
        '''
        下载验证码图片
        :return: 
        '''
        with open(self.captcha_file_path, 'wb') as f:
            image = self.sess.get(self.captcha_url)
            if image.status_code == 200:
                f.write(image.content)
            else:
                print('验证码下载失败, 正在重试...')
                self.captcha_download()

    def get_captcha(self):
        '''
        提交图片到打码平台,获取坐标
        :return: 
        '''
        self.captcha_download()
        im = open(self.captcha_file_path, 'rb').read()
        print('chaojiying111111111',self.chaojiying.PostPic(im, 9004))
        res = self.chaojiying.PostPic(im, 9004)
        return res

    def captcha_check(self):
        '''
        提交答案,进行验证
        会返回一个json数据里面有result_code
        如果result_code==4的话为成功,否则进行重新验证
        :return: 
        '''
        captcha_res = self.get_captcha()
        captcha_code = captcha_res.get('pic_str').replace('|', ',')
        check_data = {
                'answer': captcha_code,
                'login_site': 'E',
                'rand': 'sjrand',
        }
        check_url = 'https://kyfw.12306.cn/passport/captcha/captcha-check'
        res = self.sess.post(check_url, data=check_data).text
        print(res)
        check_result = str(json.loads(res).get('result_code'))
        print(check_result)
        if check_result!='4':
            print('验证码校验失败, 正在重试...')
            try:
                print('验证码验证报错....')
                pic_id = captcha_res.get('pic_id')
                #报错到打码平台
                self.chaojiying.ReportError(pic_id)
            except:
                print('验证码验证报错失败...')
            self.captcha_check()
        print(11111, res)


    def login(self):
        '''
        12306登陆
        :return: 
        '''
        login_headers = {
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Connection': 'keep-alive',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
            'X-Requested-With': 'XMLHttpRequest'
        }
        login_data = {
            'username': user_12306,
            'password': passwd_12306,
            'appid': 'otn'
        }
        login_url = 'https://kyfw.12306.cn/passport/web/login'
        res = self.sess.post(login_url, data=login_data, headers = login_headers).text
        print(22222, res)
        return res
  • OK我们再来登陆一次
    image.png
  • 登陆成功…

#总结:

  • 12306更新的频率很快,动不动就改动一些地方,其实这个12306我2个礼拜前写过一次,今天运行时候就各种报错,重新抓包对比了一下,发现所有的URL全都修改了!!!=–
    所以 我这代码可能过段时间就又用不了了 ,主要过程。

  • 最后,登陆后购票的部分,会写到下篇文章,《Python-requests-12306-购票》

你可能感兴趣的:(python爬虫)