PYTHON抢票实现

爬虫抢火车票的原理可以分为以下几个步骤:

  1. 发送请求:使用爬虫工具发送HTTP请求到12306官方网站的查询接口,以获取火车票的相关信息。

  2. 解析响应:对返回的HTML页面进行解析,提取出需要的数据,如车次、座位、票价等。

  3. 数据处理:根据用户的需求和筛选条件,对解析得到的数据进行筛选、排序等操作,以确定最合适的火车票。

  4. 登录验证:如果需要登录才能进行抢票操作,爬虫需要模拟用户登录12306官方网站,提交登录表单并验证登录状态。

  5. 抢票操作:模拟用户在12306官方网站上进行抢票操作,包括选择车次、座位、填写乘客信息等。

  6. 提交订单:将抢到的火车票信息提交到12306官方网站,生成订单。

  7. 支付流程:如果需要支付,爬虫需要模拟用户进行支付操作,包括选择支付方式、填写支付信息等。

  8. 确认订单:确认订单并获取订单信息。

需要注意的是,抢票过程中可能会遇到验证码、登录限制、网络延迟等问题,需要相应的处理策略。此外,12306官方网站也有一些反爬虫的措施,需要注意遵守相关规定和法律法规。

总之,爬虫抢火车票的原理是通过模拟用户在12306官方网站上的操作流程,获取并处理相关数据,以实现自动化的抢票功能。

以下是一个简单的Python代码示例,用于模拟抢火车票的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests

def grab_train_ticket():
# 设置请求的URL和参数
url = "https://api.example.com/tickets"
params = {
"date": "2022-01-01",
"from": "北京",
"to": "上海",
"passengers": 1
}

# 发送请求
response = requests.get(url, params=params)

# 处理响应
if response.status_code == 200:
tickets = response.json()
if tickets:
print("成功抢到火车票!")
print("票务信息:")
for ticket in tickets:
print("车次:", ticket["train_number"])
print("座位:", ticket["seat"])
print("价格:", ticket["price"])
else:
print("很抱歉,没有可用的火车票。")
else:
print("请求失败,请稍后重试。")

# 调用抢票函数
grab_train_ticket()

在这个示例中,我们使用了requests库发送HTTP请求,并处理了响应。您需要根据实际情况修改URL、参数和处理逻辑。请确保您有正确的API地址和参数,并根据API文档进行相应的调整。

