拼多多商品数据获取

1.使得点击【登录复制】后可以直接复制,而不用登录; 2.设置整个页面的文本为可选的以及复制。

// ==UserScript==
// @name         拼多多商品数据获取
// @namespace    http://tampermonkey.net/
// @version      2024-05-34
// @description 1.使得点击【登录复制】后可以直接复制,而不用登录; 2.设置整个页面的文本为可选的以及复制。
// @author       逃逸线
// @match        *://mms.pinduoduo.com/goods/goods_list*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=pinduoduo.com
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @grant        GM_addStyle
// @require      https://cdn.jsdelivr.net/npm/xlsx@0.18.3/dist/xlsx.full.min.js
// @license MIT
// @description 1.使得点击【登录复制】后可以直接复制,而不用登录; 2.设置整个页面的文本为可选的以及复制。
// ==/UserScript==

(function() {
    'use strict';

    let dataList = [];
    let dataDetail=[];
    function waitForElement(selector, callback) {
        var element = document.querySelector(selector);
        if (element) {
            callback(element);
        } else {
            setTimeout(function() {
                waitForElement(selector, callback);
            }, 500);
        }
    }
    // 尝试等页面元素加载完成
    waitForElement('.batch_operate_batchOperate__1mERa', function(element) {
        // 找到元素后执行您的操作
        return createBtn()
    });

    function createBtn(){
        // 创建按钮元素
        var button = document.createElement('button')
        button.textContent = '下载数据(最大数<30),请先进行筛选,确保不超过30条,刷新请按 F5。';
        button.id = 'downloadButton';

        // 设置按钮样式
        button.style.padding = '10px';
        button.style.backgroundColor = '#3498db';
        button.style.color = '#ffffff';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        button.disabled=false
        var element = document.querySelector('.batch_operate_batchOperate__1mERa');
        element.appendChild(button); //插入按钮


        // 添加点击事件处理程序
        button.addEventListener('click', function() {
            dataList = [];
            var button = document.getElementById('downloadButton');
            button.disabled=true
            button.textContent='请求获取中...'
            button.style.backgroundColor = '#056b00';
             // 经常会变化
            var bottomPGT = document.querySelector('.TB_bottom_5-119-0');

            console.log("111111111111")
            var pageNum=bottomPGT.querySelector('[data-testid="beast-core-select-htmlInput"]')


            // 创建点击事件
            var clikeEvent = new MouseEvent('click', {
                bubbles: true,
                cancelable: true
            });



            pageNum.dispatchEvent(clikeEvent) //模拟点击页码 弹出下拉选择


            // var dropdown = document.querySelector('.ST_dropdownPanel_5-116-0 '); //下拉组件
            //  var lastChild = dropdown.lastElementChild;
            //  lastChild.dispatchEvent(clikeEvent)
            setTimeout(() => getList(), 2000);
        });
    }

    async function getList(){
        var rows = document.querySelectorAll('[data-testid="beast-core-table-body-tr"]'); //获取表格里的每一行数据
        let i = 0;

        for (let i = 0; i < rows.length; i++) {
            var row=rows[i]
            var tds= row.querySelectorAll('[data-testid="beast-core-table-td"]');
            var id= row.querySelector('.goods-id').children[0].textContent;//商品id

            await getDeatil(id)
            await new Promise(resolve => setTimeout(resolve, 5000)); // 延迟5秒后处理下一个商品


            var pic=row.querySelector('img').src////商品图片
            var goodsName=row.querySelector('.goods-name').textContent//商品标题

            var sumSales=tds[4].children[0].textContent
            var thirtySales=tds[5].children[0].textContent
            var createText=tds[7].textContent
            var createTime
            var operator
            var datetimeRegex = /(\d{4}-\d{2}-\d{2} \d{2}:\d{2})/;
            var match = createText.match(datetimeRegex);


          //  var operatorText =row.querySelector('[data-testid="beast-core-box"]').children[0].children[0].querySelector('span').textContent


            // 检查字符串中是否包含 ':'
         //   if (operatorText.split(':').length>1) {
        //        operator = operatorText.split(':')[1].trim();
        //    } else {
                // 如果不包含 ':',直接使用整个文本内容
         //       operator = operatorText.trim();
       //     }

            if (match) {
                createTime = match[1]; // 提取匹配到的时间部分
            }

            let obj={}
           obj.链接标题=goodsName
            obj.商品id=id
       //     obj.运营人员=operator
            obj.商品图片=pic
            obj.三十天销量=thirtySales
            obj.总销量=sumSales
            obj.上架时间=createTime
            obj.商品链接="https://mobile.yangkeduo.com/goods.html?goods_id="+id
            dataList.push(obj)
        };
        console.log(dataList)
        console.log(dataDetail)

        var next = document.querySelector('[data-testid="beast-core-pagination-next"]'); // 获取下一页按钮  如果没有下一页终止递归 导出表格
        if(dataList.length>29){
            next=null
        }


        if(!next||next.classList.contains("PGT_disabled_5-119-0")){
            download(dataList,dataDetail)
        }

        else if(next.classList.contains("PGT_next_5-119-0")){
            // 创建点击事件
            var clickEvent = new MouseEvent('click', {
                bubbles: true,
                cancelable: true,
            });
            // 触发点击事件
            next.dispatchEvent(clickEvent);
            var delay = Math.floor(Math.random() * (1000 + 1)) + 500; //  做延迟处理 防止请求过快风控
            setTimeout(() => getList(), delay);
        }
        else{
            alert('脚本出错,请检查!!!')
        }

    }

    async function getDeatil(goods_id){

        // 异步请求的具体实现
        return new Promise(resolve => {
            // 异步操作...
            // 在操作完成后调用 resolve

            var detailUrl = 'https://mms.pinduoduo.com/goods/goods_detail?goods_id='+goods_id+'&page_num=12';
            var newTab = window.open(detailUrl, '_blank');
            if (newTab) {
                newTab.onload = async function() {
                    await new Promise(resolve => setTimeout(resolve, 2000));
                    // 等待一段时间确保页面加载完全(可根据实际情况调整)

                    var trs=newTab.document.querySelectorAll('[data-testid="beast-core-table-th"]');
                    var guigeSize
                    var trTitle={}

                    for(var i=0; i<trs.length ;i++){
                        if(trs[i].children[0].textContent=='库存'){
                            guigeSize=i
                        }
                        var titleText = trs[i].children[0].textContent.trim(); // 获取标题文本并去除首尾空格
                        if(titleText=="拼单价(元)"){
                            trTitle['拼单价'] = i
                        }
                        else if(titleText=="单买价(元)"){
                            trTitle['单买价'] = i
                        }
                        else{
                            trTitle[titleText] = i; // 将标题文本作为键,列索引 i 作为值存储到对象中
                        }

                    }


                    // 提取详情页中的内容
                    var rows = newTab.document.querySelectorAll('[data-testid="beast-core-table-body-tr"]');
                    let rowspan
                    let rowIndex

                    for (let i = 0; i < rows.length; i++) {
                        var rowSpan= rows[i].querySelectorAll('[data-testid="beast-core-table-td"]')[0];

                        if(rowSpan.getAttribute('rowspan')>1){
                            rowspan=rowSpan.getAttribute('rowspan')
                            var curSpan=rowSpan
                            rowIndex=i
                            rowSpan.setAttribute('rowspan',1)
                            curSpan.setAttribute('rowspan',1)
                        }

                        let row=rows[i]
                        var tds= row.querySelectorAll('[data-testid="beast-core-table-td"]');
                        if(rowspan>1&&i!=rowIndex){
                            var parentElement = tds[0].parentNode; // 获取第一个 td 元素的父节点

                            // 复制第一个节点的数据
                            var clonedNode = curSpan.cloneNode(true);

                            // 在第一个节点之前插入复制的节点
                            parentElement.insertBefore(clonedNode, tds[0]);
                            tds= row.querySelectorAll('[data-testid="beast-core-table-td"]');
                        }

                        var skuName
                        var promotionPrice
                        var unitPrice
                        var purchasePrice
                        var code
                        var img

                        if(guigeSize==1){
                            skuName=tds[0].children[0].textContent
                        }else{
                            skuName=tds[0].children[0].textContent+tds[1].children[0].textContent          
                        }

                        if(trTitle.活动价){
                            promotionPrice=tds[trTitle.活动价].textContent

                        }

                        unitPrice=tds[trTitle.拼单价].textContent
                        purchasePrice=tds[trTitle.单买价].textContent
                        code=tds[trTitle.规格编码].textContent
                        img=tds[trTitle.规格预览图].querySelector('img').src
                        let obj={}
                        obj.商品id=goods_id
                        obj.sku名称=skuName
                        obj.活动价=promotionPrice
                        obj.拼单价=unitPrice
                        obj.单买价=purchasePrice
                        obj.规格编码=code
                        obj.sku图片=img
                        dataDetail.push(obj)

                    }
                    // 处理详情页内容,例如输出到控制台

                    // 关闭新标签页
                    newTab.close();

                };
            }

            resolve();
        });

    }


    function download(dataList, dataDetail) {
        function exportToExcel(data, sheetName,nullHeader) {
            var headers = Object.keys(data[0]);
            headers = headers.concat(nullHeader);

            var rows = data.map(obj => {
                var row = headers.map(header => {
                    if (Array.isArray(nullHeader) && nullHeader.includes(header)) {
                        return ''; // 空白值
                    } else {
                        return obj[header];
                    }
                });
                return row;
            });


            var workbook = XLSX.utils.book_new();
            var worksheet = XLSX.utils.aoa_to_sheet([headers].concat(rows));
            XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);

            var xlsxData = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
            var blob = new Blob([xlsxData], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });

            var link = document.createElement('a');
            link.href = window.URL.createObjectURL(blob);
            var today = new Date();
            var formattedDate = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
            link.download = 'PDD' +formattedDate+ sheetName + '.xlsx';

            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
        var dataListNullHeader=["款式编码","保本投产比","预设投产比","前置利润率","运营人员"]
        var dataDetailNullHeader=["利润率"]

        exportToExcel(dataList, '商品数据',dataListNullHeader);
        exportToExcel(dataDetail, 'sku数据',dataDetailNullHeader);

        var button = document.getElementById('downloadButton');
        button.disabled = false;
        button.textContent =  '下载数据(最大数<30),请先进行筛选,确保不超过30条,刷新请按 F5。';
        button.style.backgroundColor = '#3498db';
   
 }

})();