fix: exclude hidden Amazon ad cards

This commit is contained in:
syrup 2026-09-02 10:22:26 +08:00
parent 6b4aa2306a
commit 1d60213a33
2 changed files with 44 additions and 5 deletions

View File

@ -60,6 +60,24 @@ const html = `
</div>
</div>
</section>
<section style="display: none">
<span>Sponsored</span>
<article data-asin="B000000009">
<h3 aria-label="Sponsored Ad - hidden preloaded product">Hidden preloaded product</h3>
<a href="/sspa/click?url=%2Fdp%2FB000000009">
<img src="https://example.com/hidden.jpg" />
</a>
</article>
</section>
<section aria-hidden="true">
<span>Sponsored</span>
<article data-asin="B000000010">
<h3>Hidden accessibility product</h3>
<a href="/dp/B000000010"><img src="https://example.com/hidden-aria.jpg" /></a>
</article>
</section>
</div>
<aside id="rightResultsATF" data-component-type="sponsored-display">
@ -107,6 +125,8 @@ async function main() {
assert.equal(byAsin.get('B000000008')?.adFormat, 'video');
assert.equal(byAsin.get('B000000008')?.placement, 'bottomOfSearch');
assert.equal(items.filter((item) => item.asin === 'B000000008').length, 1);
assert.equal(byAsin.has('B000000009'), false);
assert.equal(byAsin.has('B000000010'), false);
assert.deepEqual(
items.map((item) => item.pageRank),
[1, 2, 3, 4, 5, 6, 7, 8],

View File

@ -32,11 +32,30 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
const text = (node: Element | null | undefined) =>
(node?.textContent || '').replace(/\s+/g, ' ').trim();
const isVisible = (node: HTMLElement) =>
!node.hidden &&
node.getAttribute('aria-hidden') !== 'true' &&
node.style.display !== 'none' &&
node.style.visibility !== 'hidden';
const isVisible = (node: HTMLElement) => {
for (let current: HTMLElement | null = node; current; current = current.parentElement) {
if (current.hidden || current.getAttribute('aria-hidden') === 'true') return false;
const style = current.ownerDocument.defaultView?.getComputedStyle(current);
if (
style &&
(style.display === 'none' ||
style.visibility === 'hidden' ||
style.visibility === 'collapse' ||
style.opacity === '0' ||
style.getPropertyValue('content-visibility') === 'hidden')
) {
return false;
}
}
// JSDOM has no layout engine, so computed-style checks above are the authoritative test path.
// In Chromium, require the card or one of its key visual descendants to own a real render box.
if (node.ownerDocument.documentElement.getClientRects().length === 0) return true;
return [node, ...Array.from(node.querySelectorAll<HTMLElement>('img, h2, h3'))].some(
(candidate) =>
Array.from(candidate.getClientRects()).some((rect) => rect.width > 0 && rect.height > 0),
);
};
const decodeRepeatedly = (value: string) => {
let decoded = value;