×

深度分析大麦网API接口,用Python脚本实现

万邦科技Lex 万邦科技Lex 发表于2025-08-18 16:57:22 浏览34 评论0

抢沙发发表评论

免费测试大麦网API接口

大麦网是国内领先的演出票务平台,涵盖演唱会、话剧、体育赛事等票务服务。由于大麦网未正式开放公共 API,以下分析基于其移动端 / 网页端的非官方接口(通过抓包分析),并提供 Python 调用方案。需注意:非官方 API 可能存在稳定性问题,且使用需遵守平台规则。

### 一、大麦网接口核心特性分析

#### 1. 接口体系与功能域

通过抓包分析,大麦网核心接口可分为以下几类:

-   **首页推荐**:获取热门演出、分类推荐;

-   **演出列表**:按城市、品类、时间筛选演出;

-   **演出详情**:获取演出时间、场馆、票价、座位图;

-   **购票相关**:库存查询、下单、支付;

-   **用户中心**:订单查询、收货地址。

#### 2. 认证与请求规范

-   **匿名接口**:首页推荐、演出列表等公开信息接口无需登录,仅需标准请求头;

-   **登录态接口**:购票、订单查询等需携带登录 Cookie(关键 Cookie 为`damai.cn`的`SESSION`和`userId`);

-   **请求头**:必需`User-Agent`(模拟移动端 / PC 端)、`Referer`(防盗链);

-   **参数加密**:部分接口参数经过简单加密(如时间戳 + 固定密钥 MD5),但公开信息接口参数多为明文。

#### 3. 典型接口参数与响应

以**演出列表接口**为例:

-   **请求 URL**:

-   **方法**:GET

-   **核心参数**:

    -   `city`:城市 ID(如北京`101010100`);

    -   `ctl`:品类(演唱会`1`、话剧`2`、体育`5`等);

    -   `page`:页码;

    -   `ts`:时间戳(毫秒级)。

-   **响应格式**:JSON,包含`totalCount`(总数)、`pageData`(演出列表,含 ID、名称、价格等)。

### 二、Python 脚本实现:大麦网接口调用框架

以下实现大麦网公开接口的调用,包括演出列表查询、演出详情获取,并处理基本的请求头和参数构造。

import requests

import json

import time

import logging

