Raw Source
doupoa / 中控做表助手 - 抖音直播大屏数据导出

// ==UserScript==
// @name         中控做表助手 - 抖音直播大屏数据导出
// @namespace    doupoa.site
// @version      1.0.2
// @description  抖音专业版直播大屏数据导出。脚本启用后会在左侧logo旁出现"导出数据"字样。建议下播后再进行导出操作。开启的大屏必须从巨量百应中进入,巨量百应与抖音罗盘的数据大屏接口不同。导出的数据为:头部核心数据,流量统计及短视频引流。导出的数据仅供数据分析,不得用于非法行为。所有数据均在您浏览器中处理,不会上传至任何第三方平台,请自行解决数据保密问题,发生任何意外与本脚本及作者无关。
// @author       doupoa
// @copyright    2023,doupoa.site
// @license      MIT
// @updateURL    https://openuserjs.org/meta/doupoa/douyin_live_screen_data_export.meta.js
// @downloadURL https://openuserjs.org/install/doupoa/中控做表助手.user.js
// @match        *://compass.jinritemai.com/screen/live/*
// @icon         https://p1-ecda.byteimg.com/tos-cn-i-n15nrygpm8/563a38e870c441b8af75c0c8c38b1a39~tplv-n15nrygpm8-image.image
// @grant        none
// @run-at       document-end
// ==/UserScript==

// TODO : 关于大屏核心数据项获取并实现自动分配请求

