mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-10-05 15:22:59 +08:00
fix: preserve base Amazon product collection
This commit is contained in:
parent
2ad58b6f9c
commit
6fbf8b7bcd
@ -78,19 +78,6 @@ const html = `
|
|||||||
<a href="/dp/B000000010"><img src="https://example.com/hidden-aria.jpg" /></a>
|
<a href="/dp/B000000010"><img src="https://example.com/hidden-aria.jpg" /></a>
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="featured-from-amazon-brands">
|
|
||||||
<span>Featured from Amazon brands</span>
|
|
||||||
<article data-asin="B000000011">
|
|
||||||
<h3
|
|
||||||
aria-label="Sponsored Ad - hidden brand-banner product"
|
|
||||||
style="position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0)"
|
|
||||||
>Hidden brand-banner product</h3>
|
|
||||||
<a href="/sspa/click?url=%2Fdp%2FB000000011">
|
|
||||||
<img src="https://example.com/visible-brand-hero.jpg" />
|
|
||||||
</a>
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside id="rightResultsATF" data-component-type="sponsored-display">
|
<aside id="rightResultsATF" data-component-type="sponsored-display">
|
||||||
@ -140,12 +127,42 @@ async function main() {
|
|||||||
assert.equal(items.filter((item) => item.asin === 'B000000008').length, 1);
|
assert.equal(items.filter((item) => item.asin === 'B000000008').length, 1);
|
||||||
assert.equal(byAsin.has('B000000009'), false);
|
assert.equal(byAsin.has('B000000009'), false);
|
||||||
assert.equal(byAsin.has('B000000010'), false);
|
assert.equal(byAsin.has('B000000010'), false);
|
||||||
assert.equal(byAsin.has('B000000011'), false);
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
items.map((item) => item.pageRank),
|
items.map((item) => item.pageRank),
|
||||||
[1, 2, 3, 4, 5, 6, 7, 8],
|
[1, 2, 3, 4, 5, 6, 7, 8],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hiddenDisclosureDom = new JSDOM(
|
||||||
|
`
|
||||||
|
<main id="search">
|
||||||
|
<div class="s-main-slot">
|
||||||
|
<article data-component-type="s-search-result" data-asin="B000000011">
|
||||||
|
<h2>Visible organic product</h2>
|
||||||
|
<span
|
||||||
|
aria-label="Sponsored Ad - hidden disclosure"
|
||||||
|
style="position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0)"
|
||||||
|
>Sponsored</span>
|
||||||
|
<a href="/sspa/click?url=%2Fdp%2FB000000011">
|
||||||
|
<img class="s-image" src="https://example.com/visible-product.jpg" />
|
||||||
|
</a>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
`,
|
||||||
|
{ url: 'https://www.amazon.com/s?k=mirror' },
|
||||||
|
);
|
||||||
|
Object.assign(globalThis, {
|
||||||
|
document: hiddenDisclosureDom.window.document,
|
||||||
|
Node: hiddenDisclosureDom.window.Node,
|
||||||
|
Element: hiddenDisclosureDom.window.Element,
|
||||||
|
HTMLElement: hiddenDisclosureDom.window.HTMLElement,
|
||||||
|
HTMLAnchorElement: hiddenDisclosureDom.window.HTMLAnchorElement,
|
||||||
|
HTMLImageElement: hiddenDisclosureDom.window.HTMLImageElement,
|
||||||
|
});
|
||||||
|
const hiddenDisclosureItems = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
||||||
|
assert.equal(hiddenDisclosureItems.length, 1);
|
||||||
|
assert.equal(hiddenDisclosureItems[0].adType, 'organic');
|
||||||
|
|
||||||
console.log('Amazon search parser fixtures passed.');
|
console.log('Amazon search parser fixtures passed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -42,12 +42,7 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
|||||||
style.visibility === 'hidden' ||
|
style.visibility === 'hidden' ||
|
||||||
style.visibility === 'collapse' ||
|
style.visibility === 'collapse' ||
|
||||||
style.opacity === '0' ||
|
style.opacity === '0' ||
|
||||||
style.getPropertyValue('content-visibility') === 'hidden' ||
|
style.getPropertyValue('content-visibility') === 'hidden')
|
||||||
(style.clip !== 'auto' && style.clip !== '') ||
|
|
||||||
(style.clipPath !== 'none' && style.clipPath !== '') ||
|
|
||||||
(style.overflow === 'hidden' &&
|
|
||||||
Number.parseFloat(style.width) <= 1 &&
|
|
||||||
Number.parseFloat(style.height) <= 1))
|
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -62,6 +57,27 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isVisuallyHiddenText = (node: HTMLElement) => {
|
||||||
|
if (!isVisible(node)) return true;
|
||||||
|
const style = node.ownerDocument.defaultView?.getComputedStyle(node);
|
||||||
|
if (!style) return false;
|
||||||
|
|
||||||
|
const width = Number.parseFloat(style.width);
|
||||||
|
const height = Number.parseFloat(style.height);
|
||||||
|
const screenReaderOnlyBox =
|
||||||
|
(style.position === 'absolute' || style.position === 'fixed') &&
|
||||||
|
style.overflow === 'hidden' &&
|
||||||
|
Number.isFinite(width) &&
|
||||||
|
Number.isFinite(height) &&
|
||||||
|
width <= 1 &&
|
||||||
|
height <= 1;
|
||||||
|
const zeroClip = /rect\(\s*0(?:px)?[, ]+\s*0(?:px)?[, ]+\s*0(?:px)?[, ]+\s*0(?:px)?\s*\)/i.test(
|
||||||
|
style.clip || '',
|
||||||
|
);
|
||||||
|
const insetClip = /inset\(\s*(?:50%|100%)/i.test(style.clipPath || '');
|
||||||
|
return screenReaderOnlyBox || zeroClip || insetClip;
|
||||||
|
};
|
||||||
|
|
||||||
const decodeRepeatedly = (value: string) => {
|
const decodeRepeatedly = (value: string) => {
|
||||||
let decoded = value;
|
let decoded = value;
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
@ -94,7 +110,7 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
|||||||
...Array.from(node.querySelectorAll('[aria-label], [title], span')),
|
...Array.from(node.querySelectorAll('[aria-label], [title], span')),
|
||||||
].filter((candidate): candidate is HTMLElement => candidate instanceof HTMLElement);
|
].filter((candidate): candidate is HTMLElement => candidate instanceof HTMLElement);
|
||||||
return candidates.some((candidate) => {
|
return candidates.some((candidate) => {
|
||||||
if (!isVisible(candidate)) return false;
|
if (isVisuallyHiddenText(candidate)) return false;
|
||||||
const accessibleLabels = [
|
const accessibleLabels = [
|
||||||
candidate.getAttribute('aria-label'),
|
candidate.getAttribute('aria-label'),
|
||||||
candidate.getAttribute('title'),
|
candidate.getAttribute('title'),
|
||||||
@ -238,11 +254,6 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
|||||||
});
|
});
|
||||||
|
|
||||||
const cards = Array.from(candidateSet)
|
const cards = Array.from(candidateSet)
|
||||||
.filter((card) => {
|
|
||||||
const titleNode = card.querySelector<HTMLElement>('h2[aria-label], h2, h3[aria-label], h3');
|
|
||||||
const imageNode = card.querySelector<HTMLImageElement>('img.s-image, img[src]');
|
|
||||||
return Boolean(titleNode && imageNode && isVisible(titleNode) && isVisible(imageNode));
|
|
||||||
})
|
|
||||||
.filter(
|
.filter(
|
||||||
(card, index, all) =>
|
(card, index, all) =>
|
||||||
!all.some(
|
!all.some(
|
||||||
@ -278,7 +289,6 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
|||||||
|
|
||||||
const titleNode = card.querySelector<HTMLElement>('h2[aria-label], h2, h3[aria-label], h3');
|
const titleNode = card.querySelector<HTMLElement>('h2[aria-label], h2, h3[aria-label], h3');
|
||||||
const imageNode = card.querySelector<HTMLImageElement>('img.s-image, img[src]');
|
const imageNode = card.querySelector<HTMLImageElement>('img.s-image, img[src]');
|
||||||
if (!titleNode || !imageNode || !isVisible(titleNode) || !isVisible(imageNode)) return items;
|
|
||||||
const title =
|
const title =
|
||||||
titleNode?.getAttribute('aria-label')?.trim() ||
|
titleNode?.getAttribute('aria-label')?.trim() ||
|
||||||
text(titleNode) ||
|
text(titleNode) ||
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user