天猫详情商品信息提取

天猫详情商品信息提取辅助,用于批量开票的信息辅助

  1. // ==UserScript==
  2. // @name 天猫详情商品信息提取
  3. // @namespace http://tampermonkey.net/
  4. // @homepage https://einvoice.taobao.com/?source=qn#/invoice/compensate
  5. // @version 1.1.0
  6. // @description 天猫详情商品信息提取辅助,用于批量开票的信息辅助
  7. // @author You
  8. // @match https://trade.tmall.com/detail/orderDetail.htm*
  9. // @match https://myseller.taobao.com/home.htm*
  10. // @match https://einvoice.taobao.com/*
  11. // @match https://qn.taobao.com/home.htm/*
  12. // @match https://dppt.jiangsu.chinatax.gov.cn:8443/*
  13. // @icon https://icons.duckduckgo.com/ip2/tmall.com.ico
  14. // @grant GM_setClipboard
  15. // @grant GM_setValue
  16. // @grant GM_getValue
  17. // @grant GM_registerMenuCommand
  18. // @grant GM_listValues
  19. // @grant GM_deleteValue
  20. // @license MIT
  21. // ==/UserScript==
  22.  
  23. (async function () {
  24. 'use strict';
  25.  
  26. /**
  27. * 新版天猫详情
  28. * @returns obj
  29. */
  30. async function get_order_obj() {
  31. const result = { hasRepeat: false }
  32. const products = []
  33. let finded = await el_find("div[class^=order-info_order-info] tr.order-item");
  34. if (!finded) {
  35. let msg = "页面加载超时/结构更新,未找到商品列表信息";
  36. alert(msg);
  37. throw msg;
  38. }
  39. const order_item = document.querySelectorAll("div[class^=order-info_order-info] tr.order-item");
  40.  
  41. const titles = []
  42. for (let item of order_item) {
  43. const temp = get_detail(item)
  44. console.log("temp=", temp)
  45. if (temp !== null && temp.price !== '0.00') {
  46. if (titles.includes(temp.title)) {
  47. result.hasRepeat = true
  48. continue;
  49. }
  50. products.push(temp)
  51. }
  52. }
  53.  
  54. function get_detail(item) {
  55. const tds = item.querySelectorAll('td');
  56. if (tds.length < 5) {
  57. return null
  58. }
  59. const details = {}
  60. // 标题
  61. details.title = tds[0].querySelector('a')?.innerText;
  62. // 商品编码
  63. const code = /[A-Z]{2,3}\d{6}-?[0-9A-Z]{0,5}/.exec(tds[0].innerText)
  64. details.code = code !== null ? code[0] : ''
  65. // 状态
  66. details.status = tds[2].innerText
  67. // 单价
  68. details.price = tds[3].innerText.replace('\n', '')
  69. // 数量
  70. details.count = tds[4].innerText
  71. return details;
  72. }
  73.  
  74. const order_id_re = /订单编号:[1-9][0-9]*/.exec(document.querySelector("div[class^=misc-info_misc-info__]")?.innerText)
  75. if (order_id_re === null) {
  76. return null
  77. }
  78. result.id = order_id_re[0].replace('订单编号:', '')
  79. result.products = products
  80. return result
  81. }
  82.  
  83. function getExportInvoiceInfo() {
  84. const values = prompt("请粘贴天猫发票平台导出数据")
  85. const list = [];
  86. for (let row of values.replace('\r\n', '\n').split('\n')) {
  87. const cells = row.split('\t');
  88. list.push(cells);
  89. }
  90. const T = {};
  91. const title = list[0];
  92. for (let i = 0; i < title.length; i++) {
  93. T[title[i]] = i;
  94. }
  95.  
  96. const trades = {};
  97. for (let item of list) {
  98. const id = item[T['*订单编号']];
  99. if (id === '*订单编号') { continue; }
  100. let temp = trades[id] === void 0 ? { 'id': id, 'products': {}, 'company': {} } : trades[id];
  101. // 如果企业信息未空,则填充
  102. if (JSON.stringify(temp.company) === '{}') {
  103. temp.company.name = item[T['*发票抬头']] !== void 0 ? item[T['*发票抬头']] : '';
  104. temp.company.id = item[T['购方税号']] !== void 0 ? item[T['购方税号']] : '';
  105. temp.company.address = item[T['企业地址']] !== void 0 ? item[T['企业地址']] : '';
  106. temp.company.phone = item[T['企业电话']] !== void 0 ? item[T['企业电话']] : '';
  107. temp.company.bank = item[T['开户行']] !== void 0 ? item[T['开户行']] : '';
  108. temp.company.account = item[T['开户账号']] !== void 0 ? item[T['开户账号']] : '';
  109. }
  110. // 添加商品
  111. let name = item[T['货物名称']];
  112. temp.products[name] = {
  113. 'price_invoice': item[T['商品金额']],
  114. 'count': item[T['数量']]
  115. }
  116. // 重新写入trades
  117. trades[id] = temp;
  118. }
  119. return trades;
  120. }
  121.  
  122. GM_registerMenuCommand("提取订单信息", () => {
  123. const trades = GM_listValues();
  124. for (let item of trades) {
  125. GM_deleteValue(item)
  126. }
  127. let ids = prompt("输入订单号");
  128. for (let k of ['\n', '\r', '\t', ' ', ',', ',,', ',,']) {
  129. ids = ids.replaceAll(k, ',')
  130. }
  131. const arr = ids.split(',');
  132. for (let id of arr) {
  133. GM_setValue(id, false);
  134. }
  135. if (arr.length > 0 && arr[0] !== '') {
  136. GM_setValue('running', true);
  137. GM_setValue('current', arr[0]);
  138. window.open(`https://trade.tmall.com/detail/orderDetail.htm?biz_order_id=${arr[0]}`)
  139. }
  140. })
  141.  
  142. GM_registerMenuCommand("导入发票信息", () => {
  143. const trades = GM_listValues();
  144. const invoices = getExportInvoiceInfo();
  145. for (let item of trades) {
  146. // 用订单号查询存储结果,没有结果的设为激活的,下一次处理
  147. const trade = GM_getValue(item, false);
  148. console.log(typeof (trade), trade)
  149. const invoice = invoices[item]; // 从导入数据提取订单信息
  150. if (trade && invoice !== void 0) {
  151. const ps = invoice.products; // 天猫导出的数据
  152. const co = invoice.company; // 天猫导出的数据
  153. console.log("ps=", ps)
  154. console.log("co=", co)
  155. console.log("trade=", trade)
  156. const nps = [];
  157. for (let p of trade.products) {
  158. if (!p.status.includes("退款成功")) {
  159. const p1 = ps[p.title];
  160. nps.push({
  161. 'title': p.title,
  162. 'code': p.code,
  163. 'price_invoice': p1.price_invoice,
  164. 'price_page': p.price_page,
  165. 'count': p1.count,
  166. 'status': p.status,
  167. })
  168. }
  169. }
  170. trade.products = nps;
  171. trade.company = co;
  172. GM_setValue(item, trade);
  173. }
  174. }
  175. alert("发票信息导入处理完成");
  176. })
  177.  
  178. GM_registerMenuCommand("结果=>发票基本信息", () => {
  179. const trades = GM_listValues();
  180. let results = '';
  181. for (let item of trades) {
  182. // 用订单号查询存储结果,没有结果的设为激活的,下一次处理
  183. const trade = GM_getValue(item, false);
  184. if (trade.company !== void 0) {
  185. const id = trade.id;
  186. const co = trade.company;
  187. results += `${id}\t普通发票\t\t\t\t${co.name}\t\t${co.id}\t${co.address}\t${co.phone}\t${co.bank}\t${co.account}\t${id}\n`;
  188. }
  189. }
  190. console.log(results);
  191. GM_setClipboard(results);
  192. alert("发票基本信息已复制")
  193. })
  194.  
  195. GM_registerMenuCommand("结果=>发票明细信息", () => {
  196. const trades = GM_listValues();
  197. let results = '';
  198. for (let item of trades) {
  199. // 用订单号查询存储结果,没有结果的设为激活的,下一次处理
  200. const trade = GM_getValue(item, false);
  201. if (trade.company !== void 0) {
  202. const id = trade.id;
  203. const ps = trade.products;
  204. for (let p of ps) {
  205. results += `${id}\t${p.code}\t\t\t\t${p.count}\t\t${p.price_invoice}\t0.13\n`;
  206. }
  207. }
  208. }
  209. console.log(results);
  210. GM_setClipboard(results);
  211. alert("发票明细信息已复制")
  212. })
  213.  
  214.  
  215. GM_registerMenuCommand("屏蔽发票申请界面元素", () => {
  216. if (!document.URL.includes('merchant-invoice')) { return }
  217. const els = document.querySelectorAll("div[class*=SearchContent_search]");
  218. const keys = ["是否免赔", "开票倒计时"];
  219. for (let el of els) {
  220. for (let key of keys) {
  221. if (el.innerText.includes(key)) { el.style.display = "none"; }
  222. }
  223. }
  224. })
  225.  
  226. /**
  227. *
  228. * @param {string} selector
  229. * @param {number} count 查找次数,默认10
  230. * @param {number} ms 查找间隔,默认500ms
  231. * @returns
  232. */
  233. async function el_find(selector, count = 10, ms = 500) {
  234. let found = false
  235. for (let i = 0; i < count; i++) {
  236. console.log(`尝试${i + 1}/${count}次查找【${selector}】`)
  237. found = await new Promise((resolve) => {
  238. setTimeout(() => {
  239. resolve(document.querySelector(selector) !== null);
  240. }, ms);
  241. })
  242. if (found) {
  243. console.log('got it ' + selector)
  244. break
  245. }
  246. }
  247. return found;
  248. }
  249.  
  250. // 订单详情读取
  251.  
  252. if (!document.URL.includes('https://qn.taobao.com/home.htm/trade-platform/tp/detail')) { return; }
  253.  
  254. setTimeout(async () => {
  255. const current = GM_getValue('current', false);
  256. if (!current) { return; }
  257. if (document.URL.includes(current)) {
  258. const p = await get_order_obj()
  259. console.log(current, ":", p)
  260. if (p.hasRepeat) {
  261. console.log(current, '存在重复商品,跳过未统计')
  262. } else {
  263. GM_setValue(current, p);
  264. console.log(GM_getValue(current, `GM获取失败,${current}`));
  265. }
  266. }
  267. const running = GM_getValue('running', false);
  268. if (!running) { return; }
  269. const trades = GM_listValues();
  270. for (let item of trades) {
  271. // 用订单号查询存储结果,没有结果的设为激活的,下一次处理
  272. if (!GM_getValue(item, false)) {
  273. GM_setValue('current', item);
  274. window.location = `https://qn.taobao.com/home.htm/trade-platform/tp/detail?bizOrderId=${item}`
  275. return;
  276. }
  277. }
  278. GM_deleteValue('running');
  279. GM_deleteValue('current');
  280. alert("订单遍历完成");
  281. }, 3000);
  282. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址