// ==UserScript== // @name 京东商品参数对比工具 // @namespace http://tampermonkey.net/ // @version 0.0.3 // @description 该脚本可用于对比不限数量的同类型商品(如:手机、笔记本)的详细参数 // @author Yihang Wang // @match https://item.jd.com/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant GM_setValue // @grant GM_getValue // @license MIT // ==/UserScript== (function () { 'use strict'; var itemIDs = GM_getValue("jd-price-compare-item-ids", []); function pollUntil(conditionFn, interval = 1000, maxAttempts = 10) { return new Promise((resolve, reject) => { let attempts = 0; function checkCondition() { if (conditionFn()) { resolve(); } else if (attempts < maxAttempts) { attempts++; setTimeout(checkCondition, interval); } else { reject(new Error('Polling timed out')); } } checkCondition(); }); } function getItemID() { return window.location.pathname.match(/(\d+)\.html/)[1]; } function getPrice() { const itemID = getItemID(); const targetSelector = `.price.J-p-${itemID}`; const targetNode = document.querySelector(targetSelector); return parseFloat(targetNode.innerText); } function getBasicInfo() { const basicInfoElement = document.querySelector('#detail > div.tab-con > div:nth-child(1) > div.p-parameter'); const basicInfo = {}; basicInfoElement.querySelectorAll('li').forEach(dl => { let text = dl.textContent.trim(); console.log(text); if (text.indexOf(":") < 0) { console.error(`invalid basic info: ${text}, colon is not present`); return; } let items = text.split(":"); if (items.length != 2) { console.error(`invalid basic info: ${text}, incorrect number of items`); return; } const key = items[0] const value = items[1] basicInfo[key] = value; }); return basicInfo; } function getMainInfo() { const mainInfoElements = document.querySelectorAll('.Ptable-item'); const mainInfo = {}; mainInfoElements.forEach(item => { const key = item.querySelector('h3').textContent.trim(); const values = {}; item.querySelectorAll('dl').forEach(dl => { const detailKey = dl.querySelector('dt').textContent.trim(); const detailValue = dl.querySelector('dd').textContent.trim(); values[detailKey] = detailValue; }); mainInfo[key] = values; }); return mainInfo; } function getPackageList() { const packageListElement = document.querySelector('.package-list p'); return packageListElement.textContent.trim(); } function parseItem() { pollUntil(() => (!isNaN(getPrice()))); let price = getPrice(); let basicInfo = getBasicInfo(); let mainInfo = getMainInfo(); let packageList = getPackageList(); let itemID = getItemID(); let data = JSON.stringify({ "商品编号": itemID, "基本信息": basicInfo, "主体信息": mainInfo, "包装信息": packageList, "价格": price, }) return data; } function appendList(id) { let endpoint = "https://jd-compare.authu.online/api/v1/item"; fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(parseItem()) }) .then(response => { if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } console.log(response); return response.json(); }) .then(data => { console.log('Response:', data); }) .catch(error => { console.error('Error:', error); }); let itemIdList = GM_getValue("jd-price-compare-item-ids", []); console.log(itemIdList); let itemIdSet = new Set(itemIdList) itemIdSet.add(id); GM_setValue("jd-price-compare-item-ids", Array.from(itemIdSet.values())); } function createList() { let itemIdList = GM_getValue("jd-price-compare-item-ids", []); let endpoint = `https://jd-compare.authu.online/api/v1/list`; let listId = fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(itemIdList) }) .then(response => { if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } return response.text(); }) .then(data => { console.log("aaa"+data); return compareList(data); }) .catch(error => { console.error('Error:', error); }); return listId; } function compareList(listId) { let endpoint = `https://jd-compare.authu.online/api/v1/list/${listId}/compare`; console.log(endpoint); let resultUrl = fetch(endpoint) .then(response => { if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } return response.text(); }) .then(data => { window.open(data); GM_setValue("jd-price-compare-item-ids", []); }) .catch(error => { console.error('Error:', error); }); return resultUrl; } function addButton() { var compareButton = document.createElement('a'); compareButton.href = '#'; compareButton.id = 'jd-price-compare-add-button'; compareButton.textContent = `加入对比清单 (${itemIDs.length})`; compareButton.className = 'btn-special1 btn-lg'; compareButton.addEventListener("click", function (event) { event.preventDefault(); appendList(getItemID()); let itemIDs = GM_getValue("jd-price-compare-item-ids", []); compareButton.textContent = `加入对比清单 (${itemIDs.length})`; }); var cartButtton = document.querySelector('#preview > div.preview-info > div.left-btns.shieldShopInfo'); cartButtton.appendChild(compareButton); } function compareButton() { var compareButton = document.createElement('a'); compareButton.href = '#'; compareButton.id = 'jd-price-compare-start-button'; compareButton.textContent = '开始对比'; compareButton.className = 'btn-special1 btn-lg'; compareButton.addEventListener("click",function (event) { event.preventDefault(); createList(); }); var cartButtton = document.querySelector('#preview > div.preview-info > div.left-btns.shieldShopInfo'); cartButtton.appendChild(compareButton); } function main() { addButton(); compareButton(); } window.addEventListener('load', main); })();