feat: extract individual Facebook marketplace items
This commit is contained in:
101
src/facebook.ts
101
src/facebook.ts
@@ -610,6 +610,107 @@ function formatCentsToCurrency(
|
||||
return formatter.format(dollars);
|
||||
}
|
||||
|
||||
/**
|
||||
Extract marketplace item details from Facebook item page HTML
|
||||
Updated for 2026 Facebook Marketplace API structure with multiple extraction paths
|
||||
*/
|
||||
function extractFacebookItemData(htmlString: HTMLString): FacebookMarketplaceItem | null {
|
||||
const { document } = parseHTML(htmlString);
|
||||
const scripts = document.querySelectorAll("script");
|
||||
|
||||
for (const script of scripts) {
|
||||
const scriptText = script.textContent;
|
||||
if (!scriptText) continue;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(scriptText);
|
||||
|
||||
// Check for the 2026 require structure with marketplace product details
|
||||
if (parsed.require && Array.isArray(parsed.require)) {
|
||||
// Try multiple extraction paths discovered from reverse engineering
|
||||
const extractionPaths = [
|
||||
// Path 1: Primary path from current API structure
|
||||
() => parsed.require[0][3].__bbox.result.data.viewer.marketplace_product_details_page.target,
|
||||
// Path 2: Alternative path with nested require
|
||||
() => parsed.require[0][3][0].__bbox.require[3][3][1].__bbox.result.data.viewer.marketplace_product_details_page.target,
|
||||
// Path 3: Variation without the [0] index
|
||||
() => parsed.require[0][3].__bbox.require[3][3][1].__bbox.result.data.viewer.marketplace_product_details_page.target,
|
||||
// Path 4-5: Additional fallback paths for edge cases
|
||||
() => parsed.require[0][3][1]?.__bbox?.result?.data?.viewer?.marketplace_product_details_page?.target,
|
||||
() => parsed.require[0][3][2]?.__bbox?.result?.data?.viewer?.marketplace_product_details_page?.target,
|
||||
];
|
||||
|
||||
let pathIndex = 0;
|
||||
for (const getPath of extractionPaths) {
|
||||
try {
|
||||
const targetData = getPath();
|
||||
if (targetData && typeof targetData === 'object' &&
|
||||
targetData.id && targetData.marketplace_listing_title &&
|
||||
targetData.__typename === 'GroupCommerceProductItem') {
|
||||
console.log(`Successfully extracted Facebook item data using extraction path ${pathIndex + 1}`);
|
||||
return targetData as FacebookMarketplaceItem;
|
||||
}
|
||||
} catch {
|
||||
// Path not found or invalid, try next path
|
||||
}
|
||||
pathIndex++;
|
||||
}
|
||||
|
||||
// Fallback: Search recursively for marketplace data in the parsed structure
|
||||
const findMarketplaceData = (obj: unknown, depth = 0, maxDepth = 10): FacebookMarketplaceItem | null => {
|
||||
if (depth > maxDepth) return null; // Prevent infinite recursion
|
||||
if (isRecord(obj)) {
|
||||
// Check if this object matches the expected marketplace item structure
|
||||
if (obj.marketplace_listing_title && obj.id &&
|
||||
obj.__typename === 'GroupCommerceProductItem' &&
|
||||
obj.redacted_description) {
|
||||
return obj as FacebookMarketplaceItem;
|
||||
}
|
||||
// Recursively search nested objects and arrays
|
||||
for (const key in obj) {
|
||||
const value = obj[key];
|
||||
if (isRecord(value) || Array.isArray(value)) {
|
||||
const result = findMarketplaceData(value, depth + 1, maxDepth);
|
||||
if (result) return result;
|
||||
}
|
||||
}
|
||||
} else if (Array.isArray(obj)) {
|
||||
// Search through arrays
|
||||
for (const item of obj) {
|
||||
const result = findMarketplaceData(item, depth + 1, maxDepth);
|
||||
if (result) return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Search through the entire require structure
|
||||
const recursiveResult = findMarketplaceData(parsed.require);
|
||||
if (recursiveResult) {
|
||||
console.log('Successfully extracted Facebook item data using recursive search');
|
||||
return recursiveResult;
|
||||
}
|
||||
|
||||
// Additional search in other potential locations
|
||||
if (parsed.__bbox?.result?.data?.viewer?.marketplace_product_details_page?.target) {
|
||||
const bboxData = parsed.__bbox.result.data.viewer.marketplace_product_details_page.target;
|
||||
if (bboxData && typeof bboxData === 'object' &&
|
||||
bboxData.id && bboxData.marketplace_listing_title &&
|
||||
bboxData.__typename === 'GroupCommerceProductItem') {
|
||||
console.log('Successfully extracted Facebook item data from __bbox structure');
|
||||
return bboxData as FacebookMarketplaceItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Log parsing errors for debugging but continue to next script
|
||||
console.debug(`Failed to parse script for Facebook item data: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
Parse Facebook marketplace search results into ListingDetails[]
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user