test version

dev
wenxianping 2018-01-20 23:31:13 +08:00
commit 1411824016
13 changed files with 699 additions and 411 deletions

View File

@ -1,11 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="79a86edc-721f-4aad-920d-b595d0348b7f" name="Default" comment="">
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/.idea/markdown-navigator.xml" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/.idea/markdown-navigator/profiles_settings.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/codeStyleSettings.xml" afterPath="$PROJECT_DIR$/.idea/codeStyleSettings.xml" />
<list default="true" id="79a86edc-721f-4aad-920d-b595d0348b7f" name="Default" comment="test version">
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/config/emailConf.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/config/urlConf.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/myException/UserPasswordException.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/tkcode" afterPath="$PROJECT_DIR$/tkcode" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/README.md" afterPath="$PROJECT_DIR$/README.md" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/config/ticketConf.py" afterPath="$PROJECT_DIR$/config/ticketConf.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/config/ticket_config.yaml" afterPath="$PROJECT_DIR$/config/ticket_config.yaml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/init/login.py" afterPath="$PROJECT_DIR$/init/login.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/init/select_ticket_info.py" afterPath="$PROJECT_DIR$/init/select_ticket_info.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/myUrllib/httpUtils.py" afterPath="$PROJECT_DIR$/myUrllib/httpUtils.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/myUrllib/myurllib2.py" afterPath="$PROJECT_DIR$/myUrllib/myurllib2.py" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/run.py" afterPath="$PROJECT_DIR$/run.py" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
@ -43,7 +51,7 @@
<entry file="file://$PROJECT_DIR$/init/login.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2030">
<caret line="145" column="38" lean-forward="false" selection-start-line="141" selection-start-column="7" selection-end-line="145" selection-end-column="38" />
<caret line="145" column="28" lean-forward="false" selection-start-line="141" selection-start-column="7" selection-end-line="145" selection-end-column="28" />
<folding>
<element signature="e#41#54#0" expanded="true" />
</folding>
@ -77,7 +85,7 @@
<entry file="file://$PROJECT_DIR$/config/ticketConf.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="168">
<caret line="12" column="18" lean-forward="true" selection-start-line="12" selection-start-column="18" selection-end-line="12" selection-end-column="18" />
<caret line="12" column="7" lean-forward="true" selection-start-line="12" selection-start-column="7" selection-end-line="12" selection-end-column="7" />
<folding>
<element signature="e#46#55#0" expanded="true" />
</folding>
@ -85,11 +93,11 @@
</provider>
</entry>
</file>
<file leaf-file-name="select_ticket_info.py" pinned="false" current-in-tab="true">
<file leaf-file-name="select_ticket_info.py" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/init/select_ticket_info.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="527">
<caret line="240" column="41" lean-forward="false" selection-start-line="240" selection-start-column="41" selection-end-line="240" selection-end-column="41" />
<state relative-caret-position="-456">
<caret line="204" column="16" lean-forward="true" selection-start-line="204" selection-start-column="16" selection-end-line="204" selection-end-column="16" />
<folding>
<element signature="e#23#34#0" expanded="true" />
</folding>
@ -97,10 +105,10 @@
</provider>
</entry>
</file>
<file leaf-file-name="ticket_config.yaml" pinned="false" current-in-tab="false">
<file leaf-file-name="ticket_config.yaml" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/config/ticket_config.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="400">
<state relative-caret-position="44">
<caret line="56" column="9" lean-forward="false" selection-start-line="56" selection-start-column="9" selection-end-line="56" selection-end-column="9" />
<folding />
</state>
@ -250,6 +258,8 @@
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope" />
<pane id="Scratches" />
<pane id="ProjectPane">
<subPane>
<expand>
@ -281,8 +291,6 @@
<select />
</subPane>
</pane>
<pane id="Scratches" />
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
@ -844,15 +852,15 @@
<frame x="0" y="0" width="1440" height="900" extended-state="0" />
<editor active="true" />
<layout>
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.17238913" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.17453505" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.5465538" sideWeight="0.50429183" order="14" side_tool="true" content_ui="tabs" />
<window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.54413545" sideWeight="0.49570817" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32889965" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" />
<window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.48004836" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" />
<window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.48004836" sideWeight="0.5" order="16" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.24964234" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32889965" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.32889965" sideWeight="0.5" order="17" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.7315599" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3297568" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="Data View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3297568" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
@ -1086,7 +1094,7 @@
<entry file="file://$PROJECT_DIR$/config/ticketConf.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="168">
<caret line="12" column="18" lean-forward="true" selection-start-line="12" selection-start-column="18" selection-end-line="12" selection-end-column="18" />
<caret line="12" column="7" lean-forward="true" selection-start-line="12" selection-start-column="7" selection-end-line="12" selection-end-column="7" />
<folding>
<element signature="e#46#55#0" expanded="true" />
</folding>
@ -1096,7 +1104,7 @@
<entry file="file://$PROJECT_DIR$/init/login.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2030">
<caret line="145" column="38" lean-forward="false" selection-start-line="141" selection-start-column="7" selection-end-line="145" selection-end-column="38" />
<caret line="145" column="28" lean-forward="false" selection-start-line="141" selection-start-column="7" selection-end-line="145" selection-end-column="28" />
<folding>
<element signature="e#41#54#0" expanded="true" />
</folding>
@ -1121,23 +1129,23 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/config/ticket_config.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="400">
<caret line="56" column="9" lean-forward="false" selection-start-line="56" selection-start-column="9" selection-end-line="56" selection-end-column="9" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/init/select_ticket_info.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="527">
<caret line="240" column="41" lean-forward="false" selection-start-line="240" selection-start-column="41" selection-end-line="240" selection-end-column="41" />
<state relative-caret-position="-456">
<caret line="204" column="16" lean-forward="true" selection-start-line="204" selection-start-column="16" selection-end-line="204" selection-end-column="16" />
<folding>
<element signature="e#23#34#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/config/ticket_config.yaml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="44">
<caret line="56" column="9" lean-forward="false" selection-start-line="56" selection-start-column="9" selection-end-line="56" selection-end-column="9" />
<folding />
</state>
</provider>
</entry>
</component>
</project>

102
README.md
View File

