在票务数据采集、演出信息监控等开发场景中,大麦网作为国内领先的演出票务平台,其数据接口具有极高的应用价值。但需明确的是,大麦网未正式开放公共API,当前开发者使用的接口均来自移动端/网页端的抓包分析(非官方接口)。本文将聚焦核心的关键字搜索列表API 和详情数据API,从接口规范、参数解析、Python调用实现到风险提示进行完整梳理,助力开发者合规高效地获取演出数据。
一、前置认知:大麦网API核心特性
在开始接口调用前,需先掌握大麦网接口的基础特性,避免因参数缺失或格式错误导致调用失败:
-
认证机制 :公开信息接口(如搜索列表、演出详情)无需登录态,仅需配置标准请求头;涉及购票、订单等敏感操作的接口需携带登录Cookie(核心为
damai.cn域名下的SESSION和userId)。 -
请求规范 :必须携带
User-Agent(模拟移动端/PC端)和Referer(防盗链);部分接口需传入毫秒级时间戳(ts参数),用于防缓存和请求验证。 -
响应格式 :所有接口均返回JSON格式数据,包含状态码(
status)、提示信息(msg)和核心数据(如pageData)三大核心字段。 -
稳定性风险:非官方接口可能随时调整参数格式或接口地址,建议做好异常捕获和动态适配;同时需控制请求频率,避免IP被封禁。
二、核心接口解析
本文重点解析两个最常用的公开接口:关键字搜索列表API (获取符合关键词的演出列表)和详情数据API(获取单场演出的详细信息),以下均为抓包验证后的可用接口规范。
2.1 关键字搜索列表API
该接口用于根据关键词、城市、品类等条件搜索演出,返回分页的演出列表数据(含演出ID、名称、时间、场馆等基础信息)。
2.1.1 接口信息
-
请求URL:
https://search.damai.cn/searchajax.html -
请求方法:GET
-
是否需要登录:否
2.1.2 核心请求参数
| 参数名 | 类型 | 是否必须 | 示例值 | 描述 |
|---|---|---|---|---|
| city | String | 是 | 101010100 | 城市ID,北京为101010100,其他城市需查询对应ID映射表 |
| keyword | String | 否 | 周杰伦 | 搜索关键词,为空时返回全品类演出 |
| ctl | Int | 否 | 1 | 演出品类:1-演唱会、2-话剧、5-体育赛事、0-全部 |
| page | Int | 是 | 1 | 分页页码,从1开始 |
| ts | Int | 是 | 1736700000000 | 毫秒级时间戳,用于请求有效性验证 |
| order | Int | 否 | 1 | 排序方式:1-热门排序、2-时间排序 |
2.1.3 响应数据解析
响应示例(精简):
{ "status": 1, "msg": "success", "pageData": { "totalCount": 5, "resultData": [ { "id": "12345678", "name": "周杰伦2026世界巡回演唱会-北京", "categoryName": "演唱会", "showTime": "2026-06-15 19:30", "venueName": "国家体育场(鸟巢)", "priceStr": "880-2280元", "statusName": "预售" }, // 更多演出... ] } }
核心字段说明:
-
status:1表示请求成功,0表示失败(失败原因见msg字段)。
-
totalCount:符合条件的演出总数,用于分页计算。
-
resultData:演出列表数组,每个元素包含单场演出的基础信息(id为演出唯一标识,用于调用详情API)。
2.2 详情数据API
该接口通过演出ID获取单场演出的详细信息,包括演出介绍、场次安排、票价明细、取票规则等核心数据。
2.2.1 接口信息
-
请求URL:
https://detail.damai.cn/item.htm?id={演出ID} -
请求方法:GET
-
是否需要登录:否
2.2.2 核心请求参数
| 参数名 | 类型 | 是否必须 | 示例值 | 描述 |
|---|---|---|---|---|
| id | String | 是 | 12345678 | 演出唯一ID(从搜索列表API的resultData.id获取) |
补充说明:该接口为网页端详情页接口,返回的是HTML内容,核心数据嵌入在页面的JSON脚本中(key为window.__INITIAL_STATE__),需通过解析HTML提取JSON数据。
2.2.3 响应数据解析
步骤1:获取HTML后,提取window.__INITIAL_STATE__对应的JSON字符串;步骤2:解析JSON得到核心数据,关键字段如下:
-
projectDetail:演出基础信息(名称、分类、演出时间、场馆地址等)。
-
performList:场次列表(每场的时间、票价、剩余库存、售票状态等)。
-
notice:购票须知(儿童政策、取票规则、退票规则等)。
-
artistList:艺人信息(适用于演唱会、话剧等有明确艺人的演出)。
三、Python实战:完整调用实现
以下实现基于Python3.8+,使用requests库发送请求,lxml库解析HTML,封装为DamaiAPI类,包含关键字搜索和详情获取两个核心方法。
3.1 环境准备
安装依赖库:
pip install requests lxml logging
3.2 完整代码实现
import requests import json import time import logging from typing import Dict, List, Optional from lxml import etree from requests.exceptions import RequestException # 配置日志 logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) class DamaiAPI: def __init__(self, city_id: str = "101010100"): """ 初始化大麦网API客户端 :param city_id: 城市ID(默认北京:101010100,其他城市需查询对应ID) """ self.city_id = city_id # 模拟移动端请求头,提升兼容性 self.headers = { "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Damai/7.2.0", "Referer": "https://m.damai.cn/", "Origin": "https://m.damai.cn", "Accept": "application/json, text/plain, */*" } # 会话保持,处理Cookie self.session = requests.Session() self.session.headers.update(self.headers) def _get_timestamp(self) -> int: """生成毫秒级时间戳""" return int(time.time() * 1000) def search_performances(self, keyword: str = "", category: int = 0, page: int = 1) -> Optional[List[Dict]]: """ 关键字搜索演出列表 :param keyword: 搜索关键词(如"周杰伦") :param category: 品类(1:演唱会,2:话剧,5:体育,0:全部) :param page: 页码 :return: 格式化后的演出列表,失败返回None """ url = "https://search.damai.cn/searchajax.html" params = { "city": self.city_id, "keyword": keyword, "ctl": category, "page": page, "ts": self._get_timestamp(), "stype": 0, "order": 1 # 1:热门排序,2:时间排序 } try: response = self.session.get(url, params=params, timeout=10) response.raise_for_status() # 抛出HTTP请求异常 result = response.json() # 解析并格式化演出数据 if result.get("status") == 1: performances = result.get("pageData", {}).get("resultData", ()) if not performances: logging.info(f"未搜索到符合条件的演出,关键词:{keyword},页码:{page}") return [] # 提取核心字段,返回格式化列表 formatted_data = [ { "id": item.get("id"), "name": item.get("name"), "category": item.get("categoryName"), "time": item.get("showTime"), "venue": item.get("venueName"), "price": item.get("priceStr"), "status": item.get("statusName") # 售票状态:预售/在售/售罄 } for item in performances ] logging.info(f"搜索成功,关键词:{keyword},页码:{page},共{len(formatted_data)}场演出") return formatted_data else: logging.error(f"搜索失败:{result.get('msg')}") return None except RequestException as e: logging.error(f"搜索请求异常:{str(e)}") return None except json.JSONDecodeError as e: logging.error(f"响应数据解析失败:{str(e)}") return None def get_performance_detail(self, performance_id: str) -> Optional[Dict]: """ 获取演出详情 :param performance_id: 演出ID(从search_performances方法获取) :return: 格式化后的演出详情,失败返回None """ url = f"https://detail.damai.cn/item.htm?id={performance_id}" try: response = self.session.get(url, timeout=10) response.raise_for_status() response.encoding = "utf-8" # 设置编码,避免中文乱码 # 解析HTML,提取window.__INITIAL_STATE__对应的JSON数据 html = etree.HTML(response.text) script_content = html.xpath('//script[contains(text(), "window.__INITIAL_STATE__")]/text()') if not script_content: logging.error(f"未找到核心数据脚本,演出ID:{performance_id}") return None # 提取JSON字符串(截取window.__INITIAL_STATE__ = 后面的内容,去掉末尾的;) json_str = script_content[0].split("window.__INITIAL_STATE__ = ")[1].rstrip(";") detail_data = json.loads(json_str) # 格式化详情数据,提取关键信息 formatted_detail = { "performance_id": performance_id, "name": detail_data.get("projectDetail", {}).get("name"), "category": detail_data.get("projectDetail", {}).get("categoryName"), "show_time": detail_data.get("projectDetail", {}).get("showTime"), "venue": { "name": detail_data.get("projectDetail", {}).get("venueName"), "address": detail_data.get("projectDetail", {}).get("venueAddress") }, "performances": [ { "perform_id": item.get("performId"), "time": item.get("performTime"), "price_list": item.get("priceList", []), # 包含票价、库存等信息 "status": item.get("statusName") } for item in detail_data.get("performList", []) ], "ticket_notice": detail_data.get("notice", {}).get("ticketNotice"), # 购票须知 "artist_list": detail_data.get("artistList", []) # 艺人列表 } logging.info(f"详情获取成功,演出ID:{performance_id}") return formatted_detail except RequestException as e: logging.error(f"详情请求异常:{str(e)},演出ID:{performance_id}") return None except json.JSONDecodeError as e: logging.error(f"详情数据解析失败:{str(e)},演出ID:{performance_id}") return None except Exception as e: logging.error(f"详情获取未知异常:{str(e)},演出ID:{performance_id}") return None # 测试代码 if __name__ == "__main__": # 初始化客户端(默认北京,可替换为其他城市ID) damai_api = DamaiAPI(city_id="101010100") # 1. 搜索演出(关键词:周杰伦,品类:演唱会) search_result = damai_api.search_performances(keyword="周杰伦", category=1, page=1) if not search_result: print("搜索无结果") exit() # 2. 获取第一个演出的详情 first_performance_id = search_result[0]["id"] detail_result = damai_api.get_performance_detail(performance_id=first_performance_id) # 打印结果 print("\n=== 搜索结果 ===") print(json.dumps(search_result, ensure_ascii=False, indent=2)) print("\n=== 演出详情 ===") print(json.dumps(detail_result, ensure_ascii=False, indent=2))
3.3 代码说明
-
封装性:将请求头、会话管理、时间戳生成等通用逻辑封装在DamaiAPI类中,降低复用成本。
-
异常处理:捕获HTTP请求异常、JSON解析异常等,通过日志输出错误信息,便于问题排查。
-
数据格式化:提取核心字段,过滤冗余数据,返回结构化的字典/列表,便于后续业务处理。
-
测试代码:提供了完整的测试流程,可直接运行验证接口调用效果。
四、关键注意事项(必看)
本文涉及的接口均为非官方接口,使用前需严格遵守以下规则,避免法律风险和技术问题:
-
合规性优先:仅用于个人学习、研究,不得用于商业用途;不得大量抓取数据,避免侵犯大麦网的知识产权和数据权益。
-
控制请求频率:建议在请求中加入延时(如time.sleep(1)),避免短时间内高频请求导致IP被封禁。
-
接口适配:非官方接口可能随时变更,若出现调用失败,需重新抓包分析最新的接口地址和参数格式。
-
官方替代方案 :若需商业使用,建议联系大麦网商务团队申请官方合作,通过阿里开放平台获取正规API(如分销项目详情接口
alibaba.damai.maitix.project.distribution.detail.query)。
五、总结
本文详细解析了大麦网关键字搜索列表API和详情数据API的核心规范,提供了可直接运行的Python调用代码。通过抓包分析非官方接口实现数据获取的方式,适合开发者进行技术研究和学习。但需重点强调:非官方接口存在天然的不稳定性和合规风险,商业场景下务必优先选择官方合作渠道。
若接口发生变更,可通过浏览器开发者工具(F12)重新抓包:切换到Network面板,筛选XHR/JS请求,找到包含搜索关键词或演出ID的请求,分析其参数和响应格式,即可完成适配调整。
欢迎在评论区交流接口适配经验或问题,共同完善技术方案!
