Voici votre URL de partage https://sharemycode.io/c/54fbed8 (Cliquer pour copier) (Copié)
Nom du fichier : RedundanT
class ProductDataExtractor {
constructor(document) {
this.document = document;
}
extractProductData() {
const productElements = Array.from(this.document.querySelectorAll('.product-technical-data'));
return productElements.map(element => this.extractProductInfo(element));
}
extractProductInfo(productElement) {
const getTextContent = (xpath) => {
const element = this.document.evaluate(xpath, productElement, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
return element ? element.textContent.trim().toUpperCase() : null;
};
const variantId = productElement.getAttribute('data-product-id');
const sku = getTextContent(".//span[contains(text(),'SKU')]/following-sibling::span");
const colorDescription = getTextContent(".//span[contains(text(),'olor')]/following-sibling::span");
const sizeDescription = getTextContent(".//span[contains(text(),'roduct') or contains(text(),'rodotto')]/following-sibling::span");
const groupTension = getTextContent(".//span[contains(text(),'tension') or contains(text(),'fonte')]/following-sibling::span");
const composition = getTextContent(".//span[contains(text(),'aterial')]/following-sibling::span");
let cleanedSizeDescription;
if (sizeDescription) {
const sizeMatch = sizeDescription.match(/(SMALL|MEDIUM|LARGE|PICCOLA|MEDIA|GRANDE)/);
if (sizeMatch) {
cleanedSizeDescription = groupTension ? `${sizeMatch[0]} ${groupTension}` : sizeMatch[0];
cleanedSizeDescription = cleanedSizeDescription.replace(/INTEGRATED LED|SCHEDA LED|LED INTEGRATO/i, 'LED');
}
}
return {
variantId,
sku,
colorDescription: colorDescription || '',
sizeDescription: cleanedSizeDescription || sizeDescription,
composition: composition || ''
};
}
extractJsonData(xpath, fallbackXpath) {
const getJsonFromScript = (xpath) => {
const scriptNode = this.document.evaluate(xpath, this.document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if (scriptNode) {
const scriptContent = scriptNode.textContent;
const jsonText = scriptContent.replace(/^\s*<script[^>]*>/, '').replace(/<\/script>\s*$/, '');
return JSON.parse(jsonText);
}
return null;
};
return getJsonFromScript(xpath) || getJsonFromScript(fallbackXpath);
}
extractRendererData(jsonData, xpath, fallbackXpath) {
if (!jsonData) return null;
const rendererData = jsonData["[data-role=swatch-options]"]?.["Magento_Swatches/js/swatch-renderer"];
if (rendererData) return rendererData;
const data = jsonData["*"]?.["Magento_Catalog/js/product/view/provider"]?.data;
return data ? Object.values(data.items)[0] : null;
}
extractProductDetails() {
const productNameElement = this.document.querySelector("main[id='maincontent'] div[class*='page'][class*='product'] [itemprop='name']");
const productName = productNameElement ? productNameElement.textContent : '';
const selector = this.document.querySelector("div[class*='product'][class*='info'] [class*='product'][class*='description'] > p");
const selector2 = this.document.evaluate(
"//div[contains(@class,'product-technical')]//div[contains(@class,'tabs-content')]/div/p[contains(text(),' ')]",
this.document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
const description = selector ? selector.textContent : (selector2 ? selector2.textContent : '');
return { productName, description };
}
extractGalleryImages() {
const productSelector = this.document.evaluate(
'//script[contains(.,"[data-gallery-role=gallery-placeholder]") and contains(.,"thumb")]',
this.document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
if (!productSelector) return [];
const productJson = JSON.parse(productSelector.textContent);
const galleryData = productJson["[data-gallery-role=gallery-placeholder]"]?.["mage/gallery/gallery"]?.data || [];
return galleryData.map(data => data.thumb.replace(/\/cache\/.*?\//, '').split('?')[0]);
}
formatProductData(productData, renderer, sizeMap) {
const sortedData = this.sortProductData(productData);
const { productName, description } = this.extractProductDetails();
return {
productId: renderer?.jsonConfig?.productId || renderer?.id,
productName,
description,
variants: Object.keys(sortedData).map(color => ({
color,
sizes: sortedData[color].map(item => {
const sizeDescription = sizeMap[item.variantId]?.size.toUpperCase() || item.sizeDescription.toUpperCase() || '';
const lightSource = sizeMap[item.variantId]?.lightSource.toUpperCase() || ''; // Convertir en majuscules
const variantData = renderer?.jsonConfig?.optionPrices?.[item.variantId] || renderer?.price_info || {};
const brut = variantData.oldPrice ? `${renderer.jsonConfig.priceFormat.pattern.replace('%s', variantData.oldPrice.amount)}` : `${variantData.regular_price} ${renderer.currency_code}`;
const net = variantData.finalPrice ? `${renderer.jsonConfig.priceFormat.pattern.replace('%s', variantData.finalPrice.amount)}` : `${variantData.final_price} ${renderer.currency_code}`;
const images = renderer?.jsonConfig?.images?.[item.variantId]?.map(image => image.img) || this.extractGalleryImages();
const availability = item.variantId in (renderer?.jsonConfig?.index || {});
const availabilityFallback = renderer?.is_available || false;
return {
[`${sizeDescription} ${lightSource}`]: [{
variantId: item.variantId,
sku: item.sku,
brut,
net,
images,
availability: availability || availabilityFallback,
composition: item.composition
}]
};
})
}))
};
}
sortProductData(productData) {
return productData.reduce((acc, item) => {
if (!acc[item.colorDescription]) acc[item.colorDescription] = [];
acc[item.colorDescription].push(item);
return acc;
}, {});
}
getFinalJson() {
const productData = this.extractProductData();
const jsonData = this.extractJsonData("//script[contains(text(), 'json')]", "//script[contains(text(), '/js/product')]");
const renderer = this.extractRendererData(jsonData);
const sizeMap = this.extractSizesFromRenderer(renderer);
const formattedData = this.formatProductData(productData, renderer, sizeMap);
return JSON.stringify(formattedData, null, 2);
}
extractSizesFromRenderer(renderer) {
const sizeMap = {};
if (!renderer || !renderer.jsonConfig) return sizeMap;
const attributes = renderer.jsonConfig.attributes;
if (!attributes) return sizeMap;
const colorAttribute = attributes["856"];
const sizeAttribute = attributes["547"];
const lightSourceAttribute = attributes["1653"];
if (!colorAttribute || !sizeAttribute) return sizeMap;
sizeAttribute.options.forEach(sizeOption => {
colorAttribute.options.forEach(colorOption => {
colorOption.products.forEach(sku => {
const indexEntry = renderer.jsonConfig.index[sku];
if (indexEntry && indexEntry["856"] === colorOption.id && indexEntry["547"] === sizeOption.id) {
const lightSource = lightSourceAttribute ?
(renderer.jsonConfig.index[sku]["1653"] ? lightSourceAttribute.options.find(source => source.id === renderer.jsonConfig.index[sku]["1653"]).label.toUpperCase() : '')
: '';
sizeMap[sku] = { size: sizeOption.label.toUpperCase(), lightSource };
}
});
});
});
return sizeMap;
}
}
const extractor = new ProductDataExtractor(document);
const finalJson = JSON.parse(extractor.getFinalJson());
const jsonContainer = document.createElement('pre');
jsonContainer.textContent = JSON.stringify(finalJson, null, 2);
jsonContainer.id = 'product-data-json';
jsonContainer.style.whiteSpace = 'pre-wrap';
document.body.appendChild(jsonContainer);
`/oort-da-esterno-config-lampada-da-terra-curva/lighting/wall-lamps/tropico-config-wall-and-ceiling-lamp/ricambi/diffusore-sillaba-media-r277501v00op00/illuminazione/lampade-da-parete/frenesi-lampada-a-parete-trasparente-f446745550tcle/ricambi/diffusore-sillaba-piccola-r277500voo0p00`
Informations
Cet extrait a été créé le 2 sept. 2024 à 15:27:26
Cet extrait expire le 2 oct. 2024 à 15:27:26
Langage : javascript
Link
Voici votre URL de partage : https://sharemycode.io/c/54fbed8 Copié