好久没有更新了,今天周一困得要命,更新篇文章提提神。
本来想用requests写个12306购票,但是感觉写起来会很长,想想还是分开写好点
想想把12306分为2部分:
1.登陆
2.购票
问题2:登陆时的验证码问题
然后,我们来手动真正的登陆一次12306 看看登陆时需要提交哪些data
通过chrome浏览器抓包后能发现一共需要3个参数
1)username: 你的账号
2)password:你的密码
3)appid:otn
但是我们没发现任何关于验证码有关的参数,可见是在我们登陆之前网站就提前验证了我们提交的验证码参数
也是3个参数
1)answer:就是你登陆时选择的答案的对应的坐标
2)login_site:E
3)rand:sjrand
这里我介绍两种方法破解这个验证码:
A)手动输入
B)打码平台:超级鹰
先来说下手动输入的这种方法,这种方法就需要我们去测试这个坐标怎么得来的。
找出图片中的仪表盘和中国结
提交的正确坐标有3个都用逗号隔开
32,42,256,46,35,122
我们来测试下看看这3个坐标都是哪里
我们以图中箭头标记处为起点 范围为图片部分
不用说大家也都明白了,坐标该怎么得到了,而且答案也不是唯一的
按照格式提交就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
#总结:
12306更新的频率很快,动不动就改动一些地方,其实这个12306我2个礼拜前写过一次,今天运行时候就各种报错,重新抓包对比了一下,发现所有的URL全都修改了!!!=–
所以 我这代码可能过段时间就又用不了了 ,主要过程。
最后,登陆后购票的部分,会写到下篇文章,《Python-requests-12306-购票》