下面给出完整的代码(需注意安装浏览器的驱动)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
```

import re
from splinter.browser import Browser
from time import sleep
import sys
import httplib2
from urllib import parse
import smtplib
from email.mime.text import MIMEText
import time


class BrushTicket(object):
"""买票类及实现方法"""

def __init__(self, passengers, from_time, from_station, to_station, number, seat_type, receiver_mobile,
receiver_email):
"""定义实例属性,初始化"""
# 乘客姓名
self.passengers = passengers
# 起始站和终点站
self.from_station = from_station
self.to_station = to_station
# 乘车日期
self.from_time = from_time
# 车次编号
self.number = number.capitalize()
# 座位类型所在td位置
if seat_type == '商务座特等座':
seat_type_index = 1
seat_type_value = 9
elif seat_type == '一等座':
seat_type_index = 2
seat_type_value = 'M'
elif seat_type == '二等座':
seat_type_index = 3
seat_type_value = 0
elif seat_type == '高级软卧':
seat_type_index = 4
seat_type_value = 6
elif seat_type == '软卧':
seat_type_index = 5
seat_type_value = 4
elif seat_type == '动卧':
seat_type_index = 6
seat_type_value = 'F'
elif seat_type == '硬卧':
seat_type_index = 7
seat_type_value = 3
elif seat_type == '软座':
seat_type_index = 8
seat_type_value = 2
elif seat_type == '硬座':
seat_type_index = 9
seat_type_value = 1
elif seat_type == '无座':
seat_type_index = 10
seat_type_value = 1
elif seat_type == '其他':
seat_type_index = 11
seat_type_value = 1
else:
seat_type_index = 7
seat_type_value = 3
self.seat_type_index = seat_type_index
self.seat_type_value = seat_type_value
# 通知信息
self.receiver_mobile = receiver_mobile
self.receiver_email = receiver_email
# 新版12306官网主要页面网址
self.login_url = 'https://kyfw.12306.cn/otn/resources/login.html'
self.init_my_url = 'https://kyfw.12306.cn/otn/view/index.html'
self.ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc'
# 浏览器驱动信息,驱动下载页:https://sites.google.com/a/chromium.org/chromedriver/downloads
self.driver_name = 'chrome'
self.driver = Browser(driver_name=self.driver_name)

def do_login(self):
"""登录功能实现,手动识别验证码进行登录"""
self.driver.visit(self.login_url)
sleep(1)
# 选择登陆方式登陆
print('请扫码登陆或者账号登陆……')
while True:
if self.driver.url != self.init_my_url:
sleep(1)
else:
break

def start_brush(self):
"""买票功能实现"""
# 浏览器窗口最大化
self.driver.driver.maximize_window()
# 登陆
self.do_login()
# 跳转到抢票页面
self.driver.visit(self.ticket_url)
try:
print('开始刷票……')
# 加载车票查询信息
self.driver.cookies.add({"_jc_save_fromStation": self.from_station})
self.driver.cookies.add({"_jc_save_toStation": self.to_station})
self.driver.cookies.add({"_jc_save_fromDate": self.from_time})
self.driver.reload()
count = 0
while self.driver.url == self.ticket_url:
try:
self.driver.find_by_text('查询').click()
except Exception as error_info:
print(error_info)
sleep(1)
continue
sleep(0.2)
count += 1
local_date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print('第%d次点击查询……[%s]' % (count, local_date))
try:
current_tr = self.driver.find_by_xpath(
'//tr[@datatran="' + self.number + '"]/preceding-sibling::tr[1]')
if current_tr:
if current_tr.find_by_tag('td')[self.seat_type_index].text == '--':
print('无此座位类型出售,已结束当前刷票,请重新开启!')
sys.exit(1)
elif current_tr.find_by_tag('td')[self.seat_type_index].text == '无':
print('无票,继续尝试……')
sleep(1)
else:
# 有票,尝试预订
print('刷到票了(余票数:' + str(
current_tr.find_by_tag('td')[self.seat_type_index].text) + '),开始尝试预订……')
current_tr.find_by_css('td.no-br>a')[0].click()
sleep(1)
key_value = 1
for p in self.passengers:
if '()' in p:
p = p[:-1] + '学生' + p[-1:]
# 选择用户
print('开始选择用户……')
self.driver.find_by_text(p).last.click()
# 选择座位类型
print('开始选择席别……')
if self.seat_type_value != 0:
self.driver.find_by_xpath(
"//select[@id='seatType_" + str(key_value) + "']/option[@value='" + str(
self.seat_type_value) + "']").first.click()
key_value += 1
sleep(0.2)
if p[-1] == ')':
self.driver.find_by_id('dialog_xsertcj_ok').click()
print('正在提交订单……')
self.driver.find_by_id('submitOrder_id').click()
sleep(2)
# 查看放回结果是否正常
submit_false_info = self.driver.find_by_id('orderResultInfo_id')[0].text
if submit_false_info != '':
print(submit_false_info)
self.driver.find_by_id('qr_closeTranforDialog_id').click()
sleep(0.2)
self.driver.find_by_id('preStep_id').click()
sleep(0.3)
continue
print('正在确认订单……')
self.driver.find_by_id('qr_submit_id').click()
print('预订成功,请及时前往支付……')
# 发送通知信息
self.send_mail(self.receiver_email, '恭喜您,抢到票了,请及时前往12306支付订单!')
self.send_sms(self.receiver_mobile, '您的验证码是:1230。请不要把验证码泄露给其他人。')
else:
print('不存在当前车次【%s】,已结束当前刷票,请重新开启!' % self.number)
sys.exit(1)
except Exception as error_info:
print(error_info)
# 跳转到抢票页面
self.driver.visit(self.ticket_url)
except Exception as error_info:
print(error_info)

def send_sms(self, mobile, sms_info):
"""发送手机通知短信,用的是-互亿无线-的测试短信"""
host = "106.ihuyi.com"
sms_send_uri = "/webservice/sms.php?method=Submit"
account = "C59782899"
pass_word = "19d4d9c0796532c7328e8b82e2812655"
params = parse.urlencode(
{'account': account, 'password': pass_word, 'content': sms_info, 'mobile': mobile, 'format': 'json'}
)
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
conn = httplib2.HTTPConnectionWithTimeout(host, port=80, timeout=30)
conn.request("POST", sms_send_uri, params, headers)
response = conn.getresponse()
response_str = response.read()
conn.close()
return response_str

def send_mail(self, receiver_address, content):
"""发送邮件通知"""
# 连接邮箱服务器信息
host = 'smtp.163.com'
port = 25
sender = 'gxcuizy@163.com' # 你的发件邮箱号码
pwd = '******' # 不是登陆密码,是客户端授权密码
# 发件信息
receiver = receiver_address
body = '<h2>温馨提醒:</h2><p>' + content + '</p>'
msg = MIMEText(body, 'html', _charset="utf-8")
msg['subject'] = '抢票成功通知!'
msg['from'] = sender
msg['to'] = receiver
s = smtplib.SMTP(host, port)
# 开始登陆邮箱,并发送邮件
s.login(sender, pwd)
s.sendmail(sender, receiver, msg.as_string())


if __name__ == '__main__':
# 乘客姓名
passengers_input = input(
'请输入乘车人姓名,多人用英文逗号“,”连接,(例如单人“张三”或者多人“张三,李四”,如果学生的话输入“王五()”):')
passengers = passengers_input.split(",")
while passengers_input == '' or len(passengers) > 4:
print('乘车人最少1位,最多4位!')
passengers_input = input('请重新输入乘车人姓名,多人用英文逗号“,”连接,(例如单人“张三”或者多人“张三,李四”):')
passengers = passengers_input.split(",")
# 乘车日期
from_time = input('请输入乘车日期(例如“2018-08-08”):')
date_pattern = re.compile(r'^\d{4}-\d{2}-\d{2}$')
while from_time == '' or re.findall(date_pattern, from_time) == []:
from_time = input('乘车日期不能为空或者时间格式不正确,请重新输入:')
# 城市cookie字典
city_list = {
'lz':'%u5170%u5DDE%2CLZJ',#兰州
'zz':'%u90D1%u5DDE%2CZZF',#郑州
'pds':'%u5E73%u9876%u5C71%2CPEN',#平顶山
'wh':'%u829C%u6E56%2CWHH',#芜湖
}
# 出发站
from_input = input('请输入出发站,只需要输入首字母就行:')
while from_input not in city_list.keys():
from_input = input('出发站不能为空或不支持当前出发站(如有需要,请联系管理员!),请重新输入:')
from_station = city_list[from_input]
# 终点站
to_input = input('请输入终点站,只需要输入首字母就行:')
while to_input not in city_list.keys():
to_input = input('终点站不能为空或不支持当前终点站(如有需要,请联系管理员!),请重新输入:')
to_station = city_list[to_input]
# 车次编号
number = input('请输入车次号(例如“K1438”):')
while number == '':
number = input('车次号不能为空,请重新输入:')
# 座位类型
seat_type = input('请输入座位类型(例如“软卧”):')
while seat_type == '':
seat_type = input('座位类型不能为空,请重新输入:')
# 抢票成功,通知该手机号码
receiver_mobile = input('请预留一个手机号码,方便抢到票后进行通知(例如:18888888888):')
mobile_pattern = re.compile(r'^1{1}\d{10}$')
while receiver_mobile == '' or re.findall(mobile_pattern, receiver_mobile) == []:
receiver_mobile = input('预留手机号码不能为空或者格式不正确,请重新输入:')
receiver_email = input('请预留一个邮箱,方便抢到票后进行通知(例如:test@163.com):')
while receiver_email == '':
receiver_email = input('预留邮箱不能为空,请重新输入:')
# 开始抢票
ticket = BrushTicket(passengers, from_time, from_station, to_station, number, seat_type, receiver_mobile,
receiver_email)
ticket.start_brush()
```

其中的火车站需要12306官网的cookie中查询,手机端可以利用Alook浏览器复制cookie