mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-10-05 07:13:06 +08:00
fix: classify bottom Sponsored Brands video placements
This commit is contained in:
parent
793c343fb6
commit
6b4aa2306a
@ -47,6 +47,19 @@ const html = `
|
||||
<a href="/dp/B000000005"><img src="https://example.com/unknown.jpg" /></a>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="bottom-sponsored-video-module">
|
||||
<video></video>
|
||||
<span>Sponsored</span>
|
||||
<div class="puis-card-container">
|
||||
<div class="puisg-row">
|
||||
<article data-asin="B000000008">
|
||||
<h3>Sponsored Brands video product</h3>
|
||||
<a href="/dp/B000000008"><img src="https://example.com/brand-video.jpg" /></a>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<aside id="rightResultsATF" data-component-type="sponsored-display">
|
||||
@ -71,7 +84,7 @@ Object.assign(globalThis, {
|
||||
|
||||
async function main() {
|
||||
const items = await collectAmazonSearchPageData({ pattern: 'pattern-2' });
|
||||
assert.equal(items.length, 7);
|
||||
assert.equal(items.length, 8);
|
||||
|
||||
const byAsin = new Map(items.map((item) => [item.asin, item]));
|
||||
assert.equal(byAsin.get('B000000003')?.adType, 'organic');
|
||||
@ -90,9 +103,13 @@ async function main() {
|
||||
assert.equal(byAsin.get('B000000005')?.placement, 'bottomOfSearch');
|
||||
assert.equal(byAsin.get('B000000006')?.adType, 'sponsoredDisplay');
|
||||
assert.equal(byAsin.get('B000000006')?.placement, 'sidebar');
|
||||
assert.equal(byAsin.get('B000000008')?.adType, 'sponsoredBrands');
|
||||
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.deepEqual(
|
||||
items.map((item) => item.pageRank),
|
||||
[1, 2, 3, 4, 5, 6, 7],
|
||||
[1, 2, 3, 4, 5, 6, 7, 8],
|
||||
);
|
||||
|
||||
console.log('Amazon search parser fixtures passed.');
|
||||
|
||||
@ -134,17 +134,18 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
||||
|
||||
const findAdModule = (card: HTMLElement) => {
|
||||
let current: HTMLElement | null = card;
|
||||
for (let depth = 0; current && depth < 6; depth++, current = current.parentElement) {
|
||||
let singleProductModule: HTMLElement | undefined;
|
||||
for (; current; current = current.parentElement) {
|
||||
if (isMainResultsBoundary(current)) break;
|
||||
const explicitModule = hasExplicitAdMetadata(current);
|
||||
const brandedSponsoredModule =
|
||||
current !== card && hasSponsoredDisclosure(current) && hasBrandCreative(current);
|
||||
const singleProductSponsored =
|
||||
current !== card && hasSponsoredDisclosure(current) && uniqueAsinsIn(current) === 1;
|
||||
if (explicitModule || brandedSponsoredModule || singleProductSponsored)
|
||||
return current;
|
||||
if (explicitModule || brandedSponsoredModule) return current;
|
||||
if (singleProductSponsored && !singleProductModule) singleProductModule = current;
|
||||
}
|
||||
return card;
|
||||
return singleProductModule || card;
|
||||
};
|
||||
|
||||
const classifyAd = (card: HTMLElement, module: HTMLElement) => {
|
||||
@ -235,7 +236,8 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
||||
(other, otherIndex) =>
|
||||
index !== otherIndex &&
|
||||
card.contains(other) &&
|
||||
card.dataset.asin === other.dataset.asin,
|
||||
parseAsin(card, card.querySelector<HTMLAnchorElement>('a[href]')?.href) ===
|
||||
parseAsin(other, other.querySelector<HTMLAnchorElement>('a[href]')?.href),
|
||||
),
|
||||
)
|
||||
.sort((left, right) => {
|
||||
@ -252,6 +254,7 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
||||
const placementRanks = new Map<HTMLElement, number>();
|
||||
const adRanks = new Map<AmazonAdType, number>();
|
||||
const moduleProductIndexes = new Map<HTMLElement, number>();
|
||||
const moduleAsins = new Map<HTMLElement, Set<string>>();
|
||||
let nextPlacementRank = 1;
|
||||
|
||||
return cards.reduce<CollectedItem[]>((items, card) => {
|
||||
@ -283,6 +286,12 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
||||
|
||||
const module = findAdModule(card);
|
||||
const classification = classifyAd(card, module);
|
||||
if (classification.adType !== 'organic') {
|
||||
const seenAsins = moduleAsins.get(module) || new Set<string>();
|
||||
if (seenAsins.has(asin)) return items;
|
||||
seenAsins.add(asin);
|
||||
moduleAsins.set(module, seenAsins);
|
||||
}
|
||||
let placementRank: number | undefined;
|
||||
let placementId: string | undefined;
|
||||
let adRank: number | undefined;
|
||||
@ -308,8 +317,7 @@ export async function collectAmazonSearchPageData(_: SearchPageCollectorPayload)
|
||||
const afterLastOrganic =
|
||||
lastOrganicCandidate &&
|
||||
Boolean(
|
||||
module.compareDocumentPosition(lastOrganicCandidate) &
|
||||
Node.DOCUMENT_POSITION_PRECEDING,
|
||||
module.compareDocumentPosition(lastOrganicCandidate) & Node.DOCUMENT_POSITION_PRECEDING,
|
||||
);
|
||||
placement = beforeFirstOrganic
|
||||
? 'topOfSearch'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user