from typing import Dict, List, Optional

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: 演出列表(含ID、名称、价格等)

        """

        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()

            result = response.json()


            # 解析演出列表

            if result.get("status") == 1:

                performances = result.get("pageData", {}).get("resultData", [])

                logging.info(f"搜索到{len(performances)}个演出,关键词:{keyword},页码:{page}")

                return [

                    {

                        "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

                ]

            else:

                logging.error(f"搜索失败:{result.get('msg')}")

                return None


        except RequestException as e:

            logging.error(f"搜索请求异常:{str(e)}")

            return None


    def get_performance_detail(self, performance_id: str) -> Optional[Dict]:

        """

        获取演出详情(时间、场馆、票价等)

        :param performance_id: 演出ID(从搜索接口获取)

        :return: 演出详情字典

        """

        url = f"https://detail.damai.cn/item.htm?id={performance_id}"

        # 详情页接口实际通过HTML渲染,需解析页面中的JSON数据

        try:

            response = self.session.get(url, timeout=10)

            response.raise_for_status()

            html = response.text


            # 提取页面中的JSON数据(大麦网详情页数据嵌在window.__INITIAL_STATE__中)

            start = html.find("window.__INITIAL_STATE__ = ") + len("window.__INITIAL_STATE__ = ")

            end = html.find(";</script>", start)

            if start == -1 or end == -1:

                logging.error("无法提取演出详情数据")

                return None


            detail_json = json.loads(html[start:end])

            item_info = detail_json.get("itemDetail", {}).get("item", {})

            sku_list = detail_json.get("skuList", {}).get("skuList", [])  # 票价信息


            # 解析核心信息

            return {

                "id": performance_id,

                "name": item_info.get("name"),

                "poster": item_info.get("verticalPic"),  # 海报图

                "time": item_info.get("showTime"),

                "venue": item_info.get("venueName"),

                "address": item_info.get("venueAddress"),

                "price_list": [

                    {

                        "price": sku.get("price"),

                        "desc": sku.get("skuName"),  # 票价描述(如"内场VIP")

                        "stock": sku.get("stockStatusName")  # 库存状态

                    } for sku in sku_list

                ],

                "description": item_info.get("detailDesc")  # 演出简介

            }


        except RequestException as e:

            logging.error(f"详情请求异常:{str(e)}")

            return None

        except json.JSONDecodeError:

            logging.error("解析演出详情JSON失败")

            return None


    def get_city_list(self) -> Optional[List[Dict]]:

        """获取大麦网支持的城市列表(含城市ID)"""

        url = "https://city.damai.cn/js/cityjson.js"

        try:

            response = self.session.get(url, timeout=10)

            response.raise_for_status()

            # 解析城市数据(格式为var cityList = {...};)

            start = response.text.find("=") + 1

            end = response.text.rfind(";")

            city_data = json.loads(response.text[start:end])

            

            # 提取城市ID和名称

            return [

                {"id": city.get("id"), "name": city.get("name")}

                for city in city_data.get("cityList", [])

            ]

        except Exception as e:

            logging.error(f"获取城市列表失败:{str(e)}")

            return None


# 示例调用

if __name__ == "__main__":

    # 初始化客户端(默认北京,可通过get_city_list()获取其他城市ID)

    damai = DamaiAPI(city_id="101010100")  # 北京


    # 1. 搜索演出(示例:搜索"演唱会")

    performances = damai.search_performances(keyword="演唱会", category=1, page=1)

    if performances:

        print("演出列表:")

        for perf in performances[:3]:  # 打印前3个

            print(f"[{perf['id']}] {perf['name']} | {perf['time']} | {perf['venue']} | {perf['price']} | {perf['status']}")


    # 2. 获取演出详情(使用搜索结果中的第一个演出ID)

    if performances:

        first_perf_id = performances[0]["id"]

        detail = damai.get_performance_detail(first_perf_id)

        if detail:

            print(f"\n演出详情:{detail['name']}")

            print(f"时间:{detail['time']}")

            print(f"场馆:{detail['venue']}({detail['address']})")

            print("票价信息:")

            for price in detail["price_list"]:

                print(f"- {price['desc']}:{price['price']}元({price['stock']})")

 三、关键技术点解析

#### 1. 接口特点与反爬应对

-   **动态参数**:部分接口需携带时间戳(`ts`),需实时生成;

-   **HTML 内嵌数据**:演出详情数据嵌在页面的`window.__INITIAL_STATE__`中,需从 HTML 中提取 JSON;

-   **请求头模拟**:需设置真实的`User-Agent`(推荐移动端 UA)和`Referer`,否则可能返回 403;

-   **IP 限制**:频繁请求可能触发 IP 封禁,建议添加随机间隔(如`time.sleep(1-3秒)`)。

#### 2. 登录态处理(可选)

如需调用购票、订单等需登录的接口,需:

1.  手动登录大麦网,从浏览器 Cookie 中提取`SESSION`和`userId`;

1.  将 Cookie 添加到会话中:

    ```

    self.session.cookies.set("SESSION", "your_session_value")

    self.session.cookies.set("userId", "your_user_id")

    ```

1.  注意 Cookie 有效期(通常为 24 小时),需定期更新。

#### 3. 城市 ID 映射

大麦网城市 ID 为固定编码(如北京`101010100`、上海`101020100`),可通过`get_city_list()`方法获取完整列表,或手动查询对应城市 ID。

### 四、风险与注意事项

1.  **合规性**:非官方 API 可能违反大麦网用户协议,用于商业用途需谨慎;

1.  **稳定性**:接口地址、参数或响应格式可能随时变更,需定期维护;

1.  **反爬限制**:高频请求可能导致 IP 封禁,建议控制频率并使用代理池;

1.  **法律风险**:未经授权的大规模爬取可能涉及法律责任,建议仅用于个人学习。

通过上述框架,可实现大麦网公开演出信息的查询,适用于个人兴趣的演出监控、信息聚合等场景。实际使用中需根据接口变化及时调整参数和解析逻辑。

群贤毕至

访客