以下内容仅供交流学习使用!!!
思路分析
前面的爬虫笔记一步一步走过来我们的技术水平也有了较大的提升了,现在我们来进行一下票星球抢票实战项目,实现票星球的自动抢票。
- 我们打开票星球的移动端页面,分析每一步操作需要用到的接口。
- 先手动操作抢票找到抢票流程的每一个接口后,判断接口请求要求,请求方式,以及是否需要传递什么参数。
- 接下来就是每个接口一步一步操作请求响应,测试成功返回200状态码即可。
- 实现过程我主要是创建一个类对象,初始化方法配置需要用到的属性,然后把每一步操作的请求封装成每一个函数。
- 最后定义一个主函数运行实现逻辑。
- 当然,下面是代码的大体内容,主要讲解如何操作,需要全部代码可以找一下我发布的资源下载(目前文件还在审核中,通过后即可直接下载)。
示例代码
首先我们要用到下面三个包,pip下载即可
#pip install requests fake_useragent datetime
#请求
import requests
#随机UA
from fake_useragent import UserAgent
#时间
import datetime
然后我们创建一个类对象,初始化方法里面创建需要用到的属性变量,token属性需要自行登录找到自己的token复制过来即可:
class PXQ:
def __init__(self):
# 输入自己的token
self.token = '这里要打开网页开发者工具找到你自己的token'
# 演唱会项目id,必填,最简单就是在url上可以看到,下面这个是要操作抢购的演唱会的show_id
# 或者也是打开网页开发者工具中的网络也是可以找到
self.show_id = '665708481d06bc0001627d83'
# 指定场次id,不指定则默认从第一场开始遍历,查找同理
self.session_id = '665708656a025300012ae72a'
# 购票数量,自行设置要购买的数量,但一定要看购票须知,不要超过上限
self.buy_count = 1
# 指定观演人,观演人序号从0开始,人数需与票数保持一致
self.audience_idx = [0]
# audience_idx = [0]
# 门票类型,不确定则可以不填,让系统自行判断。快递送票:EXPRESS,电子票:E_TICKET/ID_CARD,现场取票:VENUE,电子票或现场取票:VENUE_E,目前只发现这四种,如有新发现可补充
self.deliver_method = ''
# 获取想要购买的票价id
self.seat_plan_id = ''
self.session_id_exclude = [] # 被排除掉的场次
self.price: int = 0
# 抢购的票价名称
self.seatPlanName = ''
self.threadsLists = []
# 判断是否对应价位有票数,默认是无
self.flag = False
# 设置抢票开始时间,自行设置开抢时间,分别是年月日时分秒
self.startTime = datetime.datetime(2024, 7, 3, 2, 0, 0)
# 随机UA
self.user_agent = UserAgent()
接下来就是操作流程每个请求接口设置,封装成方法,依次是:
- 根据项目id获取所有场次和在售状态
# 根据项目id获取所有场次和在售状态
def get_sessions(self, useragent):
headers = {
'User-Agent': useragent,
'Content-Type': 'application/json'
}
url = "https://m.piaoxingqiu.com/cyy_gatewayapi/show/pub/v3/show/" + self.show_id + "/sessions_dynamic_data"
response = requests.get(url=url, headers=headers).json()
if response["statusCode"] == 200:
# 获取场次的id
# print(response)
return response["data"]["sessionVOs"]
else:
print("get_sessions异常:" + str(response))
return None
- 根据场次id获取座位信息,箭头后面表示函数返回数据类型
def get_seat_plans(self, useragent) -> list:
headers = {
'User-Agent': useragent,
'Content-Type': 'application/json'
}
url = "https://m.piaoxingqiu.com/cyy_gatewayapi/show/pub/v3/show/" + self.show_id + "/show_session/" + self.session_id + "/seat_plans_static_data"
response = requests.get(url=url, headers=headers).json()
if response["statusCode"] == 200:
# 查看票价的id
# print(response)
return response["data"]["seatPlans"]
else:
raise Exception("get_seat_plans异常:" + str(response))
- 获取座位余票
# 获取座位余票
def get_seat_count(self, useragent) -> list:
headers = {
'User-Agent': useragent,
'Content-Type': 'application/json'
}
url = "https://m.piaoxingqiu.com/cyy_gatewayapi/show/pub/v3/show/" + self.show_id + "/show_session/" + self.session_id + "/seat_plans_dynamic_data"
response = requests.get(url=url, headers=headers).json()
if response["statusCode"] == 200:
return response["data"]["seatPlans"]
else:
raise Exception("get_seat_count异常:" + str(response))
- 获取门票类型(快递送票EXPRESS,电子票E_TICKET,现场取票VENUE,电子票或现场取票VENUE_E)
# 获取门票类型(快递送票EXPRESS,电子票E_TICKET,现场取票VENUE,电子票或现场取票VENUE_E)
def get_deliver_method(self, useragent, qty: int) -> str:
headers = {
'User-Agent': useragent,
'Content-Type': 'application/json',
'access-token': self.token
}
data = {
"items": [
{
"skus": [
{
"seatPlanId": self.seat_plan_id,
"sessionId": self.session_id,
"showId": self.show_id,
"skuId": self.seat_plan_id,
"skuType": "SINGLE",
"ticketPrice": self.price,
"qty": qty #数量
}
],
"spu": {
"id": self.show_id,
"spuType": "SINGLE"
}
}
]
}
url = "https://m.piaoxingqiu.com/cyy_gatewayapi/trade/buyer/order/v3/pre_order"
response = requests.post(url=url, headers=headers, json=data).json()
if response["statusCode"] == 200:
# 这里的print可以去掉
# print(response["data"]["supportDeliveries"][0])
return response["data"]["supportDeliveries"][0]["name"]
else:
raise Exception("获取门票类型异常:" + str(response))
- 获取观演人信息
# 获取观演人信息
# def get_audiences() -> list | None:
def get_audiences(self, useragent):
headers = {
'User-Agent': useragent,
'Content-Type': 'application/json',
'access-token': self.token
}
url = "https://m.piaoxingqiu.com/cyy_gatewayapi/user/buyer/v3/user_audiences"
response = requests.get(url=url, headers=headers).json()
if response["statusCode"] == 200:
# 可以获取观影人的信息查看是否正确
return response["data"]
else:
print("get_audiences异常:" + str(response))
return None
- 提交订单需要做的判断比较多,要判断演出票是什么票型(快递送票EXPRESS,电子票E_TICKET/ID_Card,现场取票VENUE,电子票或现场取票VENUE_E)
# 提交订单(快递送票EXPRESS,电子票E_TICKET/ID_Card,现场取票VENUE,电子票或现场取票VENUE_E)
def create_order(self, useragent, qty: int, express_fee: int,
receiver, cellphone, address_id, detail_address, location_city_id, audience_ids: list):
headers = {
'User-Agent': useragent,
'Content-Type': 'application/json',
'access-token': self.token
}
if self.deliver_method == "EXPRESS":
data = {
"priceItemParam": [
{
"applyTickets": [],
"priceItemName": "票款总额",
"priceItemVal": self.price * qty,
"priceItemType": "TICKET_FEE",
"priceItemSpecies": "SEAT_PLAN",
"direction": "INCREASE",
"priceDisplay": "¥" + str(self.price * qty)
},
{
"applyTickets": [],
"priceItemName": "快递费",
"priceItemVal": express_fee,
"priceItemId": self.show_id,
"priceItemSpecies": "SEAT_PLAN",
"priceItemType": "EXPRESS_FEE",
"direction": "INCREASE",
"priceDisplay": "¥" + str(express_fee)
}
],
"items": [
{
"skus": [
{
"seatPlanId": self.seat_plan_id,
"sessionId": self.session_id,
"showId": self.show_id,
"skuId": self.seat_plan_id,
"skuType": "SINGLE",
"ticketPrice": self.price,
"qty": qty,
"deliverMethod": self.deliver_method
}
],
"spu": {
"id": self.show_id,
"spuType": "SINGLE"
}
}
],
"contactParam": {
"receiver": receiver, # 张三
"cellphone": cellphone # 13812345678
},
"one2oneAudiences": [{"audienceId": i, "sessionId": self.session_id} for i in audience_ids],
"addressParam": {
"address": detail_address, # 星巴克咖啡门口
"district": location_city_id[4:],
"city": location_city_id[2:4],
"province": location_city_id[0:2],
"addressId": address_id
}
}
elif self.deliver_method == "ID_CARD":
data = {
"priceItemParam": [
{
"applyTickets": [],
"priceItemName": "票款总额",
"priceItemVal": self.price * qty,
"priceItemType": "TICKET_FEE",
"priceItemSpecies": "SEAT_PLAN",
"direction": "INCREASE",
"priceDisplay": "¥" + str(self.price * qty),
}
],
"items": [
{
"skus": [
{
"seatPlanId": self.seat_plan_id,
"sessionId": self.session_id,
"showId": self.show_id,
"skuId": self.seat_plan_id,
"skuType": "SINGLE",
"ticketPrice": self.price,
"qty": qty,
"deliverMethod": self.deliver_method,
}
],
"spu": {"id": self.show_id, "spuType": "SINGLE"},
}
],
"one2oneAudiences": [
{"audienceId": i, "sessionId": self.session_id} for i in audience_ids
],
"many2OneAudience": {
"audienceId": audience_ids[0],
"sessionIds": [self.session_id],
},
}
# 电子票已解决
elif self.deliver_method == "E_TICKET":
data = {
"priceItemParam": [
{
"applyTickets": [],
"priceItemName": "票款总额",
"priceItemVal": self.price * qty,
"priceItemType": "TICKET_FEE",
"priceItemSpecies": "SEAT_PLAN",
"direction": "INCREASE",
"priceDisplay": "¥" + str(self.price * qty)
}
],
"items": [
{
"skus": [
{
"seatPlanId": self.seat_plan_id,
"sessionId": self.session_id,
"showId": self.show_id,
"skuId": self.seat_plan_id,
"skuType": "SINGLE",
"ticketPrice": self.price,
"qty": qty,
"deliverMethod": self.deliver_method
}
],
"spu": {
"id": self.show_id,
"spuType": "SINGLE"
}
}
],
"many2OneAudience": {
"audienceId": audience_ids[0],
"sessionIds": [
self.session_id
]
}
}
elif self.deliver_method == "VENUE":
data = {
"priceItemParam": [
{
"applyTickets": [],
"priceItemName": "票款总额",
"priceItemVal": self.price * qty,
"priceItemType": "TICKET_FEE",
"priceItemSpecies": "SEAT_PLAN",
"direction": "INCREASE",
"priceDisplay": "¥" + str(self.price * qty)
}
],
"items": [
{
"skus": [
{
"seatPlanId": self.seat_plan_id,
"sessionId": self.session_id,
"showId": self.show_id,
"skuId": self.seat_plan_id,
"skuType": "SINGLE",
"ticketPrice": self.price,
"qty": qty,
"deliverMethod": self.deliver_method
}
],
"spu": {
"id": self.show_id,
"spuType": "SINGLE"
}
}
],
"one2oneAudiences": [{"audienceId": i, "sessionId": self.session_id} for i in audience_ids]
}
elif self.deliver_method == "VENUE_E":
data = {
"priceItemParam": [
{
"applyTickets": [],
"priceItemName": "票款总额",
"priceItemVal": self.price * qty,
"priceItemType": "TICKET_FEE",
"priceItemSpecies": "SEAT_PLAN",
"direction": "INCREASE",
"priceDisplay": "¥" + str(self.price * qty)
}
],
"items": [
{
"skus": [
{
"seatPlanId": self.seat_plan_id,
"sessionId": self.session_id,
"showId": self.show_id,
"skuId": self.seat_plan_id,
"skuType": "SINGLE",
"ticketPrice": self.price,
"qty": qty,
"deliverMethod": self.deliver_method
}
],
"spu": {
"id": self.show_id,
"spuType": "SINGLE"
}
}
]
}
else:
raise Exception("不支持的deliver_method:" + str(self.deliver_method))
url = "https://m.piaoxingqiu.com/cyy_gatewayapi/trade/buyer/order/v3/create_order"
response = requests.post(url=url, headers=headers, json=data).json()
if response["statusCode"] == 200:
print("下单成功!请尽快支付!")
else:
raise Exception("下单异常:" + str(response))
- 主函数,前面都是函数定义,主要是在主函数执行操作抢票。
- 我们前面已经知道哪个演唱会show_id,以及哪个场次session_id,现在要在主函数里面的seat_plan_id进行自行赋值,也可以开头初始化方法里面赋值/默认为空,seat_plan_id可以通过前面get_seat_plans函数里的接口地址获取。
# 主函数
def main(self):
# 创建随机的useragent
useragent = self.user_agent.random
while True:
try:
# 获取座位余票信息,默认从最低价开始
seat_plans = self.get_seat_plans(useragent)
# print(seat_plans)
seat_count = self.get_seat_count(useragent)
# print(seat_count)
# 获取想要购买的票价id
self.seat_plan_id = '667ba34a090ca100015604df'
# 提取出对应的票价
for temp in seat_plans:
if temp["seatPlanId"] == self.seat_plan_id:
self.price = temp["originalPrice"]
self.seatPlanName = temp["seatPlanName"]
break
# 判断对应的票价id是否有剩余票数
for i in seat_count:
if i["seatPlanId"] == self.seat_plan_id:
if i["canBuyCount"] > 0:
self.flag = True
# print(f'{self.seatPlanName}该价位剩余数量:可满足您的购买需求')
break
else:
self.flag = False
# print(f'{self.seatPlanName}无剩余!')
break
if self.flag:
print(f'{self.seatPlanName}该价位剩余数量:可满足您的购买需求')
print('正在努力抢购中……')
else:
print(f'{self.seatPlanName}票价无剩余!,将继续为你刷新抢购')
continue
if not self.deliver_method:
self.deliver_method = self.get_deliver_method(useragent, self.buy_count)
print("演唱票类型:" + self.deliver_method)
if self.deliver_method == "VENUE_E":
self.create_order(useragent, self.buy_count, 0, None, None, None, None, None, [])
else:
# 获取观演人信息
audiences = self.get_audiences(useragent)
if len(self.audience_idx) == 0:
self.audience_idx = range(self.buy_count)
audience_ids = [audiences[i]["id"] for i in self.audience_idx]
if self.deliver_method == "EXPRESS":
# 获取默认收货地址
address = self.get_address(useragent)
address_id = address["addressId"] # 地址id
location_city_id = address["locationId"] # 460102
receiver = address["username"] # 收件人
cellphone = address["cellphone"] # 电话
detail_address = address["detailAddress"] # 详细地址
# 获取快递费用
express_fee = self.get_express_fee(useragent, self.buy_count, location_city_id)
self.create_order(useragent, self.buy_count, express_fee["priceItemVal"], receiver,
cellphone, address_id, detail_address, location_city_id, audience_ids)
elif self.deliver_method == "VENUE" or self.deliver_method == "E_TICKET" or self.deliver_method == "ID_CARD":
self.create_order(useragent, self.buy_count, 0, None, None, None, None, None, audience_ids)
else:
print("不支持的deliver_method:" + self.deliver_method)
break
except Exception as e:
print(e)
以上内容全是定义在一个类中,我们要使用那就要创建一个类对象,现在我们打开一个文件入口进行创建:
if __name__ == '__main__':
# 创建对象
pxq = PXQ()
while True:
now = datetime.datetime.now()
# 判断当前时间与设置的开抢时间,并进行倒计时,当当前时间大于开抢时间直接开抢
if now < pxq.startTime:
print(f"{int(pxq.startTime.timestamp()-now.timestamp())}秒后开抢", end="\r")
else:
pxq.main()
break
执行前我们配置好show_id,session_id和seat_plan_id,运行起来根据你设置的抢购时间在等待抢购的:
获取seat_plan_id的流程操作:
抢票成功会显示:
以上是大体的思路流程,详细的代码可以看下我发布的资源,或者找我上传到github上的资源下载。