🏠 Home 

商品_细分市场_商品洞察_商品价位段分析

云图扩展工具

  1. // ==UserScript==
  2. // @name 商品_细分市场_商品洞察_商品价位段分析
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description 云图扩展工具
  6. // @author siji-Xian
  7. // @match *://yuntu.oceanengine.com/yuntu_brand/product/segmentedMarketList?*
  8. // @icon https://lf3-static.bytednsdoc.com/obj/eden-cn/prhaeh7pxvhn/yuntu/yuntu-logo_default.svg
  9. // @grant none
  10. // @license MIT
  11. // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.2.1/jquery.min.js
  12. // @require https://cdn.bootcss.com/moment.js/2.20.1/moment.min.js
  13. // @require https://greasyfork.org/scripts/404478-jsonexportexcel-min/code/JsonExportExcelmin.js?version=811266
  14. // @require https://greasyfork.org/scripts/455576-qmsg/code/Qmsg.js?version=1122361
  15. // ==/UserScript==
  16. (function () {
  17. "use strict";
  18. var new_element = document.createElement("link");
  19. new_element.setAttribute("rel", "stylesheet");
  20. new_element.setAttribute("href", "https://qmsg.refrain.xyz/message.min.css");
  21. document.body.appendChild(new_element);
  22. let button = document.createElement("button"); //创建一个按钮
  23. button.textContent = "导出数据(商品洞察)";
  24. Object.assign(button.style, {
  25. height: "34px",
  26. lineHeight: "34px",
  27. alignItems: "center",
  28. color: "white",
  29. border: "none",
  30. background: "linear-gradient(90deg, rgba(0, 239, 253), rgba(64, 166, 254))",
  31. borderRadius: "5px",
  32. marginLeft: "10px",
  33. fontSize: "13px",
  34. padding: "0 10px",
  35. cursor: "pointer",
  36. fontWeight: "500",
  37. });
  38. button.addEventListener("click", urlClick); //监听按钮点击事件
  39. const getRequestOptions = {
  40. method: "GET",
  41. redirect: "follow",
  42. };
  43. //获取brand信息
  44. let brand = localStorage.getItem("__Garfish__platform__yuntu_user") || "";
  45. let brands = JSON.parse(brand);
  46. //获取industry_id
  47. let industry_id = null;
  48. (function listen() {
  49. var origin = {
  50. open: XMLHttpRequest.prototype.open,
  51. send: XMLHttpRequest.prototype.send,
  52. };
  53. XMLHttpRequest.prototype.open = function (a, b) {
  54. this.addEventListener("load", replaceFn);
  55. origin.open.apply(this, arguments);
  56. };
  57. XMLHttpRequest.prototype.send = function (a, b) {
  58. origin.send.apply(this, arguments);
  59. };
  60. function replaceFn(obj) {
  61. if (
  62. this?._url?.slice(0, 42) == "/yuntu_ng/api/v1/get_brand_competitor_list"
  63. ) {
  64. industry_id = JSON.parse(obj?.target?.response).data[0].industry_id;
  65. }
  66. }
  67. })();
  68. function getQueryVariable(variable) {
  69. var query = window.location.search.substring(1);
  70. var vars = query.split("&");
  71. for (var i = 0; i < vars.length; i++) {
  72. var pair = vars[i].split("=");
  73. if (pair[0] == variable) {
  74. return pair[1];
  75. }
  76. }
  77. return false;
  78. }
  79. //message.js
  80. let loadingMsg = null;
  81. function appendDoc() {
  82. setTimeout(() => {
  83. var like_comment = document.querySelectorAll(
  84. ".ReportListHeader__ButtonContainer-hbhHEO.fLMUtj"
  85. )[0];
  86. if (like_comment) {
  87. like_comment.append(button); //把按钮加入到 x 的子节点中
  88. return;
  89. }
  90. appendDoc();
  91. }, 1000);
  92. }
  93. appendDoc();
  94. let myHeaders = new Headers();
  95. myHeaders.append("content-type", "application/json");
  96. function fetchFun(url, data, requestOptions = getRequestOptions) {
  97. const urlData = Object.keys(data)
  98. .map((v) => `${v}=${data[v]}`)
  99. .join("&");
  100. return fetch(`${url}?${urlData}`, requestOptions)
  101. .then((response) => response.text())
  102. .then((r###lt) => {
  103. return JSON.parse(r###lt);
  104. })
  105. .catch((error) => console.log("error", error));
  106. }
  107. async function task_list(startPage, endPage) {
  108. loadingMsg = Qmsg.loading("正在导出,请勿重复点击!");
  109. let page = +(startPage);
  110. let pageSize = +((endPage + 1 - +startPage) * 10);
  111. let search_word = document.querySelector(".byted-input.byted-input-size-md")?.value
  112. let raw = JSON.stringify({
  113. "ids": [],
  114. page,
  115. pageSize,
  116. "keyword": search_word
  117. });
  118. let data = {
  119. aadvid: getQueryVariable("aadvid"),
  120. };
  121. let postRequestOptions = {
  122. method: "POST",
  123. headers: myHeaders,
  124. body: raw,
  125. redirect: "follow",
  126. };
  127. let taskList = await fetchFun(
  128. "/product_node/v2/api/segmentedMarket/reportList",
  129. data,
  130. postRequestOptions
  131. );
  132. let res = taskList?.data?.list
  133. ?.map((v) => {
  134. return { name: v.name, reportId: v.reportId, endTime:v.endTime, status:v.status ,periodType:v.periodType };
  135. })
  136. .filter((v) => v.status == "COMPLETED");
  137. let expExcelData = await Promise.all(
  138. res.map((v) => {
  139. let data = getData(v);
  140. return data;
  141. })
  142. );
  143. expExcel(expExcelData, startPage, endPage);
  144. expExcelData = [];
  145. }
  146. //数据获取
  147. async function getData(e) {
  148. let raw = JSON.stringify({
  149. "reportId": e.reportId,
  150. "categoryId": "0",
  151. "contentType": "ALL",
  152. "startDate": "0",
  153. "endDate": e.endTime,
  154. "dateType": e.periodType,
  155. "analysisType": "DRILL_DOWN",
  156. "itemType": "PRICE_SEGMENT"
  157. });
  158. let postRequestOptions = {
  159. method: "POST",
  160. headers: myHeaders,
  161. body: raw,
  162. redirect: "follow",
  163. };
  164. let data = {
  165. aadvid: getQueryVariable("aadvid"),
  166. };
  167. let requestData = await fetchFun(
  168. "https://yuntu.oceanengine.com/product_node/v2/api/industry/insightCategoryStats",
  169. data,
  170. postRequestOptions
  171. );
  172. let res = requestData.data.map(v=>{
  173. return {
  174. item:v.item,
  175. salesAmount: v.data?.salesAmount?.value,
  176. salesVolune: v.data?.salesVolune?.value,
  177. purchaseUidCnt: v.data?.purchaseUidCnt?.value,
  178. productCnt: v.data?.productCnt?.value,
  179. productAvgPrice: v.data?.productAvgPrice?.value,
  180. perUidOrderCnt: v.data?.perUidOrderCnt?.value,
  181. perUidPurchaseProductCnt: v.data?.perUidPurchaseProductCnt?.value,
  182. }
  183. }).filter(v=>{
  184. return v.item !== "0"
  185. })
  186. return {key:e.name,value:res}
  187. }
  188. function expExcel(e, startPage, endPage) {
  189. let contrast = {
  190. "商品关键词名称": "item",
  191. "销售金额(指数)": "salesAmount",
  192. "销售量(指数)": "salesVolune",
  193. "购买人数(指数)": "purchaseUidCnt",
  194. "商品数量(指数)": "productCnt"
  195. };
  196. let fileName = `细分市场报告商品洞察(${startPage}-${endPage}页)`;
  197. let option = {};
  198. option.fileName = fileName; //文件名
  199. option.datas = e?.map(v=>{
  200. return {
  201. sheetName: v.key,
  202. sheetData: v.value.length ? v.value : [{}],
  203. sheetHeader: Object.keys(contrast),
  204. sheetFilter: Object.values(contrast),
  205. columnWidths: [], // 列宽
  206. }
  207. });
  208. var toExcel = new ExportJsonExcel(option);
  209. toExcel.saveExcel();
  210. setTimeout(() => {
  211. loadingMsg.close();
  212. }, 1000);
  213. }
  214. function urlClick() {
  215. try {
  216. let res = prompt("页码,例: 1,2 (起始页和结束页中间用英文逗号分隔)");
  217. if (res) {
  218. let [startPage, endPage] = res.split(",");
  219. startPage = parseInt(startPage);
  220. endPage = parseInt(endPage);
  221. if (isNaN(startPage) || isNaN(endPage) || endPage < startPage) {
  222. throw new Error("页码格式错误!");
  223. }
  224. task_list(startPage, endPage);
  225. }
  226. } catch (err) {
  227. Qmsg.error(err.message);
  228. }
  229. }
  230. })();