- // ==UserScript==
- // @name 闲鱼管家上货助手
- // @namespace http://tampermonkey.net/
- // @version 1.21
- // @description 在商品页面复制商品信息并在闲鱼管家后台上架页面插入“一键填充”按钮,添加下载商品详情图片的功能。包括选品功能,可在闲鱼商详页导出猜你喜欢的数据到Excel,支持动态数据
- // @author 阿阅 wx:pangyue2 mail:pang-yue@qq.com
- // @match https://h5.m.goofish.com/item?id=*
- // @match https://www.goofish.com/item*
- // @match https://goofish.pro/*
- // @match https://www.goofish.pro/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=goofish.pro
- // @grant GM_setValue
- // @grant GM_getValue
- // @require https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
- // @license MIT
- // ==/UserScript==
-
- (function () {
- "use strict";
-
- // 使用 unsafeWindow 访问原始 window 对象
- const unsafeWindow = window.unsafeWindow || window;
-
- interceptRequests();
-
- function showToast(message, type = "success") {
- Swal.fire({
- toast: true,
- position: "top-end",
- icon: type,
- title: message,
- showConfirmButton: false,
- timer: 1500,
- });
- }
-
- function downloadImage(url, fileName) {
- fetch(url)
- .then((response) => response.blob())
- .then((blob) => {
- const a = document.createElement("a");
- a.href = URL.createObjectURL(blob);
- a.download = `${fileName}.jpg`;
- a.click();
- URL.revokeObjectURL(a.href);
- })
- .catch((error) => console.error("图片下载失败:", error));
- }
-
- // 生成随机数
- function getRandomNumber(min, max) {
- const randomInt =
- Math.floor(Math.random() * (max / 10 - min / 10 + 1)) + min / 10;
- return randomInt * 10;
- }
-
- // 获取价格,适配不同格式,比如 1~10、10
- function getPrice(priceStr) {
- // 移除特殊字符 "¥" 和 "\n"
- const cleanedStr = priceStr.replace(/[¥\n]/g, "");
-
- // 使用正则表达式提取所有数字
- const numbers = cleanedStr.match(/\d+(\.\d+)?/g);
-
- if (numbers === null) {
- return null; // 如果没有匹配到数字,返回null
- }
-
- // 将提取出的字符串数字转换为浮点数
- const prices = numbers.map(Number);
-
- // 找出最大值
- const maxPrice = Math.max(...prices);
-
- return maxPrice;
- }
-
- // 商品页面逻辑
- if (window.location.href.includes("https://www.goofish.com/item")) {
- setTimeout(() => {
- const button = document.createElement("button");
- let goofishImages = document.querySelectorAll(
- "div.slick-slide:not(.slick-cloned)"
- ).length;
-
- button.innerHTML = `复制商品信息 & 下载图片( <b> ${goofishImages} </b>)`;
- button.style.position = "fixed";
- button.style.bottom = "20px";
- button.style.left = "20px";
- button.style.zIndex = 1000;
- button.style.padding = "10px 20px";
- button.style.backgroundColor = "#28a745";
- button.style.color = "white";
- button.style.border = "none";
- button.style.borderRadius = "5px";
- button.style.cursor = "pointer";
-
- document.body.appendChild(button);
-
- button.addEventListener("click", () => {
- copyGoods();
- downloadImages();
- });
- }, 1500);
-
- // 复制商品信息
- function copyGoods() {
- const detailTextElements = document.querySelectorAll(".desc--WLgQcGKD");
- const priceElements = document.querySelectorAll(".price--Ls68DZ8a ");
- let index = 0
- if (detailTextElements.length > 1) index = 1 // 避免获取错元素
-
- if (detailTextElements.length > 0 && priceElements.length > 0) {
- const detailText = detailTextElements[index].innerText;
- const indexOfFirstNewLine = detailText.indexOf(" ");
- let productName, productDescription;
-
- if (indexOfFirstNewLine !== -1) {
- productName = detailText.slice(0, indexOfFirstNewLine).trim();
- productDescription = detailText.trim();
- } else {
- productName = detailText.trim();
- productDescription = "";
- }
-
- // 裁剪商品标题到30个中文字以内
- const maxTitleLength = 30;
- productName = productName.slice(0, maxTitleLength);
-
- const productPrice = getPrice(priceElements[0].innerText.trim());
-
- GM_setValue("productName", productName);
- // GM_setValue("productDescription", productDescription);
- GM_setValue("productPrice", productPrice);
-
- console.log("商品信息已存储");
- console.log("商品名:", productName);
- // console.log("商品详情:", productDescription);
- console.log("商品价格:", productPrice);
-
- showToast("商品信息已复制");
- } else {
- console.error("未找到商品详情或价格元素");
- showToast("未找到商品详情或价格元素", "error");
- }
- }
-
- // 下载商品图片
- function downloadImages() {
- // 获取所有 class 为 slick-slide 且不包含 slick-cloned 的 div 元素
- const goofishImageDivs = document.querySelectorAll(
- "div.slick-slide:not(.slick-cloned)"
- );
-
- // 遍历这些 div 元素,获取其中的 img 标签
- const goofishImages = Array.from(goofishImageDivs).map((div) =>
- div.querySelector("img")
- );
-
- // 过滤掉可能不存在 img 标签的 div
- const filteredGoofishImages = goofishImages.filter((img) => img !== null);
-
- filteredGoofishImages.forEach((img, index) => {
- setTimeout(() => {
- let src = img.src.replace(/_webp$/, ""); // 去掉_webp并下载jpg格式
- downloadImage(src, `${index + 1}`);
- }, index * 100); // 每个下载任务之间延迟100毫秒
- });
- showToast("图片正在开始");
- }
- }
-
- // 闲鱼管家上架页面逻辑
- if (window.location.hostname.includes("goofish.pro")) {
- const button = document.createElement("button");
- button.innerText = "一键填充";
- button.style.position = "fixed";
- button.style.bottom = "10px";
- button.style.left = "150px";
- button.style.zIndex = 1000;
- button.style.padding = "10px 20px";
- button.style.backgroundColor = "#28a745";
- button.style.color = "white";
- button.style.border = "none";
- button.style.borderRadius = "5px";
- button.style.cursor = "pointer";
-
- document.body.appendChild(button);
-
- button.addEventListener("click", () => {
- // 检查当前页面是否是商品添加页面
- if (!window.location.href.includes("/sale/product/add")) {
- window.location.href =
- "https://goofish.pro/sale/product/add?from=%2Fon-sale";
- setTimeout(() => fillProductInfo(), 1500);
- return;
- } else {
- fillProductInfo();
- }
- });
- }
-
- function fillProductInfo() {
- // 从 Tampermonkey 存储中获取商品信息
- const productName = GM_getValue("productName", "");
- const productDescription = GM_getValue("productDescription", "") || productName;
- let prict = GM_getValue("productPrice");
- let productPrice = 100;
- if (prict < 2) {
- productPrice = 1.9;
- } else {
- productPrice = parseFloat(GM_getValue("productPrice", "100")) - 0.1;
- }
-
- if (!productName || !productDescription || isNaN(productPrice)) {
- showToast("请先去闲鱼详情页复制商品信息", "warning");
- return;
- }
-
- console.log("读取到的商品名:", productName);
- console.log("读取到的商品详情:", productDescription);
- console.log("读取到的商品价格:", productPrice);
-
- // 选择类目
- const categoryElements = document.querySelectorAll(".release-history");
- if (categoryElements.length > 0) {
- categoryElements[0].click();
- } else {
- console.error("未找到类目选择元素");
- showToast("未找到类目选择元素", "error");
- }
-
- setTimeout(() => {
- // 选择店铺
- const shopList = document.querySelectorAll("ul.auth-list li");
- if (shopList.length > 0) {
- shopList[0].click();
- } else {
- console.error("未找到目标店铺元素");
- showToast("未找到目标店铺元素", "error");
- }
-
- setTimeout(() => {
- // 填充商品名
- const inputElements = document.querySelectorAll(".el-input__inner");
- if (inputElements.length > 2) {
- inputElements[2].value = productName;
- const event = new Event("input", { bubbles: true });
- inputElements[2].dispatchEvent(event);
- } else {
- console.error("未找到商品标题输入框");
- showToast("未找到商品标题输入框", "error");
- }
-
- // 填充商品详情
- const descriptionElements = document.querySelectorAll(
- ".el-textarea__inner"
- );
- if (descriptionElements.length > 0) {
- descriptionElements[0].value = productDescription;
- const event = new Event("input", { bubbles: true });
- descriptionElements[0].dispatchEvent(event);
- } else {
- console.error("未找到商品描述输入框");
- showToast("未找到商品描述输入框", "error");
- }
-
- // 填充价格
- if (inputElements.length > 4) {
- inputElements[4].value = productPrice.toFixed(2);
- const event = new Event("input", { bubbles: true });
- inputElements[4].dispatchEvent(event);
- } else {
- console.error("未找到价格输入框");
- showToast("未找到价格输入框", "error");
- }
-
- // // 填充原价
- // if (inputElements.length > 5) {
- // inputElements[5].value = getRandomNumber(200, 500);
- // const event = new Event('input', { bubbles: true });
- // inputElements[5].dispatchEvent(event);
- // } else {
- // console.error('未找到价格输入框');
- // showToast('未找到价格输入框', 'error');
- // }
-
- // 填充库存
- const productStore = 1;
- if (inputElements.length > 5) {
- inputElements[6].value = productStore;
- const event = new Event("input", { bubbles: true });
- inputElements[6].dispatchEvent(event);
- } else {
- console.error("未找到库存输入框");
- showToast("未找到库存输入框", "error");
- }
-
- // 选择发布商品时机
- const radioElements = document.querySelectorAll(".el-radio");
- if (radioElements.length > 11) {
- radioElements[11].click();
- } else {
- console.error("未找到发布商品时机的单选框");
- showToast("未找到发布商品时机的单选框", "error");
- }
-
- showToast("商品信息已填充");
- }, 500);
- }, 500);
- }
-
- // 闲鱼详情页:提取并显示想要人数、浏览量和转化率
- if (window.location.href.includes("https://www.goofish.com/item")) {
- setTimeout(() => {
- const spanElement = document.querySelector(
- "#ice-container > div.content-container--gIWgkNkm > div.item-container--yLJD5VZj > div.item-main-container--jhpFKlaS > div.item-main-info--rA5Bmpa5 > div.tips--JYdXhSNh > div.want--mVAXJTGv"
- );
-
- if (spanElement) {
- const textContent = spanElement.textContent.trim();
-
- // 初始化变量
- let wantText = "0人想要";
- let viewText = "0浏览";
-
- // 检查字符串内容并进行拆分和处理
- if (textContent.includes("人想要") && textContent.includes("浏览")) {
- // 如果同时包含 "人想要" 和 "浏览"
- [wantText, viewText] = textContent.split(" ");
- } else if (textContent.includes("浏览")) {
- // 只有 "浏览"
- viewText = textContent;
- }
-
- // 提取数字部分并转换为整数
- const wantNumber =
- parseInt(wantText.replace("人想要", "").trim(), 10) || 0;
- const viewNumber =
- parseInt(viewText.replace("浏览", "").trim(), 10) || 0;
-
- let rate = 0;
- if (wantNumber != 0 || viewNumber != 0) {
- rate = wantNumber / viewNumber;
- }
- const conversionRate = (rate * 100).toFixed(0);
- const conversionRateText = conversionRate + "%";
-
- const statsDiv = document.createElement("div");
- statsDiv.style.position = "fixed";
- statsDiv.style.bottom = "63px";
- statsDiv.style.left = "20px";
- statsDiv.style.backgroundColor = "#93ab9b";
- statsDiv.style.borderRadius = "6px";
- statsDiv.style.color = "white";
- statsDiv.style.padding = "10px";
- statsDiv.style.zIndex = "1000";
- statsDiv.style.fontSize = "14px";
-
- const conversionRateSpan = document.createElement("b");
- conversionRateSpan.textContent = conversionRateText;
- conversionRateSpan.style.backgroundColor =
- conversionRate > 7 ? "green" : "red";
- statsDiv.style.backgroundColor =
- conversionRate > 7 ? "#93ab9b" : "rgb(211 131 131)";
-
- conversionRateSpan.style.padding = "2px 4px";
- conversionRateSpan.setAttribute("id", "conversion-rate");
-
- statsDiv.innerHTML = `
- 想要数 : <b id="want-num">${wantNumber}</b><br>
- 浏览量 : <b id="view-num">${viewNumber}</b><br>
- 转化率 : `;
- statsDiv.appendChild(conversionRateSpan);
-
- document.body.appendChild(statsDiv);
-
- if (conversionRate < 7) {
- console.log(`转化率太低了 ${conversionRate}%`);
- }
- } else {
- console.error("无法找到目标 span 元素");
- }
- }, 1000);
- }
-
- // 拦截并处理接口请求
- function interceptRequests() {
-
-
- // 覆盖原生的 XMLHttpRequest
- const originalXHR = unsafeWindow.XMLHttpRequest;
- unsafeWindow.XMLHttpRequest = function () {
- const xhr = new originalXHR();
-
- // 保存原始的 open 方法
- const originalOpen = xhr.open;
- xhr.open = function (method, url, ...rest) {
- this._url = url; // 保存请求 URL
- return originalOpen.apply(this, [method, url, ...rest]);
- };
-
- // 保存原始的 send 方法
- const originalSend = xhr.send;
- xhr.send = function (...args) {
- this.addEventListener("load", function () {
-
- // 拦截商品详情接口,获取详情文本内容
- if (
- this._url.includes(
- "h5api.m.goofish.com/h5/mtop.taobao.idle.pc.detail"
- )
- ) {
- try {
- const data = JSON.parse(this.responseText);
- console.log("接收到的数据:", data);
- if (data?.data?.itemDO?.desc) {
- let productDescription = data.data.itemDO.desc;
- const goodsId = new URL(location.href).searchParams.get("id");
- productDescription += `\n \n[钉子]发的是百 度 网 盘 链 接,永不失效,售出不退。
- [钉子]任何情况,不要申请退款,私信沟通给你处理,小店经营不易。
- [钉子]所有文件均获取自网络公开渠道,仅供学习和交流使用,所有版权归版权人所有,如版权方认为侵犯了您的版权,请及时联系小店删除。`;
- productDescription += `\n${goodsId}`;
- GM_setValue("productDescription", productDescription);
- console.log("商品详情:", productDescription);
- }
- } catch (error) {
- console.error("解析 XHR 响应时发生错误:", error);
- }
- }
-
- // 拦截商品详情接口,获取详情文本内容
- if (
- this._url.includes(
- "h5api.m.goofish.com/h5/mtop.taobao.idle.item.web.recommend.list"
- )
- ) {
- try {
- const data = JSON.parse(this.responseText);
- console.log('接收到的数据:', data);
-
- // 检查 data.data.cardList 是否为数组
- if (data && Array.isArray(data.data?.cardList)) {
- // 提取有效的数据
- const tempData = data.data.cardList.filter((item) => {
- if (item && item.cardData && item.cardData.itemId) {
- const price = parseInt(item.cardData.price);
- return price > 0;
- }
- return false;
- });
-
- // 合并到全局数组 window.collectedData
- window.collectedData.push(...tempData);
- console.log('新数据已追加:', data.data.cardList);
-
- // 去重:使用一个 Set 来追踪已经存在的 itemId
- const uniqueItems = [];
- const itemIdSet = new Set();
-
- // 遍历 window.collectedData,添加未重复的 item
- for (const item of window.collectedData) {
- const itemId = item.cardData?.itemId;
- if (itemId && !itemIdSet.has(itemId)) {
- uniqueItems.push(item);
- itemIdSet.add(itemId);
- }
- }
-
- // 更新 window.collectedData 为去重后的结果
- window.collectedData = uniqueItems;
-
- console.log('去重后的数据:', window.collectedData);
-
- // 更新数据计数
- updateDataCount();
- }
- } catch (error) {
- console.error("解析 XHR 响应时发生错误:", error);
- }
- }
- });
- return originalSend.apply(this, args);
- };
-
- return xhr;
- };
- }
-
- // 自动写入类目(暂定为电子资料)
- if (window.location.hostname.includes("goofish.pro")) {
- // 要写入的键
- const key = "goods_select";
-
- // 要写入的值(注意是一个字符串)
- const value = JSON.stringify([
- {
- name: "电子资料",
- id: [
- 99,
- "eebfcb1cd9bfce8e212e21d79c0262e7",
- "eebfcb1cd9bfce8e212e21d79c0262e7",
- "3cdbae6d47df9251a7f7e02f36b0b49a",
- ],
- item_biz_type: 2,
- },
- ]);
-
- // 检查localStorage中是否已经存在该键
- if (!localStorage.getItem(key)) {
- // 将键值对写入localStorage
- localStorage.setItem(key, value);
- console.log(`已写入localStorage: ${key} = ${value}`);
- } else {
- console.log(
- `localStorage中已存在: ${key} = ${localStorage.getItem(key)}`
- );
- }
- }
-
- repleaceUrl();
- // 避免闲管家的域名混用,带www和不带www的,因为两者的cookie不同,导致登录(不可用)状态是不共享的
- function repleaceUrl() {
- // 获取当前页面的 URL
- const currentUrl = window.location.href;
-
- // 判断是否是以 'https://www.goofish.pro/' 开头
- if (currentUrl.startsWith("https://www.goofish.pro/")) {
- // 使用正则表达式替换 'www.' 为 ''
- const newUrl = currentUrl.replace("https://www.", "https://");
-
- // 跳转到新的 URL
- window.location.replace(newUrl);
- }
- }
-
- // 闲鱼商详页导出猜你喜欢的数据到Excel,支持动态数据
-
-
-
-
-
-
- if (window.location.href.includes("https://www.goofish.com/item?")) {
- exportGoodsList()
- }
-
- // 更新商品数量显示
- function updateDataCount() {
- const countElement = document.getElementById('data-count');
- if (countElement) {
- countElement.innerText = window.collectedData.length;
- }
- }
-
- function exportGoodsList () {
-
- setTimeout(() => {
- insertDownloadButton();
- }, 0);
-
- // 全局数组,用于存储商品数据,确保全局数据存储在 window 对象上
- if (!window.collectedData) {
- window.collectedData = [];
- }
-
- interceptRequests();
-
- // 插入下载按钮到页面
- function insertDownloadButton() {
- const button = document.createElement('button');
- button.innerHTML = `导出 [猜你喜欢] 商品 (<b id="data-count">0</b>)`;
- button.style.position = 'fixed';
- button.style.width = '240px';
- button.style.left = '50%';
- button.style.marginLeft = '-120px';
- button.style.bottom = '20px';
- button.style.zIndex = 9999;
- button.style.padding = '10px 20px';
- button.style.backgroundColor = 'rgb(40, 167, 69)';
- button.style.color = '#FFFFFF';
- button.style.border = 'none';
- button.style.borderRadius = '5px';
- button.style.cursor = 'pointer';
- button.addEventListener('click', downloadExcel);
- document.body.appendChild(button);
- }
-
- // 时间戳转换成日期格式
- function timestampToFormattedDate(timestamp) {
- if (!timestamp) return "";
- var date = new Date(parseInt(timestamp));
- var year = date.getFullYear(); // 获取年份
- var month = date.getMonth() + 1; // 获取月份,月份是从0开始的,所以需要加1
- var day = date.getDate(); // 获取日期
-
- // 月份和日期如果是单数,需要在前面补0
- month = month < 10 ? '0' + month : month;
- day = day < 10 ? '0' + day : day;
-
- return `${year}-${month}-${day}`;
- }
-
- // 下载Excel文件
- function downloadExcel() {
- if (window.collectedData.length === 0) {
- alert('没有数据可导出');
- return;
- }
-
- // 创建Excel工作簿
- const workbook = XLSX.utils.book_new();
- const worksheetData = window.collectedData.map((item) => {
- console.log(111, item.cardData?.trackEventParamInfo?.args?.publishTime)
- return {
- //商品ID: item.cardData.itemId || '',
- //链接: `https://www.goofish.com/item?id=` + item.cardData.itemId,
- 链接: {
- f: `HYPERLINK("https://www.goofish.com/item?id=${item.cardData.itemId}", "https://www.goofish.com/item?id=${item.cardData.itemId}")`
- },
- 标题: {
- f: `HYPERLINK("https://www.goofish.com/item?id=${item.cardData.itemId}", "${item.cardData.title}")`
- },
- 想要数: parseInt(item.cardData.clickParam.args.wantNum, 10) || '',
- 价格: parseInt(item.cardData.price) - 0.1 || '',
- 城市: item.cardData.area || '',
- 发布日期: timestampToFormattedDate(item.cardData?.clickParam?.args?.publishTime)
-
- }
- })
- .sort((a, b) => b.想要数 - a.想要数);;
-
- // 将数据转化为工作表
- const worksheet = XLSX.utils.json_to_sheet(worksheetData);
- XLSX.utils.book_append_sheet(workbook, worksheet, '猜你喜欢');
-
- // 生成Excel并触发下载
- const workbookOut = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
- const blob = new Blob([workbookOut], { type: 'application/octet-stream' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = '猜你喜欢商品.xlsx';
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- }
- }
- })();