@ -3,10 +3,55 @@
- python版本支持
- 2.7
- 依赖库
- 依赖打码兔 需要去打码兔注册账号打码兔账号地址http://www.dama2.com一般充值1元就够用了
- 依赖打码兔 需要去打码兔注册(用户)账号打码兔账号地址http://www.dama2.com一般充值1元就够用了,充值打码兔之后,首次运行是需要到官网黑白名单授权
- 项目依赖包 requirements.txt
- 安装方法 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
- 项目使用说明
- 需要配置邮箱可以配置可以不配置配置邮箱的格式在yaml里面可以看到ex
- 提交订单验证码哪里依赖打码兔,所以如果是订票遇到验证码的时候,没有打码兔是过不了的,不推荐手动,手动太慢
- 配置yaml文件的时候需注意空格和遵循yaml语法格式项目的yaml配置ex
- ticket_config.yaml 配置说明
```
#station_date:出发日期格式ex2018-01-06
#station_trains:过滤车次格式ex
# - "G1353"
# - "G1329"
# - "G1355"
# - "G1303"
# - "G1357"
# - "G1305"
# - "G1359"
# - "G1361"
# - "G1373"
# - "G1363"
#from_station: 始发站
#to_station: 到达站
#set_type: 坐席(商务座,二等座,特等座,软卧,硬卧,硬座,无座)
#is_more_ticket:余票不足是否自动提交
#select_refresh_interval:抢票刷新间隔时间1为一秒0.1为100毫秒以此类推 如果捡漏推荐为1秒刷票设置0.01
#expect_refresh_interval:售票未开始等待刷新间隔时间1为一秒0.1为100毫秒以此类推
#ticket_black_list:加入小黑屋的等待时间默认3 min 小黑屋的功能是上次买票失败,证明此票已无机会,下次刷新看到此票跳过
#enable_proxy:是否开启代理模式,代理速度比较慢,如果是抢票阶段,不建议开启
#ticke_peoples: 乘客 ex: "张三"
#damatu:打码兔账号,用于自动登录和订单自动打码
#is_aotu_code是否自动打码如果选择Ture,则调用打码兔打码,默认不使用打码兔
#is_email: 是否需要邮件通知 ex: True or False 切记邮箱加完一定到config目录下测试emailConf功能是否正常
#邮箱配置 列举163
# email: "xxx@163.com"
# notice_email_list: "123@qq.com"
# username: "xxxxx"
# password: "xxxxx
# host: "smtp.163.com"
#邮箱配置 列举qq qq设置比较复杂需要在邮箱--账户--开启smtp服务取得授权码==邮箱登录密码
# email: "xxx@qq.com"
# notice_email_list: "123@qq.com"
# username: "xxxxx"
# password: "授权码"
# host: "smtp.qq.com"
```
- 项目开始
- 修改config/ticket_config.yaml文件按照提示更改自己想要的信息
- 运行根目录run.py即可开始
@ -26,6 +71,25 @@
- 本软件只供学习交流使用务作为商业用途交流群286271084
- 能为你抢到一张回家的票,是我最大的心愿
- 成功log如果是购票失败的请带上失败的log给我我尽力帮你挑也可加群一起交流程序只是加速买票的过程并不一定能买到票
```
正在第355次查询 乘车日期: 2018-02-12 车次G4741,G2365,G1371,G1377,G1329 查询无票 代理设置 无 总耗时429ms
车次: G4741 始发车站: 上海 终点站: 邵阳 二等座:有
正在尝试提交订票...
尝试提交订单...
出票成功
排队成功, 当前余票还剩余: 359 张
正在使用自动识别验证码功能
验证码通过,正在提交订单
提交订单成功!
排队等待时间预计还剩 -12 ms
排队等待时间预计还剩 -6 ms
排队等待时间预计还剩 -7 ms
排队等待时间预计还剩 -4 ms
排队等待时间预计还剩 -4 ms
恭喜您订票成功订单号为EB52743573, 请立即打开浏览器登录12306访问未完成订单在30分钟内完成支付
```
- 2017.5.13跟新
- 增加登陆错误判断(密码错误&ip校验
- 修改queryOrderWaitTime校验orderId字段bug校验msg字段bug校验messagesbug
@ -71,3 +135,39 @@
- 优化查票流程
- 修改二等座的余票数返回为字符串的问题
- 优化订单查询bug
- 2018.1.12更新
- 优化抢票页面逻辑
-增强代码稳定性
- 2018.1.13更新
- 修改下单验证码功能
- 优化大量调用user接口导致联系人不能用理论加快订票速度
- 增加邮箱功能
```
#is_email: 是否需要邮件通知 ex: True or False 切记邮箱加完一定要到config目录下测试emailConf功能是否正常
#email: 发送的邮箱地址 ex: 1@qq.com
#notice_email_list: 被通知人邮箱 ex: 2@qq.com
#username: 邮箱账号
#password: 邮箱密码
#host: 邮箱地址
```
- 2018.1.14更新
- 优化订票流程
- 优化挂机功能
- 修改之前程序到11点自动跳出功能现在修改为到早上7点自动开启刷票
- 需要开启打码兔代码功能is_aotu_code 设置为True
- 增加异常判断
- 2018.1.15更新
- 增加捡漏自动检测是否登录功能建议捡漏不要刷新太快2S最好否则会封IP
- 优化提交订单有很大记录无限排队的情况,感谢群里的小伙伴提供的思路
- 修改休眠时间为早上6点
- 2018.1.20更新,好久没跟新了,群里的小伙伴说登录不行了,今晚抽空改了一版登录,妥妥的
- 更新新版登录功能,经测试,更稳定有高效
- 优化手动打码功能
- 更新请求第三方库
- 优化若干代码,小伙伴尽情的放肆起来

42
config/emailConf.py Normal file
View File

@ -0,0 +1,42 @@
# -*- coding: utf8 -*-
__author__ = 'MR.wen'
from email.header import Header
from email.mime.text import MIMEText
from config.ticketConf import _get_yaml
import smtplib
def sendEmail(msg):
"""
邮件通知
:param str: email content
:return:
"""
email_conf = _get_yaml()
is_email = email_conf["email_conf"]["is_email"]
if is_email:
sender = email_conf["email_conf"]["email"]
receiver = email_conf["email_conf"]["notice_email_list"]
subject = '恭喜,您已订票成功'
username = email_conf["email_conf"]["username"]
password = email_conf["email_conf"]["password"]
host = email_conf["email_conf"]["host"]
s = "{0}".format(msg)
msg = MIMEText(s, 'text', 'utf-8') # 中文需参数utf-8单字节字符不需要
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = sender
msg['To'] = receiver
smtp = smtplib.SMTP_SSL()
smtp.connect(host)
smtp.login(username, password)
smtp.sendmail(sender, receiver.split(","), msg.as_string())
smtp.quit()
print("邮件已通知, 请查收")
else:
pass
if __name__ == '__main__':
sendEmail(1)

View File

@ -6,7 +6,6 @@ import yaml
import PyQt5
def _get_yaml():
"""
解析yaml

View File

@ -22,40 +22,67 @@
#ticke_peoples: 乘客
#damatu打码兔账号用于自动登录
#is_aotu_code是否自动打码如果选择Ture,则调用打码兔打码,默认不使用打码兔
#is_email: 是否需要邮件通知 ex: True or False 切记邮箱加完一定到config目录下测试emailConf功能是否正常
#邮箱配置 列举163
# email: "xxx@163.com"
# notice_email_list: "123@qq.com"
# username: "xxxxx"
# password: "xxxxx
# host: "smtp.163.com"
#邮箱配置 列举qq qq设置比较复杂需要在邮箱--账户--开启smtp服务取得授权码==邮箱登录密码
# email: "xxx@qq.com"
# notice_email_list: "123@qq.com"
# username: "xxxxx"
# password: "授权码"
# host: "smtp.qq.com"
set:
station_date: "2018-02-07"
station_date: "2018-02-12"
station_trains:
- "G4933"
- "G2365"
- "G1371"
- "G1377"
- "G1329"
# - "K4300"
# - "K5226"
# - "K7772"
# - "G1329"
# - "G1359"
# - "G1361"
# - "G1373"
# - "G1363"
from_station: "上海"
# - "G4933"
from_station: "昆山"
to_station: "长沙"
set_type:
- "二等座"
is_more_ticket: True
ticke_peoples:
- "文贤平"
# - "梁敏"
- "宋倩倩"
# - "彭淑杰"
12306count:
# - uesr: ""
# - pwd: "649823049lilymin"
- uesr: "931128603@qq.com"
- pwd: "QWERTY"
# - pwd: "apple1995"
- uesr: ""
- pwd: "songyu1995"
select_refresh_interval: 0.1
expect_refresh_interval: 0.1
expect_refresh_interval: 0.3
ticket_black_list_time: 3
is_aotu_code: True
is_aotu_code: False
#enable_proxy: False
damatu:
uesr: ""
pwd: "wen1995"
email_conf:
is_email: False
email: "@qq.com "
notice_email_list: "@qq.com"
username: ""
password: "xwopwxbkupbqbfgb"
host: "smtp.qq.com"

89
config/urlConf.py Normal file
View File

@ -0,0 +1,89 @@
import random
urls = {
"auth": {
"req_url": "https://kyfw.12306.cn/passport/web/auth/uamtk",
"req_type": "post"
},
"login": {
"req_url": "https://kyfw.12306.cn/passport/web/login",
"req_type": "post"
},
"getCodeImg": {
"req_url": "https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&{0}".format(random.random()),
"req_type": "get"
},
"codeCheck": {
"req_url": "https://kyfw.12306.cn/passport/captcha/captcha-check",
"req_type": "post"
},
"loginInit": {
"req_url": "https://kyfw.12306.cn/otn/login/init",
"req_type": "get"
},
"getUserInfo": {
"req_url": "https://kyfw.12306.cn/otn/index/initMy12306",
"req_type": "get"
},
"userLogin": {
"req_url": "https://kyfw.12306.cn/otn/login/userLogin",
"req_type": "get"
},
"uamauthclient": {
"req_url": "https://kyfw.12306.cn/otn/uamauthclient",
"req_type": "post"
},
"initdc_url": {
"req_url": "https://kyfw.12306.cn/otn/confirmPassenger/initDc",
"req_type": "get"
},
"get_passengerDTOs": {
"req_url": "https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs",
"req_type": "post"
},
"select_url": {
"req_url": "https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date={0}&leftTicketDTO.from_station={1}&leftTicketDTO.to_station={2}&purpose_codes=ADULT",
"req_type": "post"
},
"check_user_url": {
"req_url": "https://kyfw.12306.cn/otn/login/checkUser",
"req_type": "post"
},
"submit_station_url": {
"req_url": "https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest",
"req_type": "post"
},
"checkOrderInfoUrl": {
"req_url": "https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo",
"req_type": "post"
},
"getQueueCountUrl": {
"req_url": "https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount",
"req_type": "post"
},
"checkQueueOrderUrl": {
"req_url": "https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue",
"req_type": "post"
},
"checkRandCodeAnsyn": {
"req_url": "https://kyfw.12306.cn/otn/passcodeNew/checkRandCodeAnsyn",
"req_type": "post"
},
"codeImgByOrder": {
"req_url": "https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=passenger&rand=randp&%s" % random.random(),
"req_type": "post"
},
"queryOrderWaitTimeUrl": {
"req_url": "https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime",
"req_type": "post"
},
"queryMyOrderNoCompleteUrl": {
"req_url": "https://kyfw.12306.cn/otn/queryOrder/queryMyOrderNoComplete",
"req_type": "post"
},
"initNoCompleteUrl": {
"req_url": "https://kyfw.12306.cn/otn/queryOrder/initNoComplete",
"req_type": "post"
}
}

View File

@ -3,202 +3,205 @@
import random
import json
import re
import socket
from time import sleep
from config.ticketConf import _get_yaml
from PIL import Image
from damatuCode.damatuWeb import DamatuApi
from myException.UserPasswordException import UserPasswordException
from myUrllib import myurllib2
codeimg = 'https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=login&rand=sjrand&%s' % random.random()
class GoLogin:
def __init__(self, httpClint, urlConf):
self.httpClint = httpClint
self.randCode = ""
self.urlConf = urlConf
def cookietp():
stoidinput("获取Cookie")
Url = "https://kyfw.12306.cn/otn/login/init"
myurllib2.get(Url)
# for index, c in enumerate(myurllib2.cookiejar):
# stoidinput(c)
def cookietp(self):
print("正在获取cookie")
url = self.urlConf["loginInit"]["req_url"]
self.httpClint.send(url)
# Url = "https://kyfw.12306.cn/otn/login/init"
# myurllib2.get(Url)
# for index, c in enumerate(myurllib2.cookiejar):
# stoidinput(c)
def readImg():
"""
增加手动打码只是登录接口完全不用担心提交订单效率
思路
1.调用PIL显示图片
2.图片位置说明验证码图片中每个图片代表一个下标依次类推12345678
3.控制台输入对应下标按照英文逗号分开即可手动完成打码
:return:
"""
global randCode
stoidinput("下载验证码...")
img_path = './tkcode'
result = myurllib2.get(codeimg)
try:
open(img_path, 'wb').write(result)
if _get_yaml()["is_aotu_code"]:
randCode = DamatuApi(_get_yaml()["damatu"]["uesr"], _get_yaml()["damatu"]["pwd"], img_path).main()
else:
img = Image.open('./tkcode')
img.show()
codexy()
except OSError as e:
print (e)
pass
def stoidinput(text):
"""
正常信息输出
:param text:
:return:
"""
print "\033[34m[*]\033[0m %s " % text
def errorinput(text):
"""
错误信息输出
:param text:
:return:
"""
print "\033[32m[!]\033[0m %s " % text
return False
def codexy():
"""
获取验证码
:return: str
"""
Ofset = raw_input("[*] 请输入验证码: ")
select = Ofset.split(',')
global randCode
post = []
offsetsX = 0 # 选择的答案的left值,通过浏览器点击8个小图的中点得到的,这样基本没问题
offsetsY = 0 # 选择的答案的top值
for ofset in select:
if ofset == '1':
offsetsY = 46
offsetsX = 42
elif ofset == '2':
offsetsY = 46
offsetsX = 105
elif ofset == '3':
offsetsY = 45
offsetsX = 184
elif ofset == '4':
offsetsY = 48
offsetsX = 256
elif ofset == '5':
offsetsY = 36
offsetsX = 117
elif ofset == '6':
offsetsY = 112
offsetsX = 115
elif ofset == '7':
offsetsY = 114
offsetsX = 181
elif ofset == '8':
offsetsY = 111
offsetsX = 252
else:
def readImg(self):
"""
增加手动打码只是登录接口完全不用担心提交订单效率
思路
1.调用PIL显示图片
2.图片位置说明验证码图片中每个图片代表一个下标依次类推12345678
3.控制台输入对应下标按照英文逗号分开即可手动完成打码
:return:
"""
print ("下载验证码...")
codeimgUrl = self.urlConf["getCodeImg"]["req_url"]
img_path = './tkcode'
result = self.httpClint.send(codeimgUrl)
try:
open(img_path, 'wb').write(result)
if _get_yaml()["is_aotu_code"]:
self.randCode = DamatuApi(_get_yaml()["damatu"]["uesr"], _get_yaml()["damatu"]["pwd"], img_path).main()
else:
img = Image.open('./tkcode')
img.show()
self.codexy()
except OSError as e:
print (e)
pass
post.append(offsetsX)
post.append(offsetsY)
randCode = str(post).replace(']', '').replace('[', '').replace("'", '').replace(' ', '')
def codexy(self):
"""
获取验证码
:return: str
"""
def login(user, passwd):
"""
登陆
:param user: 账户名
:param passwd: 密码
:return:
"""
login_num = 0
while True:
cookietp()
readImg()
login_num += 1
randurl = 'https://kyfw.12306.cn/otn/passcodeNew/checkRandCodeAnsyn'
logurl = 'https://kyfw.12306.cn/otn/login/loginAysnSuggest'
surl = 'https://kyfw.12306.cn/otn/login/userLogin'
randdata = {
"randCode": randCode,
"rand": "sjrand"
Ofset = raw_input("请输入验证码: ")
select = Ofset.split(',')
post = []
offsetsX = 0 # 选择的答案的left值,通过浏览器点击8个小图的中点得到的,这样基本没问题
offsetsY = 0 # 选择的答案的top值
for ofset in select:
if ofset == '1':
offsetsY = 46
offsetsX = 42
elif ofset == '2':
offsetsY = 46
offsetsX = 105
elif ofset == '3':
offsetsY = 45
offsetsX = 184
elif ofset == '4':
offsetsY = 48
offsetsX = 256
elif ofset == '5':
offsetsY = 36
offsetsX = 117
elif ofset == '6':
offsetsY = 112
offsetsX = 115
elif ofset == '7':
offsetsY = 114
offsetsX = 181
elif ofset == '8':
offsetsY = 111
offsetsX = 252
else:
pass
post.append(offsetsX)
post.append(offsetsY)
self.randCode = str(post).replace(']', '').replace('[', '').replace("'", '').replace(' ', '')
def auth(self):
"""认证"""
authUrl = self.urlConf["auth"]["req_url"]
authData = {"appid": "otn"}
tk = self.httpClint.send(authUrl, authData)
return tk
def codeCheck(self):
"""
验证码校验
:return:
"""
codeCheck = self.urlConf["codeCheck"]["req_url"]
codeCheckData = {
"answer": self.randCode,
"rand": "sjrand",
"login_site": "E"
}
logdata = {
"loginUserDTO.user_name": user,
"userDTO.password": passwd,
"randCode": randCode
}
ldata = {
"_json_att": None
}
fresult = json.loads(myurllib2.Post(randurl, randdata), encoding='utf8')
checkcode = fresult['data']['msg']
if checkcode == 'FALSE':
errorinput("验证码有误,第{}次尝试重试".format(login_num))
fresult = self.httpClint.send(codeCheck, codeCheckData)
if "result_code" in fresult and fresult["result_code"] == "4":
print ("验证码通过,开始登录..")
return True
else:
stoidinput("验证码通过,开始登录..")
if "result_message" in fresult:
print(fresult["result_message"])
sleep(1)
try:
tresult = json.loads(myurllib2.Post(logurl, logdata), encoding='utf8')
if 'data' not in tresult:
errorinput("登录失败: %s" % tresult['messages'][0])
# elif "messages" in tresult and tresult["messages"][0].find("密码输入错误") is not -1:
# errorinput("登陆失败:{}".format(tresult["messages"][0]))
# break
elif 'messages' in tresult and tresult['messages']:
messages = tresult['messages'][0]
if messages.find("密码输入错误") is not -1:
errorinput("登陆失败:{}".format(tresult["messages"][0]))
self.httpClint.del_cookies()
def baseLogin(self, user, passwd):
"""
登录过程
:param user:
:param passwd:
:return: 权限校验码
"""
logurl = self.urlConf["login"]["req_url"]
logData = {
"username": user,
"password": passwd,
"appid": "otn"
}
tresult = self.httpClint.send(logurl, logData)
if 'result_code' in tresult and tresult["result_code"] == 0:
print ("登录成功")
tk = self.auth()
if "newapptk" in tk and tk["newapptk"]:
return tk["newapptk"]
else:
return False
elif 'result_message' in tresult and tresult['result_message']:
messages = tresult['result_message']
if messages.find("密码输入错误") is not -1:
raise UserPasswordException("{0}".format(messages))
else:
print ("登录失败: {0}".format("".join(tresult)))
print ("尝试重新登陆")
return False
else:
return False
def getUserName(self, uamtk):
"""
登录成功后,显示用户名
:return:
"""
if not uamtk:
return "权限校验码不能为空"
else:
uamauthclientUrl = self.urlConf["uamauthclient"]["req_url"]
data = {"tk": uamtk}
uamauthclientResult = self.httpClint.send(uamauthclientUrl, data)
if "result_code" in uamauthclientResult and uamauthclientResult["result_code"] == 0:
print("欢迎 {} 登录".format(uamauthclientResult["username"]))
return True
else:
return False
def go_login(self):
"""
登陆
:param user: 账户名
:param passwd: 密码
:return:
"""
user, passwd = _get_yaml()["set"]["12306count"][0]["uesr"], _get_yaml()["set"]["12306count"][1]["pwd"]
login_num = 0
while True:
self.cookietp()
self.httpClint.set_cookies(_jc_save_wfdc_flag="dc", _jc_save_fromStation="%u4E0A%u6D77%u8679%u6865%2CAOH", _jc_save_toStation="%u5170%u5DDE%u897F%2CLAJ", _jc_save_fromDate="2018-02-14", _jc_save_toDate="2018-01-16", RAIL_DEVICEID="EN_3_EGSe2GWGHXJeCkFQ52kHvNCrNlkz9n1GOqqQ1wR0i98WsD8Gj-a3YHZ-XYKeESWgCiJyyucgSwkFOzVHhHqfpidLPcm2vK9n83uzOPuShO3Pl4lCydAtQu4BdFqz-RVmiduNFixrcrN_Ny43135JiEtqLaI")
self.readImg()
login_num += 1
self.auth()
if self.codeCheck():
uamtk = self.baseLogin(user, passwd)
if uamtk:
if self.getUserName(uamtk):
break
else:
errorinput("登录失败: %s" % tresult['messages'][0])
stoidinput("尝试重新登陆")
else:
stoidinput("登录成功")
myurllib2.Post(surl, ldata)
getUserinfo()
break
except ValueError as e:
errorinput(e)
sleep(1)
def logout(self):
url = 'https://kyfw.12306.cn/otn/login/loginOut'
result = myurllib2.get(url)
if result:
print ("已退出")
else:
print ("退出失败")
def getUserinfo():
"""
登录成功后,显示用户名
:return:
"""
url = 'https://kyfw.12306.cn/otn/modifyUser/initQueryUserInfo'
data = dict(_json_att=None)
result = myurllib2.Post(url, data)
userinfo = result
name = r'<input name="userDTO.loginUserDTO.user_name" style="display:none;" type="text" value="(\S+)" />'
try:
stoidinput("欢迎 %s 登录" % re.search(name, result).group(1))
except AttributeError:
pass
def main():
login(_get_yaml()["set"]["12306count"][0]["uesr"], _get_yaml()["set"]["12306count"][1]["pwd"])
def logout():
url = 'https://kyfw.12306.cn/otn/login/loginOut'
result = myurllib2.get(url)
if result:
stoidinput("已退出")
else:
errorinput("退出失败")
if __name__ == "__main__":
main()
# logout()
# if __name__ == "__main__":
# # main()
# # logout()

View File

@ -3,20 +3,25 @@ import json
import datetime
import random
import re
import threading
import socket
import urllib
import sys
import time
import Queue
from collections import OrderedDict
from config import urlConf
from init import login
from config.emailConf import sendEmail
from config.ticketConf import _get_yaml
from damatuCode.damatuWeb import DamatuApi
from init.login import GoLogin
from myException.PassengerUserException import PassengerUserException
from myException.UserPasswordException import UserPasswordException
from myException.ticketConfigException import ticketConfigException
from myException.ticketIsExitsException import ticketIsExitsException
from myException.ticketNumOutException import ticketNumOutException
from myUrllib import myurllib2
from myUrllib.httpUtils import HTTPClient
reload(sys)
sys.setdefaultencoding('utf-8')
@ -33,8 +38,9 @@ class select:
self.user_info = ""
self.secretStr = ""
self.ticket_black_list = dict()
self.submitQueue = Queue.Queue(5)
self.ticket_skip_time = dict()
self.is_check_user = dict()
self.httpClint = HTTPClient()
self.confUrl = urlConf.urls
def get_ticket_info(self):
"""
@ -53,7 +59,7 @@ class select:
expect_refresh_interval = ticket_info_config["expect_refresh_interval"]
ticket_black_list_time = ticket_info_config["ticket_black_list_time"]
print "*"*20
print "当前配置:出发站:{0}\n到达站:{1}\n乘车日期:{2}\n坐席:{3}\n是否有票自动提交:{4}\n乘车人:{5}\n刷新间隔:{6}\n候选购买车次:{7}\n未开始刷票间隔时间:{8}\n僵尸票关小黑屋时长:{9}".format\
print "当前配置:出发站:{0}\n到达站:{1}\n乘车日期:{2}\n坐席:{3}\n是否有票自动提交:{4}\n乘车人:{5}\n刷新间隔:{6}\n候选购买车次:{7}\n未开始刷票间隔时间:{8}\n僵尸票关小黑屋时长:{9}\n".format\
(
from_station,
to_station,
@ -135,12 +141,11 @@ class select:
获取提交车票请求token
:return: token
"""
initdc_url = 'https://kyfw.12306.cn/otn/confirmPassenger/initDc'
initdc_result = myurllib2.get(initdc_url)
initdc_url = self.confUrl["initdc_url"]["req+url"]
initdc_result = self.httpClint.send(initdc_url)
token_name = re.compile(r"var globalRepeatSubmitToken = '(\S+)'")
ticketInfoForPassengerForm_name = re.compile(r'var ticketInfoForPassengerForm=(\{.+\})?')
order_request_params_name = re.compile(r'var orderRequestDTO=(\{.+\})?')
# if token_name and ticketInfoForPassengerForm_name and order_request_params_name:
self.token = re.search(token_name, initdc_result).group(1)
re_tfpf = re.findall(ticketInfoForPassengerForm_name, initdc_result)
re_orp = re.findall(order_request_params_name, initdc_result)
@ -158,15 +163,14 @@ class select:
获取乘客信息
:return:
"""
get_passengerDTOs = 'https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs'
get_passengerDTOs = self.confUrl["get_passengerDTOs"]["req_url"]
get_data = {
'_json_att': None,
'REPEAT_SUBMIT_TOKEN': self.token
}
jsonData = json.loads(myurllib2.Post(get_passengerDTOs, get_data))
jsonData = self.httpClint.send(get_passengerDTOs, get_data)
if 'data' in jsonData and jsonData['data'] and 'normal_passengers' in jsonData['data'] and jsonData['data'][
'normal_passengers']:
# return jsonData['data']['normal_passengers']
normal_passengers = jsonData['data']['normal_passengers']
_normal_passenger = [normal_passengers[i] for i in range(len(normal_passengers))if normal_passengers[i]["passenger_name"] in self.ticke_peoples]
return _normal_passenger if _normal_passenger else normal_passengers[0] # 如果配置乘车人没有在账号,则默认返回第一个用户
@ -179,25 +183,13 @@ class select:
print("未查找到常用联系人")
raise PassengerUserException("未查找到常用联系人,请先添加联系人在试试")
def leftTicketLog(self, from_station, to_station):
"""
模拟进入车次列表页
:param from_station:
:param to_station:
:return:
"""
leftTicketLogUrl = 'https://kyfw.12306.cn/otn/leftTicket/log?leftTicketDTO.train_date={0}&leftTicketDTO.from_station={1}&leftTicketDTO.to_station={2}&purpose_codes=ADULT'.format(
self.station_date, from_station, to_station)
leftTicketLog = json.loads(myurllib2.get(leftTicketLogUrl), encoding='utf-8')
if "status" in leftTicketLog and leftTicketLog["status"] is True:
return True
else:
if "message" in leftTicketLog and leftTicketLog["message"]:
print leftTicketLog["message"]
elif "validateMessages" in leftTicketLog and leftTicketLog["validateMessages"]:
print leftTicketLog["validateMessages"]
def submitOrderRequestFunc(self, from_station, to_station, station_date=None):
select_url = self.confUrl["select_url"]["req_url"].format(
self.station_date if station_date is None else station_date, from_station, to_station)
station_ticket = self.httpClint.send(select_url)
return station_ticket
def submitOrderRequest(self, from_station, to_station):
def submitOrderRequestImplement(self, from_station, to_station,):
"""
提交车次信息
车次对应字典
@ -212,8 +204,7 @@ class select:
} 参照station_seat()方法
:return:
"""
select_url = 'https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date={0}&leftTicketDTO.from_station={1}&leftTicketDTO.to_station={2}&purpose_codes=ADULT'.format(self.station_date, from_station, to_station)
station_ticket = json.loads(myurllib2.get(select_url), encoding='utf-8')
station_ticket = self.submitOrderRequestFunc(from_station, to_station)
value = station_ticket['data']
if not value:
print ('{0}-{1} 车次坐席查询为空...'.format(self.from_station, self.to_station))
@ -234,13 +225,16 @@ class select:
if self.ticket_black_list.has_key(train_no) and (datetime.datetime.now() - self.ticket_black_list[train_no]).seconds/60 < int(self.ticket_black_list_time):
print("该车次{} 正在被关小黑屋,跳过此车次".format(train_no))
break
if self.ticket_skip_time.has_key(train_no) and (datetime.datetime.now() - self.ticket_skip_time[train_no]).seconds < 10:
# 判断离上次车次订票时间,如果间隔太小,则跳过,如果想不停地疯狂提交那请设置99999就无等待了
break
else:
self.submitQueue.put({"obj": self, "train_no": train_no, "seat": self._station_seat[j].encode("utf8")})
print ('正在尝试提交订票...')
self.ticket_skip_time[train_no] = datetime.datetime.now()
# self.submitOrderRequestFunc(from_station, to_station, self.time())
self.submit_station()
self.getPassengerTicketStr(self._station_seat[j].encode("utf8"))
self.getRepeatSubmitToken()
if not self.user_info: # 修改每次都调用用户接口导致用户接口不能用
self.user_info = self.getPassengerDTOs()
if self.checkOrderInfo(train_no, self._station_seat[j].encode("utf8")):
break
else:
pass
else:
@ -254,18 +248,23 @@ class select:
检查用户是否达到订票条件
:return:
"""
check_user_url = 'https://kyfw.12306.cn/otn/login/checkUser'
check_user_url = self.confUrl["check_user_url"]["req_url"]
data = dict(_json_att=None)
check_user = json.loads(myurllib2.Post(check_user_url, data), encoding='utf-8')
check_user = self.httpClint.send(check_user_url, data)
check_user_flag = check_user['data']['flag']
if check_user_flag is True:
print ('尝试提交订单...')
return True
else:
if check_user['messages']:
print ('用户检查失败:%s可能未登录可能session已经失效' % check_user['messages'][0])
print ('正在尝试重新登录')
self.call_login()
self.is_check_user["user_time"] = datetime.datetime.now()
else:
print ('用户检查失败: %s可能未登录可能session已经失效' % check_user)
print ('正在尝试重新登录')
self.call_login()
self.is_check_user["user_time"] = datetime.datetime.now()
def submit_station(self):
"""
@ -278,7 +277,7 @@ class select:
:return:
"""
submit_station_url = 'https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest'
submit_station_url = self.confUrl["submit_station_url"]["req_url"]
data = [('secretStr', urllib.unquote(self.secretStr)), # 字符串加密
('train_date', self.time()), # 出发时间
('back_train_date', self.time()), # 返程时间
@ -287,7 +286,7 @@ class select:
('query_from_station_name', self.from_station), # 起始车站
('query_to_station_name', self.to_station), # 终点车站
]
submitResult = json.loads(myurllib2.Post(submit_station_url, data), encoding='utf-8')
submitResult = self.httpClint.send(submit_station_url, data)
if 'data' in submitResult and submitResult['data']:
if submitResult['data'] == 'N':
print ('出票成功')
@ -358,7 +357,7 @@ class select:
:return:
"""
passengerTicketStrList, oldPassengerStr = self.getPassengerTicketStrListAndOldPassengerStr()
checkOrderInfoUrl = 'https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo'
checkOrderInfoUrl = self.confUrl["checkOrderInfoUrl"]["req_url"]
data = OrderedDict()
data['cancel_flag'] = 2
data['bed_level_order_num'] = "000000000000000000000000000000"
@ -367,7 +366,7 @@ class select:
data['tour_flag'] = 'dc'
data['whatsSelect'] = 1
data['REPEAT_SUBMIT_TOKEN'] = self.token
checkOrderInfo = json.loads(myurllib2.Post(checkOrderInfoUrl, data, ))
checkOrderInfo = self.httpClint.send(checkOrderInfoUrl, data)
if 'data' in checkOrderInfo:
if "ifShowPassCode" in checkOrderInfo["data"] and checkOrderInfo["data"]["ifShowPassCode"] == "Y":
is_need_code = True
@ -381,6 +380,7 @@ class select:
else:
if "errMsg" in checkOrderInfo['data'] and checkOrderInfo['data']["errMsg"]:
print checkOrderInfo['data']["errMsg"]
else:
print checkOrderInfo
elif 'messages' in checkOrderInfo and checkOrderInfo['messages']:
@ -393,13 +393,11 @@ class select:
:param token:
:return:
"""
old_train_date = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['train_date']+"00:00:00" # 模仿12306格式 Sun May 21 2017 00:00:00 GMT+0800 (中国标准时间)
m_time = time.mktime(time.strptime(old_train_date, "%Y%m%d%H:%M:%S"))
l_time = time.localtime(m_time)
new_train_date = time.strftime("%a %b %d %Y %H:%M:%S", l_time)
getQueueCountUrl = 'https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount'
l_time = time.localtime(time.time())
new_train_date = time.strftime("%a %b %d %Y", l_time)
getQueueCountUrl = self.confUrl["getQueueCountUrl"]["req_url"]
data = {
'train_date': new_train_date,
'train_date': str(new_train_date) + " 00:00:00 GMT+0800 (中国标准时间)",
'train_no': self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['train_no'],
'stationTrainCode': self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['station_train_code'],
'seatType': self.set_type,
@ -410,16 +408,11 @@ class select:
'train_location': self.get_ticketInfoForPassengerForm()['train_location'],
'REPEAT_SUBMIT_TOKEN': self.get_token(),
}
getQueueCountResult = json.loads(myurllib2.Post(getQueueCountUrl, data))
getQueueCountResult = self.httpClint.send(getQueueCountUrl, data)
if "status" in getQueueCountResult and getQueueCountResult["status"] is True:
if "countT" in getQueueCountResult["data"]:
ticket = getQueueCountResult["data"]["ticket"]
ticket_split = sum(map(self.conversion_int, ticket.split(","))) if ticket.find(",") != -1 else ticket
# ticket_sum = sum([int(ticket_split[0]),int(ticket_split[1])])
# if set_type == "无座": # 修改无座和硬座的座位号提交是个字符串的问题
# ticket = ticket_split[1]
# elif set_type == "硬座":
# ticket = ticket_split[0]
countT = getQueueCountResult["data"]["countT"]
if int(countT) is 0:
if int(ticket_split) < len(self.user_info):
@ -448,8 +441,9 @@ class select:
模拟提交订单是确认按钮参数获取方法还是get_ticketInfoForPassengerForm 中获取
:return:
"""
passengerTicketStrList, oldPassengerStr = self.getPassengerTicketStrListAndOldPassengerStr()
checkQueueOrderUrl = "https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue"
checkQueueOrderUrl = self.confUrl["checkQueueOrderUrl"]["req_url"]
data = {
"passengerTicketStr": self.set_type + "," + ",".join(passengerTicketStrList).rstrip("_{0}".format(self.set_type)),
"oldPassengerStr": "".join(oldPassengerStr),
@ -460,15 +454,17 @@ class select:
"seatDetailType": "000", # 开始需要选择座位但是目前12306不支持自动选择作为那这个参数为默认
"roomType": "00", # 好像是根据一个id来判断选中的两种 第一种是00第二种是10但是我在12306的页面没找到该id目前写死是00不知道会出什么错
"dwAll": "N",
"whatsSelect": 1,
"_json_at": "",
"REPEAT_SUBMIT_TOKEN": self.get_token(),
}
try:
for i in range(3):
if is_node_code:
print("正在使用自动识别验证码功能")
randurl = 'https://kyfw.12306.cn/otn/passcodeNew/checkRandCodeAnsyn'
codeimg = 'https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=passenger&rand=sjrand&%s' % random.random()
result = myurllib2.get(codeimg)
checkRandCodeAnsyn = self.confUrl["checkRandCodeAnsyn"]["req_url"]
codeImgByOrder = self.confUrl["codeImgByOrder"]["req_url"]
result = self.httpClint.send(codeImgByOrder)
img_path = './tkcode'
open(img_path, 'wb').write(result)
randCode = DamatuApi(_get_yaml()["damatu"]["uesr"], _get_yaml()["damatu"]["pwd"],
@ -479,18 +475,18 @@ class select:
"_json_att": None,
"REPEAT_SUBMIT_TOKEN": self.get_token()
}
fresult = json.loads(myurllib2.Post(randurl, randData), encoding='utf8') # 校验验证码是否正确
fresult = self.httpClint.send(checkRandCodeAnsyn, randData) # 校验验证码是否正确
checkcode = fresult['data']['msg']
if checkcode == 'FALSE':
print ("验证码有误,第{}次尝试重试".format(i))
else:
if checkcode == 'TRUE':
print("验证码通过,正在提交订单")
data['randCode'] = randCode
break
else:
print ("验证码有误, 接口返回{0}{1}次尝试重试".format(fresult, i))
else:
print("不需要验证码")
break
checkQueueOrderResult = json.loads(myurllib2.Post(checkQueueOrderUrl, data))
checkQueueOrderResult = self.httpClint.send(checkQueueOrderUrl, data)
if "status" in checkQueueOrderResult and checkQueueOrderResult["status"]:
c_data = checkQueueOrderResult["data"] if "data" in checkQueueOrderResult else {}
if 'submitStatus' in c_data and c_data['submitStatus'] is True:
@ -514,12 +510,6 @@ class select:
排队获取订单等待信息,每隔3秒请求一次最高请求次数为20次
:return:
"""
# queryOrderWaitTimeUrl = "https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime"
# data = {
# "random": "{0}{1}".format(int(time.time()), random.randint(1, 9)),
# "tourFlag": "dc",
# "REPEAT_SUBMIT_TOKEN": self.get_token(),
# }
num = 1
while True:
_random = int(round(time.time() * 1000))
@ -528,13 +518,15 @@ class select:
print("超出排队时间,自动放弃,正在重新刷票")
break
try:
queryOrderWaitTimeUrl = "https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random={0}&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN={1}".format(_random, self.get_token())
queryOrderWaitTimeResult = json.loads(myurllib2.get(queryOrderWaitTimeUrl))
data = {"random": _random, "tourFlag": "dc"}
queryOrderWaitTimeUrl = self.confUrl["queryOrderWaitTimeUrl"]["req_url"]
queryOrderWaitTimeResult = self.httpClint.send(queryOrderWaitTimeUrl, data)
except ValueError:
queryOrderWaitTimeResult = {}
if queryOrderWaitTimeResult:
if "status" in queryOrderWaitTimeResult and queryOrderWaitTimeResult["status"]:
if "orderId" in queryOrderWaitTimeResult["data"] and queryOrderWaitTimeResult["data"]["orderId"] is not None:
sendEmail("恭喜您订票成功,订单号为:{0}, 请立即打开浏览器登录12306访问未完成订单在30分钟内完成支付".format(queryOrderWaitTimeResult["data"]["orderId"]))
raise ticketIsExitsException("恭喜您订票成功,订单号为:{0}, 请立即打开浏览器登录12306访问未完成订单在30分钟内完成支付".format(queryOrderWaitTimeResult["data"]["orderId"]))
elif "msg" in queryOrderWaitTimeResult["data"] and queryOrderWaitTimeResult["data"]["msg"]:
print queryOrderWaitTimeResult["data"]["msg"]
@ -549,10 +541,12 @@ class select:
print("{}次排队中,请耐心等待".format(num))
else:
print("排队中")
time.sleep(2)
time.sleep(1)
order_id = self.queryMyOrderNoComplete() # 尝试查看订单列表,如果有订单,则判断成功,不过一般可能性不大
if order_id:
sendEmail("恭喜您订票成功,订单号为:{0}, 请立即打开浏览器登录12306访问未完成订单在30分钟内完成支付".format(order_id))
raise ticketIsExitsException("恭喜您订票成功,订单号为:{0}, 请立即打开浏览器登录12306访问未完成订单在30分钟内完成支付".format(order_id))
else:
print(ticketNumOutException("订单提交失败!,正在重新刷票"))
@ -562,10 +556,10 @@ class select:
:return:
"""
self.initNoComplete()
queryMyOrderNoCompleteUrl = "https://kyfw.12306.cn/otn/queryOrder/queryMyOrderNoComplete"
queryMyOrderNoCompleteUrl = self.confUrl["queryMyOrderNoCompleteUrl"]["req_url"]
data = {"_json_att": None}
try:
queryMyOrderNoCompleteResult = json.loads(myurllib2.Post(queryMyOrderNoCompleteUrl, data))
queryMyOrderNoCompleteResult = self.httpClint.send(queryMyOrderNoCompleteUrl, data)
except ValueError:
queryMyOrderNoCompleteResult = {}
if queryMyOrderNoCompleteResult:
@ -590,9 +584,9 @@ class select:
获取订单前需要进入订单列表页获取订单列表页session
:return:
"""
initNoCompleteUrl = "https://kyfw.12306.cn/otn/queryOrder/initNoComplete"
initNoCompleteUrl = self.confUrl["initNoCompleteUrl"]["req_url"]
data = {"_json_att": None}
myurllib2.Post(initNoCompleteUrl, data)
self.httpClint.send(initNoCompleteUrl, data)
# def call_submit_ticket(self, function_name=None):
# """
@ -606,81 +600,61 @@ class select:
# else:
# self.submitOrderRequest()
def call_login(self):
"""登录回调方法"""
login = GoLogin(self.httpClint, self.confUrl)
login.go_login()
def main(self):
self.call_login()
from_station, to_station = self.station_table(self.from_station, self.to_station)
if self.leftTicketLog(from_station, to_station):
submitOrderConsumer("daemon1", self.submitQueue).start()
num = 1
runedTime = 0
while 1:
num += 1
sleepTime = self.select_refresh_interval*1000-runedTime
if sleepTime > 0:
time.sleep(sleepTime/1000.0)
if time.strftime('%H:%M:%S', time.localtime(time.time())) > "23:00:00":
print "12306休息时间本程序自动停止,明天早上七点运行"
break
start_time = datetime.datetime.now()
self.submitOrderRequest(from_station, to_station)
runedTime = (datetime.datetime.now()-start_time).microseconds/1000
print "正在第{0}次查询 乘车日期: {1} 车次{2} 查询无票 代理设置 无 总耗时{3}ms".format(num, self.station_date, ",".join(self.station_trains), runedTime)
class selectProducer(threading.Thread):
"""刷票队列"""
def __init__(self, t_name, data):
self.t_name = t_name
self.data = data
threading.Thread.__init__(self, name=self.t_name)
print "{0} 正在运行".format(self.t_name)
def run(self):
self.worker(self.data)
def worker(self, data):
obj = data['obj']
try:
if obj.check_user():
obj.submit_station()
obj.getPassengerTicketStr(data['seat'])
obj.getRepeatSubmitToken()
obj.user_info = obj.getPassengerDTOs()
if obj.checkOrderInfo(data['train_no'], data['seat']):
return
obj.submitQueue.task_done()
except PassengerUserException as e:
print e.message
except ticketConfigException as e:
print e.message
except ticketIsExitsException as e:
print e.message
except ticketNumOutException as e:
print e.message
except ValueError as e:
if e.message == "No JSON object could be decoded":
print("12306接口无响应正在重试")
else:
print(e.message)
print("{0} 线程运行结束".format(self.t_name))
class submitOrderConsumer(threading.Thread):
"""订单队列"""
def __init__(self, t_name, queue):
threading.Thread.__init__(self, name=t_name)
self.data = queue
print "{0} 正在运行".format(t_name)
def run(self):
# if self.leftTicketLog(from_station, to_station):
num = 1
while 1:
if not self.data.empty():
taskData = self.data.get()
selectProducer(taskData['train_no']+str(random.random()), taskData).start()
try:
num += 1
if "user_time" in self.is_check_user and (datetime.datetime.now() - self.is_check_user["user_time"]).seconds/60 > 10:
# 十分钟调用一次检查用户是否登录
self.check_user()
time.sleep(self.select_refresh_interval)
if time.strftime('%H:%M:%S', time.localtime(time.time())) > "23:00:00":
print "12306休息时间本程序自动停止,明天早上6点将自动运行"
time.sleep(60 * 60 * 7)
self.call_login()
start_time = datetime.datetime.now()
self.submitOrderRequestImplement(from_station, to_station)
print "正在第{0}次查询 乘车日期: {1} 车次{2} 查询无票 代理设置 无 总耗时{3}ms".format(num, self.station_date, ",".join(self.station_trains), (datetime.datetime.now()-start_time).microseconds/1000)
except PassengerUserException as e:
print e.message
break
except ticketConfigException as e:
print e.message
break
except ticketIsExitsException as e:
print e.message
break
except ticketNumOutException as e:
print e.message
break
except UserPasswordException as e:
print e.message
break
except ValueError as e:
if e.message == "No JSON object could be decoded":
print("12306接口无响应正在重试")
else:
time.sleep(1.5)
print(e.message)
except KeyError as e:
print(e.message)
except TypeError as e:
print(e.message)
except socket.error as e:
print(e.message)
if __name__ == '__main__':
a = select('上海', '北京')
a.main()
login()
# a = select('上海', '北京')
# a.main()

View File

@ -0,0 +1,2 @@
class UserPasswordException(Exception):
pass

View File

@ -1,5 +1,8 @@
# -*- coding: utf8 -*-
import datetime
import json
import socket
import requests
@ -10,45 +13,84 @@ class HTTPClient(object):
:param method:
:param headers: Must be a dict. Such as headers={'Content_Type':'text/html'}
"""
self.session = requests.session()
self._set_header()
self.initS()
def initS(self):
self._s = requests.Session()
self._s.headers.update(self._set_header())
return self
def set_cookies(self, **kwargs):
"""
设置cookies
:param kwargs:
:return:
"""
for k, v in kwargs.items():
self._s.cookies.set(k, v)
def del_cookies(self):
"""
删除所有的key
:return:
"""
self._s.cookies.clear()
def del_cookies_by_key(self, key):
"""
删除指定key的session
:return:
"""
self._s.cookies.set(key, None)
def _set_header(self):
"""设置header"""
add_header = {
return {
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"X-Requested-With": "xmlHttpRequest",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36",
"Referer": "https://kyfw.12306.cn/otn/login/init",
"Accept": "*/*",
}
self.session.headers.update(add_header)
def get(self, url, proxy=None, **kwargs):
if proxy:
proxies = {"http": proxy}
else:
proxies = ""
response = self.session.request(method="GET",
url=url,
proxies=proxies,
**kwargs)
if response.status_code == 200:
return response.content
else:
print("请求失败。{0}".format(response))
def setHeaders(self, headers):
self._s.headers.update(headers)
return self
def post(self, url, data=None, proxy=None, **kwargs):
if proxy:
proxies = {"http": proxy}
else:
proxies = ""
response = self.session.request(method="POST",
url=url,
data=data,
proxies=proxies,
**kwargs)
if response.status_code == 200:
return response.content
else:
print("请求失败。{0}".format(response))
def getHeadersHost(self):
return self._s.headers["Host"]
def setHeadersHost(self, host):
self._s.headers.update({"Host": host})
return self
def getHeadersReferer(self):
return self._s.headers["Referer"]
def setHeadersReferer(self, referer):
self._s.headers.update({"Referer": referer})
return self
def send(self, url, data=None, **kwargs):
"""send request to url.If response 200,return response, else return None."""
method = "post"if data else "get"
response = self._s.request(method=method,
url=url,
data=data,
**kwargs)
try:
if response.content:
return json.loads(response.content) if method == "post" else response.content
else:
return ""
except ValueError as e:
if e.message == "No JSON object could be decoded":
print("12306接口无响应正在重试")
else:
print(e.message)
except KeyError as e:
print(e.message)
except TypeError as e:
print(e.message)
except socket.error as e:
print(e.message)

View File

@ -20,9 +20,9 @@ def get(url):
request = urllib2.Request(url=url)
request.add_header("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
request.add_header('X-Requested-With', 'xmlHttpRequest')
request.add_header('User-Agent',
'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36')
request.add_header('Referer', 'https://kyfw.12306.cn/otn/login/init')
request.add_header('User-Agent', "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
)
request.add_header('Referer', 'https://kyfw.12306.cn/otn/confirmPassenger/initDc')
request.add_header('Accept', '*/*')
result = urllib2.urlopen(request).read()
assert isinstance(result, object)
@ -44,9 +44,9 @@ def Post(url, data):
# request = urllib2Post.Request(ajax_url, urllib.urlencode(dc))
request.add_header("Content-Type", "application/x-www-form-urlencoded;application/json;charset=utf-8")
request.add_header('X-Requested-With', 'xmlHttpRequest')
request.add_header('User-Agent',
'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36')
request.add_header('Referer', 'https://kyfw.12306.cn/otn/login/init')
request.add_header('User-Agent', "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
)
request.add_header('Referer', 'https://kyfw.12306.cn/otn/confirmPassenger/initDc')
request.add_header('Accept', '*/*')
# request.add_header('Accept-Encoding', 'gzip, deflate')
for i in range(3):

6
run.py
View File

@ -3,7 +3,9 @@ from init import login, select_ticket_info
def run():
login.main()
# login.main()
select_ticket_info.select().main()
run()
if __name__ == '__main__':
run()

BIN
tkcode

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 12 KiB