大麦网作为国内领先的演出票务平台,其演出详情数据(如场次信息、票价、剩余票量、演出介绍等)是票务分析、比价工具、粉丝服务平台的核心数据源。由于大麦网未公开官方开放平台,开发者需通过合理合规的方式对接数据接口。本文将系统讲解大麦网详情数据的获取逻辑、接口对接流程、反爬应对及最佳实践,帮助开发者从入门到精通,构建稳定的演出详情获取系统。
一、接口基础认知(非官方接口特性)
二、对接前置准备
三、接口调用流程(模拟请求方案)
四、代码实现示例(Python 模拟请求)
import requests
import time
import random
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
import json
import redis
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class DamaiItemApi:
def __init__(self, redis_host="localhost", redis_port=6379):
# 初始化代理池与Cookie存储(Redis)
self.redis_client = redis.Redis(host=redis_host, port=redis_port, decode_responses=True)
self.ua = UserAgent()
self.session = self._init_session()
# 接口URL模板
self.detail_api = "https://detail.damai.cn/ajax/detail?itemId={item_id}"
self.ticket_api = "https://detail.damai.cn/ajax/ticketPrice?itemId={item_id}"
def _init_session(self):
"""初始化请求会话,配置重试与超时"""
session = requests.Session()
retry = Retry(total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503])
session.mount("https://", HTTPAdapter(max_retries=retry))
return session
def _get_headers(self):
"""生成随机请求头"""
return {
"User-Agent": self.ua.random,
"Referer": "https://search.damai.cn/search.htm",
"Accept": "application/json, text/plain, */*",
"Cookie": self._get_cookie(), # 从Redis获取Cookie
"Connection": "keep-alive"
}
def _get_cookie(self):
"""从Redis获取或刷新Cookie"""
cookie = self.redis_client.get("damai_cookie")
if not cookie:
# 模拟访问首页获取新Cookie
self.session.get("https://www.damai.cn/", headers={"User-Agent": self.ua.random})
cookie = "; ".join([f"{k}={v}" for k, v in self.session.cookies.items()])
self.redis_client.setex("damai_cookie", 3600, cookie) # 缓存1小时
return cookie
def _get_proxy(self):
"""从代理池获取代理(示例:实际需搭建代理池)"""
proxies = self.redis_client.lrange("proxy_pool", 0, -1)
if proxies:
return {"https": random.choice(proxies)}
return None # 无代理时直连
def item_get(self, item_id):
"""
获取演出详情数据
:param item_id: 演出ID(如6987654321)
:return: 标准化的演出数据
"""
try:
# 1. 获取基础信息
headers = self._get_headers()
proxies = self._get_proxy()
detail_url = self.detail_api.format(item_id=item_id)
# 发送请求(带随机延迟)
time.sleep(random.uniform(1, 3))
detail_resp = self.session.get(detail_url, headers=headers, proxies=proxies, timeout=10)
detail_resp.raise_for_status()
detail_data = detail_resp.json()
# 检查基础信息是否有效
if detail_data.get("status") != 0:
return {"success": False, "msg": "基础信息接口返回异常"}
# 2. 获取票价与场次信息
ticket_url = self.ticket_api.format(item_id=item_id)
time.sleep(random.uniform(1, 2))
ticket_resp = self.session.get(ticket_url, headers=headers, proxies=proxies, timeout=10)
ticket_resp.raise_for_status()
ticket_data = ticket_resp.json()
if ticket_data.get("status") != 0:
return {"success": False, "msg": "票价接口返回异常"}
# 3. 标准化数据
base_info = detail_data["result"]["baseInfo"]
show_info = detail_data["result"]["showInfo"]
ticket_info = ticket_data["result"]
standardized = {
"item_id": item_id,
"title": base_info.get("name"),
"category": base_info.get("categoryName"), # 演出类型
"poster": base_info.get("verticalPic"), # 主图
"intro": self._parse_intro(base_info.get("description")), # 演出简介
"venue": {
"name": show_info.get("venueName"),
"address": show_info.get("venueAddress")
},
"shows": self._parse_shows(show_info.get("showList", [])), # 场次列表
"tickets": self._parse_tickets(ticket_info.get("priceList", [])) # 票价信息
}
return {"success": True, "data": standardized}
except requests.exceptions.HTTPError as e:
# 处理403等反爬响应,刷新Cookie和代理
if "403" in str(e) or "503" in str(e):
self.redis_client.delete("damai_cookie") # 清除无效Cookie
return {"success": False, "msg": "触发反爬,已刷新Cookie", "retry": True}
return {"success": False, "msg": f"HTTP错误: {str(e)}"}
except Exception as e:
return {"success": False, "msg": f"获取失败: {str(e)}"}
def _parse_intro(self, html):
"""解析演出简介(去除HTML标签)"""
if not html:
return ""
soup = BeautifulSoup(html, "lxml")
return soup.get_text().strip()
def _parse_shows(self, show_list):
"""解析场次信息"""
shows = []
for show in show_list:
shows.append({
"show_id": show.get("id"),
"date": show.get("showDate"), # 日期
"time": show.get("showTime"), # 时间
"status": show.get("statusDesc") # 状态(在售/售罄等)
})
return shows
def _parse_tickets(self, price_list):
"""解析票价信息"""
tickets = []
for ticket in price_list:
tickets.append({
"ticket_id": ticket.get("priceId"),
"name": ticket.get("priceName"), # 票价档位(如VIP)
"original_price": ticket.get("originalPrice"), # 原价
"current_price": ticket.get("price"), # 现价
"stock": self._estimate_stock(ticket.get("stockStatus")), # 库存状态
"limit": ticket.get("limitNum") # 限购数量
})
return tickets
def _estimate_stock(self, stock_status):
"""估算剩余票量(大麦网不直接返回具体数量)"""
status_map = {
"售罄": 0,
"可售": "较多",
"少量": "少量",
"即将售罄": "极少"
}
return status_map.get(stock_status, "未知")
# 使用示例
if __name__ == "__main__":
# 初始化API客户端(需本地Redis服务存储Cookie和代理)
api = DamaiItemApi()
# 获取演出详情(示例item_id:周杰伦演唱会)
item_id = "6987654321"
result = api.item_get(item_id)
if result["success"]:
data = result["data"]
print(f"演出标题: {data['title']}")
print(f"演出类型: {data['category']}")
print(f"场馆: {data['venue']['name']} ({data['venue']['address']})")
print(f"场次数量: {len(data['shows'])}")
print(f"票价档位: {[t['name'] for t in data['tickets']]}")
print(f"主图地址: {data['poster']}")
else:
print(f"获取失败: {result['msg']}")
if result.get("retry"):
print("建议重试...")