Amazon keywords Positioning by Asin

1.在亚马逊搜索结果页上定位ASIN, 获取排名

// ==UserScript==
// @name         Amazon keywords Positioning by Asin
// @namespace    http://tampermonkey.net/
// @version      2.1.1
// @description  1.在亚马逊搜索结果页上定位ASIN, 获取排名
// @description  2.代码重构————dom操作->fetch+DOMParser
// @author       You
// @match        https://www.amazon.com/*
// @match        https://www.amazon.co.uk/*
// @match        https://www.amazon.ca/*
// @icon         https://www.amazon.com/favicon.ico
// @license      MIT
// @grant        none
// ==/UserScript==


(function () {
    'use strict';

    // —— 配置区 ——  
    const DEFAULT_MAX_PAGES = 3; // 默认最多搜索页数
    const STYLE = `
    /* 容器 */
    #tm-asin-container {
        position: fixed;
        top: 60px;
        left: 0; right: 0;
        padding: 6px 12px;
        background: #fff;
        padding: 6px 12px
        box-shadow: 0 2px 12px rgba(0,0,0,0.1);
        font-family: "Helvetica Neue", Arial, sans-serif;
        z-index: 9999;
        display: flex;
        align-items: center;
    }

    /* ASIN 和页数输入框 */
    #tm-asin-container input[type="text"],
    #tm-asin-container input[type="number"] {
        margin-right: 14px;
        font-size: 16px;
        border: 1px solid #dcdfe6;
        border-radius: 4px;
        color: #606266;
        outline: none;
        transition: border-color .2s, box-shadow .2s;
        width: 200px;
        box-sizing: border-box;
    }
    #tm-asin-container input:focus {
        border-color: #409eff;
        box-shadow: 0 0 2px rgba(64,158,255,0.2);
    }

    /* 按钮 */
    #tm-asin-container button {
        margin-right: 12px;
        padding: 5px 10px;
        font-size: 14px;
        font-weight: 600;
        color: #fff;
        background-color: #409eff;
        border: 1px solid #409eff;
        border-radius: 4px;
        cursor: pointer;
        transition: background-color .2s, border-color .2s;
    }
    #tm-asin-container button[disabled] {
        background-color: #c0c4cc;
        border-color: #c0c4cc;
        cursor: not-allowed;
    }
    #tm-asin-container button:hover:not([disabled]) {
        background-color: #66b1ff;
        border-color: #66b1ff;
    }

    #tm-asin-container span {
        font-size: 16px;
    }
    /* 状态文字:紧跟按钮后面 */
    #tm-asin-container span#tm-status {
        margin-left: 12px;
        font-size: 16px;
        color:rgb(110, 111, 111);
    }
    `;

    // —— 状态 ——  
    let targetASIN = '';
    let maxPages = DEFAULT_MAX_PAGES;

    // —— 注入样式 & UI ——  
    const styleEl = document.createElement('style');
    styleEl.textContent = STYLE;
    document.head.appendChild(styleEl);

    const asinText = document.createElement('span');
    asinText.textContent = '✍ASIN:';
    const container = document.createElement('div');
    container.id = 'tm-asin-container';
    const inputASIN = document.createElement('input');
    inputASIN.type = 'text';
    inputASIN.placeholder = 'ASIN';
    const maxPageText = document.createElement('span');
    maxPageText.textContent = 'Max🔎Pages:';
    const inputPages = document.createElement('input');
    inputPages.type = 'number';
    inputPages.min = '1';
    inputPages.value = DEFAULT_MAX_PAGES;
    inputPages.style.width = '60px';
    const btnSearch = document.createElement('button');
    btnSearch.textContent = '搜索排名';
    const btnGoto = document.createElement('button');
    btnGoto.textContent = '跳转到结果页';
    btnGoto.disabled = true;
    // status的div的元素
    const status = document.createElement('span');
    status.setAttribute("id", "tm-status");
    status.textContent = '请填写 ASIN,点击“搜索排名”';
    /* 动画过渡——container栏的伸缩 */
    container.style.transition = 'top 0.4s ease';
    let isScrolling;
    let lastScrollY = window.scrollY;
    window.addEventListener("scroll", () => {
        // 清除之前的计时器,避免频繁触发
        window.cancelAnimationFrame(isScrolling);

        // 用 requestAnimationFrame 优化性能
        isScrolling = window.requestAnimationFrame(() => {
            const currentScrollY = window.scrollY;
            const isScrollingDown = currentScrollY > lastScrollY;

            container.style.top = isScrollingDown ? "0px" : "55px";
            lastScrollY = currentScrollY;
        });
    });

    [asinText, inputASIN, maxPageText, inputPages, btnSearch, btnGoto, status].forEach(el => container.appendChild(el));
    document.body.appendChild(container);

    // —— 状态更新 ——  
    const updateStatus = txt => { status.textContent = txt; };

    // —— 主搜索逻辑 ——  
    btnSearch.addEventListener('click', async () => {
        targetASIN = inputASIN.value.trim();
        maxPages = parseInt(inputPages.value, 10) || DEFAULT_MAX_PAGES;
        btnGoto.disabled = true;
        if (!targetASIN) return alert('请输入有效ASIN !!!');

        // 删除原有 page 参数
        const baseUrl = new URL(location.href);
        baseUrl.searchParams.delete('page');

        updateStatus(`🔎 开始搜索 ASIN-${targetASIN},最多 ${maxPages} 页…`);
        for (let page = 1; page <= maxPages; page++) {
            updateStatus(`🔎 正在搜索第 ${page} 页…`);
            const url = new URL(baseUrl);
            // 重新设置 page 参数
            url.searchParams.set('page', page);

            // fetch获取html字符串 DOMParser转成document对象 再搜索
            try {
                // 表示跨域请求时会带上 cookie(登录态)
                const resp = await fetch(url.href, { credentials: 'include' });
                const text = await resp.text();
                const doc = new DOMParser().parseFromString(text, 'text/html');
                // 查找ASIN
                const items = doc.querySelectorAll('div[data-asin]');
                console.log('div[data-asin]', items);
                let nat = 0, sp = 0, foundPos = null, isAd = false;

                for (const node of items) {
                    const asin = node.getAttribute('data-asin');
                    if (!asin) continue;
                    const sponsored = !!node.querySelector(
                        'a.puis-label-popover.puis-sponsored-label-text'
                    );
                    // 有购物车按钮才算一个排名
                    const hasCart = !!node.querySelector('button.a-button-text');
                    if (!hasCart) continue;
                    sponsored ? sp++ : nat++;
                    if (asin === targetASIN) {
                        foundPos = sponsored ? sp : nat;
                        isAd = sponsored;
                        break;
                    }
                }

                if (foundPos !== null) {
                    status.style.color = 'black';
                    updateStatus(`✅ 找到 ASIN-${targetASIN}:第 ${page} 页,第 ${foundPos} 位${isAd ? '(广告)' : ''}`);
                    btnGoto.disabled = false;
                    btnGoto.onclick = () => location.href = url.href;
                    return;
                }
            } catch (e) {
                console.error('Fetch 错误', e);
                updateStatus('❌ 网络请求出错,请重试');
                return;
            }
        }

        updateStatus(`❌ 在前 ${maxPages} 页未找到 ASIN-${targetASIN}`);
    });

})();