(function () {
  "use strict";

  var roomID = "";
  var getFlowDistributionUrl =
    "https://compass.jinritemai.com/compass_api/content_live/author/live_screen/flow_distribution"; // 获取自然、付费流量数据接口
  var getFlowVideoUrl =
    "https://compass.jinritemai.com/compass_api/author/live/live_screen/flow_video"; // 获取引流视频数据接口
  var getCoreDataUrl =
    "https://compass.jinritemai.com/compass_api/author/live/live_screen/core_data"; //获取大屏核心数据接口
  var csvData = "";

  function getUrlParams() {
    const url = window.location.href;
    let urlStr = url.split("?")[1];
    const urlSearchParams = new URLSearchParams(urlStr);
    const result = Object.fromEntries(urlSearchParams.entries());
    return result;
  }

  function parseFlowDistributionData(data) {
    data = JSON.parse(data);
    return new Promise((resolve) => {
      let temp =
        ",\r\n渠道名称,流量占比,成交金额,成交金额占比,渠道千次观看成交,,注:自然流量\r\n";
      let nd = data.data.natural_data;
      let pd = data.data.pay_data;
      if (nd.length > 0) {
        for (let i = 0; i < nd.length; i++) {
          //遍历自然流量数据
          temp += nd[i].channel_name + ","; // 渠道名称
          temp += nd[i].watch_ratio.value * 100 + "%,"; //流量占比
          temp += "¥" + nd[i].pay_amt.value / 100 + ","; // 成交金额
          temp += nd[i].pay_ratio.value * 100 + "%,"; //成交金额占比
          temp += "¥" + nd[i].gpm.value / 100 + "\r\n"; //渠道千次观看成交
          if (nd[i].hasOwnProperty("sub_flow")) {
            //判断是否存在子数据
            for (let k = 0; k < nd[i].sub_flow.length; k++) {
              temp += " - " + nd[i].sub_flow[k].channel_name + ","; // 渠道名称
              temp += nd[i].sub_flow[k].watch_ratio.value * 100 + "%,"; //流量占比
              temp += "¥" + nd[i].sub_flow[k].pay_amt.value / 100 + ","; // 成交金额
              temp += nd[i].sub_flow[k].pay_ratio.value * 100 + "%,"; //成交金额占比
              temp += "¥" + nd[i].sub_flow[k].gpm.value / 100 + "\r\n"; //渠道千次观看成交
            }
          }
        }
      }
      else {
        temp += "暂无数据\r\n";
      }

      temp +=
        ",\r\n渠道名称,流量占比,成交金额,成交金额占比,渠道千次观看成交,,注:付费流量\r\n";
      if (pd.length > 0) {
        for (let i = 0; i < pd.length; i++) {
          //遍历自然流量数据
          temp += pd[i].channel_name + ","; // 渠道名称
          temp += pd[i].watch_ratio.value * 100 + "%,"; //流量占比
          temp += "¥" + pd[i].pay_amt.value / 100 + ","; // 成交金额
          temp += pd[i].pay_ratio.value * 100 + "%,"; //成交金额占比
          temp += "¥" + pd[i].gpm.value / 100 + "\r\n"; //渠道千次观看成交
          if (pd[i].hasOwnProperty("sub_flow")) {
            //判断是否存在子数据
            for (let k = 0; k < pd[i].sub_flow.length; k++) {
              temp += " - " + pd[i].sub_flow[k].channel_name + ","; // 渠道名称
              temp += pd[i].sub_flow[k].watch_ratio.value * 100 + "%,"; //流量占比
              temp += "¥" + pd[i].sub_flow[k].pay_amt.value / 100 + ","; // 成交金额
              temp += pd[i].sub_flow[k].pay_ratio.value * 100 + "%,"; //成交金额占比
              temp += "¥" + pd[i].sub_flow[k].gpm.value / 100 + "\r\n"; //渠道千次观看成交
            }
          }
        }
      }
      else {
        temp += "暂无数据\r\n";
      }
      csvData += temp;
      resolve();
    });
  }

  function parseFlowVideo(data) {
    data = JSON.parse(data);
    return new Promise((resolve) => {
      let temp =
        ",\r\n视频标题,发布时间,直播间入口曝光次数,引流直播间次数,直播间入口点击率(次数),,注:数据按引流直播间次数排序\r\n";
      let dr = data.data.data_result;
      if (dr.length > 0) {
        for (let i = 0; i < dr.length; i++) {
          temp += dr[i].title + ","; //标题
          temp += dr[i].publish_time + ","; //时间
          if (dr[i].show_in_live_cnt.includes("万")) {
            temp +=
              (dr[i].show_in_live_cnt.replace("万", "") - 0) * 10000 + ",";
          }
          else {
            temp += dr[i].show_in_live_cnt.replace(",", "") + ","; //直播入口曝光次数
          }
          temp += dr[i].drainage_in_live_cnt.replace(",", "") + ","; //引流直播间次数
          temp += dr[i].show_to_drainage_rate + ",\r\n"; //直播间入口点击率(次数)
        }
      }
      else {
        temp += "暂无数据\r\n";
      }
      csvData += temp;
      resolve();
    });
  }

  function parseCoreData(data, count) {
    data = JSON.parse(data);
    return new Promise((resolve, reject) => {
      let temp = "\r\n大屏核心数据 - 第" + count + "批\r\n";
      let cd = data.data.core_data;
      if (cd.length > 0) {
        for (let i = 0; i < cd.length; i++) {
          if (cd[i].index_display != "") {
            temp += cd[i].index_display + ", -> ,"; // 数据名称
          }
          else {
            temp += cd[i].index_name + ", -> ,"; // 数据名称
          }
          switch (cd[i].value.unit) {
            case "price": //金额
              temp += "¥" + cd[i].value.value + "\r\n";
              break;
            case "ratio": //百分数
              temp += cd[i].value.value * 100 + "%\r\n";
              break;
            default:
              temp += cd[i].value.value + "".replace(",", "") + "\r\n";
          }
        }
      }
      else {
        temp += "暂无第" + count + "批大屏数据";
      }
      csvData += temp;
      resolve();
    });
  }

  function doGet(url) {
    return new Promise((resolve, reject) => {
      let req = new XMLHttpRequest();
      req.open("Get", url);
      req.send();
      req.onload = function () {
        if (req.status == 200) {
          resolve(req.response);
        }
        else {
          reject(Error("Network Error"));
        }
      };
      req.onerror = function () {
        reject(Error("Network Error"));
      };
    });
  }

  async function getData() {
    await parseFlowDistributionData(
      await doGet(getFlowDistributionUrl + "?room_id=" + roomID)
    );
    await parseFlowVideo(
      await doGet(
        getFlowVideoUrl +
        "?room_id=" +
        roomID +
        "&page_no=1&page_size=10&sort_field=show_in_live_cnt"
      )
    );
    await parseCoreData(
      await doGet(
        getCoreDataUrl +
        "?room_id=" +
        roomID +
        "&index_selected=current_cnt,watch_cnt_show_ratio,watch_ucnt,live_show_cnt,avg_watch_duration,avg_min_comment_cnt,interact_watch_ucnt_ratio,follow_anchor_ucnt,follow_anchor_watch_ucnt_ratio,fans_club_join_ucnt"
      ),
      1
    );
    await parseCoreData(
      await doGet(
        getCoreDataUrl +
        "?room_id=" +
        roomID +
        "&index_selected=fans_club_join_watch_ucnt_ratio,ecom_live_ecf_joinclub_ucnt_td,ecf_club_join_watch_ucnt_ratio,pay_ucnt,gpm,watch_pay_ucnt_ratio,product_click_pay_ucnt_ratio,real_refund_amt,real_refund_amt_ratio"
      ),
      2
    );
    await parseCoreData(
      await doGet(
        getCoreDataUrl +
        "?room_id=" +
        roomID +
        "&index_selected=pay_deposit_pre_order_amt,pay_combo_cnt,old_fans_pay_ucnt_ratio,livetoind_pay_amt,stat_cost,punish_cnt"
      ),
      3
    );
    var newDate = new Date();
    export_csv(
      csvData,
      document.getElementsByClassName("accountName--jyOI0")[0].innerText +
      newDate.toLocaleDateString()
    );
  }

  function export_csv(data, name) {
    //导出表格
    // “\ufeff” BOM头
    var uri = "data:text/csv;charset=utf-8," + encodeURIComponent(data);
    var downloadLink = document.createElement("a");
    downloadLink.href = uri;
    downloadLink.download = name + ".csv" || "大屏数据导出.csv";
    document.body.appendChild(downloadLink);
    downloadLink.click();
    document.body.removeChild(downloadLink);
  }

  async function main() {
    // 尝试自动获取房间号
    let urlParams = getUrlParams();
    if ("live_room_id" in urlParams) {
      roomID = urlParams.live_room_id;
    }
    if (roomID == "") {
      // 自动获取失败尝试让用户手动输入房间号
      roomID = prompt("获取房间号失败,请手动输入房间号:");
    }
    if (roomID == "") {
      // 还是空直接终止脚本运行
      alert("关键数据获取失败,脚本终止运行");
      return;
    }
    getData();
  }

  var button = document.createElement("button");
  button.className =
    "ecom-btn ecom-btn-primary ecom-btn-sm reviewButton--DqvEZ";
  button.type = "button";
  button.textContent = "导出数据";
  button.addEventListener("click", main);

  window.setTimeout(() => {
    var header = document.getElementsByClassName("center--HdRS4")[0];
    header.appendChild(button);
  }, 3000);
})();