看到很多人对12306抢票有着非常大的兴趣,所以我就试了下登录,查票,购买还没实现,并且我的方法也不是很理想,推荐给小白学习,并且我也是用最易懂的代码来写,所以说如果你是一位python爬虫小白的话,那这篇文章或许会帮到你,干货满满@
2021-02-23日更新:
各位小伙伴,是不是查询余票都特别困难了?12306升级后,直接使用开发者工具抓包得到的ajax链接直接照搬不管用了,除非是将原cookies加入头文件中,否则访问永远都是提示错误,其实这个很好解决
但是cookie有有效期,过一段时间就无法使用了,其实这也是一个反爬措施,我走了点捷径,发现这样也行,所以,查票目前还是很简单的
1.使用requests模块的session函数,记录下当前的操作一直都是一个浏览器
2.先访问查询页面的前一个页面
3.再访问ajax查询接口
来看看我的代码吧:
import requests
# 使用session记录当前是同一个会话(通俗的说就是所有的操作都在同一个浏览器中操作)
request = requests.session()
# 查询页的前一个页面
url_1 = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E6%B7%B1%E5%9C%B3," \
"SZQ&ts=%E6%88%90%E9%83%BD,CDW&date=2021-02-27&flag=N,N,Y"
# ajax查询接口
url = "https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2021-02-27&leftTicketDTO.from_station=SZQ&leftTicketDTO.to_station=CDW&purpose_codes=ADULT"
# 这里列出了此接口的参数,因为网址已经包含这些信息了,后期就不用拼接了
from_data = {
'leftTicketDTO.train_date': '2021-02-23',
'leftTicketDTO.from_station': 'SZQ',
'leftTicketDTO.to_station': 'CDW',
'purpose_codes': 'ADULT',
}
# 这个记得加上哦
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
}
# 先访问前一个页面
req = request.get(url_1, headers=header)
# 再访问接口
req_3 = request.get(url, headers=header)
# 成功!
print(req_3.text)
这样一来,系统就会认为你是从前一个页面过来的,于是成功绕过这个反爬措施
2021-02-24日更新:获取所有12306车站编码信息,格式化数据成字典类型(变成字典了就能随便存到哪了,mysql、mongodb、elasticsearch等等)
以下是代码:
# version版本信息,默认是我写这块代码的最新版本,我测试过,不加版本号也是可以获取到的
def init_station_code(version=1.9186):
# 车站编码网址
get_station_url = "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js"
station_msg_req = requests.get(url=get_station_url, headers=headers, params={"station_version": version})
# 正则提取“|北京|VPA|”格式的内容
d = re.findall("^|[\u4e00-\u9fa5]{2,5}[|][A-Z]{2,5}|$", station_msg_req.text)
# 将车站名称和车站编码变成字典格式
station_msg = [{"station_name": x.split("|")[0], "station_code": x.split("|")[1]} for x in d[1: -1]]
# 剩余的你想怎么存就怎么存
print(station_msg)
注:所有代码在python3.6环境下编写,请留意版本区别
查票:
2021-02-24日更新:查票信息更加详细化
如果你没有登录,查票也是可以查的,同时按照逻辑来说的话,你也是先看有没有票再去考虑登录购买吧?你也许会问为什么你知道data[3]代表车次,data[*]代表**,同样,我也是通过开发者工具抓包知道的,抓到数据之后,进行一些简单分析即可知道
代码同样非常简单:
# 查票,参数:from_station:起始站, to_station:终点站, train_date:乘车时间
def find_train_tickets(from_station, to_station, train_date):
url_1 = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E6%B7%B1%E5%9C%B3,SZQ&ts=%E6%88%90%E9%83%BD,CDW&date=2021-02-27&flag=N,N,Y"
url = "https://kyfw.12306.cn/otn/leftTicket/queryZ"
from_data = {
'leftTicketDTO.train_date': train_date,
'leftTicketDTO.from_station': from_station,
'leftTicketDTO.to_station': to_station,
'purpose_codes': 'ADULT',
}
request.get(url_1, headers=headers)
train_tickets_req = request.get(url, headers=headers, params=from_data)
train_tickets_msg = train_tickets_req.json()
if train_tickets_msg["httpstatus"] == 200:
return train_tickets_msg["data"]["result"]
else:
return False
# 官网上能显示的我都整理出来了,比很多博主都详细
for i in check():
gf = i.split("|")
msg = {"车辆编号": gf[2], "车次": gf[3], "出发站": gf[6], "到达站": gf[7],
"出发时间": gf[8], "到达时间": gf[9], "历时": gf[10], "乘车时间": gf[13],
"商务座/特等座": gf[32] or "--",
"一等座": gf[31] or "--",
"二等座/二等包座": gf[30] or "--",
"高级软卧": gf[21] or "--",
"软卧/一等卧": gf[23] or "--",
"动卧": gf[33] or "--",
"硬卧/二等卧": gf[28] or "--",
"软座": gf[24] or "--",
"硬座": gf[29] or "--",
"无座": gf[26] or "--",
"其他": gf[22] or "--"}
再来看看登录吧:
2021-02-26日更新:登录信息优化,同时代码也比之前更简洁
现在图片需要base64转码了,我也用过打码平台的验证码校验,发现还挺准的,如果你不想手动输入验证码坐标,就用打码平台吧
get_v_code_url, check_v_code_url, login_url 分别代表验证码获取地址,校验验证码地址,登录地址,经测试发现,验证码校验方式由以前的POST方式变成了get了,这点需要注意,同时发现,如果验证码校验没有通过,是不会访问login_url的,所以必须要先验证完验证码,
最后就是登录了,经过多次尝试,单提交用户名密码这些信息还不够,现在正在找方法,先还是把我目前写的代码附上吧:
def login():
get_v_code_url = "https://kyfw.12306.cn/passport/captcha/captcha-image64"
check_v_code_url = "https://kyfw.12306.cn/passport/captcha/captcha-check"
login_url = "https://kyfw.12306.cn/passport/web/login"
# 步骤-:将base64编码的图片保存下来,手动输入验证码位置
req_v_code = request.get(url=get_v_code_url)
with open("code.jpg", "wb") as f:
f.write(base64.b64decode(req_v_code.json()['image']))
# 各验证码参考位置:
code_answer = {
1: (37, 47), 2: (121, 41), 3: (193, 45), 4: (268, 46),
5: (42, 111), 6: (117, 110), 7: (188, 113), 8: (263, 114),
}
v_code_ad = input("请输入验证码位置,多个位置用逗号隔开:")
data_check = {
'login_site': 'E', # 固定的
'rand': 'sjrand', # 固定的
'answer': v_code_ad # 验证码对应的坐标
}
# 步骤二:校验验证码信息
req_check_v_code = request.get(url=check_v_code_url, params=data_check).json()
# 步骤三:登录网站
if response.json()["result_code"] == "4":
username = str(input("请输入用户名:"))
userpassword = str(input("请输入密码:"))
data_2 = {
'appid':'otn',
'password': userpassword,
'username': username
}
response = request.post(login_url, data=data_2)
print(response.text)
登录和查票就到此结束了,如果有什么疑问可以留言哈