Extract the complete buyer-facing details of a Shopee Brazil product listing from its product page, including the current price, selected variation, technical sheet, description, included items, condition, warranty, and seller information.
Use Cases
Use when a Shopee Brazil product URL is available in the form https://shopee.com.br/product/{shop-id}/{item-id}, or when the input is a Shopee Brazil short link such as https://br.shp.ee/{code} that must first be resolved. The product URL contains opaque shop and item identifiers; do not guess either identifier from a product name.
Automation Flow
- If given a
br.shp.eeshort link, navigate directly to it withwaitUntil: "domcontentloaded", then read the finallocation.href. Follow the redirect to obtain the canonical/product/{shop-id}/{item-id}URL. If the canonical product URL is already supplied, skip this step. - Navigate directly to
https://shopee.com.br/product/{shop-id}/{item-id}withwaitUntil: "domcontentloaded". - On the loaded product page, run this single
evaluate()extractor. It reads visible product-page content, selected variation controls, structured metadata, and seller sections without additional navigation:
(() => {
const clean = s => (s || '').replace(/\u00a0/g, ' ').replace(/[ \t]+/g, ' ').replace(/\n[ \t]+/g, '\n').trim();
const text = el => clean(el?.innerText || el?.textContent || '');
const visible = el => { if (!el) return false; const r = el.getBoundingClientRect(); if (!(r.width || r.height)) return false; const s = getComputedStyle(el); return s.display !== 'none' && s.visibility !== 'hidden'; };
const uniq = a => [...new Set(a.map(clean).filter(Boolean))];
const body = text(document.body);
const firstText = selectors => {
for (const selector of selectors) {
const el = [...document.querySelectorAll(selector)].find(visible);
if (el && text(el)) return text(el);
}
return '';
};
const meta = name => {
const el = document.querySelector(`meta[property="${name}"],meta[name="${name}"]`);
return clean(el?.content || '');
};
const section = labels => {
const wanted = labels.map(x => x.toLowerCase());
const nodes = [...document.querySelectorAll('h1,h2,h3,h4,h5,h6,[role="heading"],strong,b')].filter(visible);
const head = nodes.find(n => wanted.some(x => text(n).toLowerCase().includes(x)));
if (!head) return '';
const out = [];
let n = head.nextElementSibling;
while (n && out.length < 40) {
if (/^(H[1-6])$/.test(n.tagName) || n.getAttribute('role') === 'heading') break;
const t = text(n);
if (t) out.push(t);
n = n.nextElementSibling;
}
return clean(out.join('\n'));
};
const priceText = firstText([
'[class*="price"]', '[data-testid*="price"]', '[data-e2e*="price"]',
'div[aria-label*="R$"]', 'span[aria-label*="R$"]'
]);
const priceRe = /R\$\s*[\d.]+(?:,\d{2})?/gi;
// Shopee renders crossed-out, installment and shipping prices alongside the current one.
// Collect visible R$ values, dropping those inside struck/installment/original nodes, and
// take the first remaining as the current price while returning the rest for disambiguation.
const nonCurrentSel = 'del, s, [class*="through"], [class*="strike"], [class*="original"], [class*="installment"], [class*="parcel"], [class*="shipping"], [class*="frete"]';
const nonCurrent = el => el && el.closest(nonCurrentSel);
const priceNodes = [...document.querySelectorAll('[class*="price"], [data-testid*="price"], [data-e2e*="price"], [aria-label*="R$"]')].filter(el => visible(el) && !nonCurrent(el));
const currentText = el => { const c = el.cloneNode(true); c.querySelectorAll(nonCurrentSel).forEach(n => n.remove()); return [clean(c.innerText), clean(el.getAttribute('aria-label'))].join(' '); };
const priceCandidates = uniq(priceNodes.flatMap(el => currentText(el).match(priceRe) || []));
const price = priceCandidates[0] || '';
const variationEls = [...document.querySelectorAll('button[aria-pressed],button[class*="selected"],button[class*="Selected"],[role="radio"],[role="option"],[class*="variation"] button,[class*="product-variation"] button')].filter(visible);
const variation = uniq(variationEls.map(el => {
const a = el.getAttribute('aria-label') || '';
const t = text(el);
return (a || t).replace(/^(Selecionado|Selected)[:\s-]*/i, '');
}).filter(x => x && x.length < 160 && !/^(comprar agora|adicionar ao carrinho|comprar)$/i.test(x))).slice(0, 30);
const specSection = section(['ficha técnica', 'informações do produto', 'especificações', 'detalhes do produto']);
const descSection = section(['descrição', 'description']);
const includedSection = section(['itens inclusos', 'conteúdo da embalagem', 'o que vem na caixa']);
const conditionSection = section(['condição', 'condition']);
const warrantySection = section(['garantia', 'warranty']);
const sellerSection = section(['vendedor', 'seller', 'loja']);
const specPairs = {};
const specRoot = [...document.querySelectorAll('table,dl,[class*="spec"],[class*="Spec"],[class*="attribute"],[class*="Attribute"]')].find(visible);
if (specRoot) {
[...specRoot.querySelectorAll('tr')].forEach(row => {
const cells = [...row.querySelectorAll('th,td')].map(text).filter(Boolean);
if (cells.length >= 2) specPairs[cells[0]] = cells.slice(1).join(' | ');
});
[...specRoot.querySelectorAll('dt')].forEach(dt => {
const dd = dt.nextElementSibling;
if (dd && text(dt)) specPairs[text(dt)] = text(dd);
});
}
const sellerName = firstText(['[class*="seller"] [class*="name"]','[class*="shop-name"]','[class*="ShopName"]','a[href*="/shop/"]']);
const title = firstText(['h1','[data-testid="product-title"]','[class*="product-title"]']) || meta('og:title');
return {
url: location.href,
title,
price,
priceCandidates,
selectedVariation: variation,
technicalSpecifications: { text: specSection, pairs: specPairs },
description: descSection,
includedItems: includedSection,
condition: conditionSection,
warranty: warrantySection,
seller: { name: sellerName, details: sellerSection },
extractionNote: 'Empty fields mean the corresponding section was not visible or was not present on the loaded page.'
};
})()Possible Friction Points
- Shopee product pages use the opaque URL pattern
/product/{shop-id}/{item-id}; the shop and item IDs must come from the supplied URL, a resolved Shopee short link, or another explicit resolution step. br.shp.eelinks are redirect-style Shopee Brazil short links. Resolve them first and use the final canonical product URL rather than guessing identifiers.- Variation controls may be rendered as buttons with
aria-label,aria-pressed, or role-radio state. The extractor reports the currently selected controls and does not silently change the buyer’s selection. - Shopee frequently changes hashed CSS class names and may lazy-render description or seller content. Prefer the semantic selectors and heading-based section extraction above; if a section is empty, wait for the page content to render and rerun the evaluator rather than guessing values.
- Some listings omit condition, warranty, included-items, or technical-sheet sections. Preserve those fields as empty strings or empty objects instead of inferring them.
- Price extraction may encounter multiple prices such as installments or crossed-out prices; the returned
priceis the first visibleR$value associated with the page and should be checked against the product price region when several prices are displayed.