feat: add Facebook marketplace item fetching API
This commit is contained in:
141
src/facebook.ts
141
src/facebook.ts
@@ -1038,3 +1038,144 @@ export default async function fetchFacebookItems(
|
||||
console.log(`\nParsed ${pricedItems.length} Facebook marketplace listings.`);
|
||||
return pricedItems.slice(0, MAX_ITEMS); // Limit results
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch individual Facebook marketplace item details with enhanced error handling
|
||||
*/
|
||||
export async function fetchFacebookItem(
|
||||
itemId: string,
|
||||
cookiesSource?: string,
|
||||
cookiePath?: string,
|
||||
): Promise<ListingDetails | null> {
|
||||
// Load Facebook cookies - required for Facebook Marketplace access
|
||||
let cookies: Cookie[];
|
||||
if (cookiesSource) {
|
||||
// Use provided cookie source (backward compatibility)
|
||||
cookies = await loadFacebookCookies(cookiesSource);
|
||||
} else {
|
||||
// Auto-load from file or parse from env var
|
||||
cookies = await ensureFacebookCookies(cookiePath);
|
||||
}
|
||||
|
||||
if (cookies.length === 0) {
|
||||
throw new Error(
|
||||
"Facebook cookies are required for marketplace access. " +
|
||||
"Please provide cookies via 'cookies' parameter or create ./cookies/facebook.json file with valid Facebook session cookies.",
|
||||
);
|
||||
}
|
||||
|
||||
// Format cookies for HTTP header
|
||||
const domain = "www.facebook.com";
|
||||
const cookiesHeader = formatCookiesForHeader(cookies, domain);
|
||||
if (!cookiesHeader) {
|
||||
throw new Error(
|
||||
"No valid Facebook cookies found. Please check that cookies are not expired and apply to facebook.com domain.",
|
||||
);
|
||||
}
|
||||
|
||||
const itemUrl = `https://www.facebook.com/marketplace/item/${itemId}/`;
|
||||
|
||||
console.log(`Fetching Facebook marketplace item: ${itemUrl}`);
|
||||
|
||||
let itemHtml: string;
|
||||
try {
|
||||
itemHtml = await fetchHtml(itemUrl, 1000, {
|
||||
onRateInfo: (remaining, reset) => {
|
||||
if (remaining && reset) {
|
||||
console.log(
|
||||
"\n" +
|
||||
`Facebook - Rate limit remaining: ${remaining}, reset in: ${reset}s`,
|
||||
);
|
||||
}
|
||||
},
|
||||
cookies: cookiesHeader,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof HttpError) {
|
||||
console.warn(
|
||||
`\nFacebook marketplace item access failed (${err.status}): ${err.message}`,
|
||||
);
|
||||
|
||||
// Enhanced error handling based on status codes
|
||||
switch (err.status) {
|
||||
case 400:
|
||||
case 401:
|
||||
case 403:
|
||||
console.warn(
|
||||
"Authentication error: Invalid or expired cookies. Please update ./cookies/facebook.json with fresh session cookies.",
|
||||
);
|
||||
console.warn("Try logging out and back into Facebook, then export fresh cookies.");
|
||||
break;
|
||||
case 404:
|
||||
console.warn(
|
||||
"Listing not found: The marketplace item may have been removed, sold, or the URL is invalid.",
|
||||
);
|
||||
break;
|
||||
case 429:
|
||||
console.warn(
|
||||
"Rate limited: Too many requests. Facebook is blocking access temporarily.",
|
||||
);
|
||||
break;
|
||||
case 500:
|
||||
case 502:
|
||||
case 503:
|
||||
console.warn(
|
||||
"Facebook server error: Marketplace may be temporarily unavailable.",
|
||||
);
|
||||
break;
|
||||
default:
|
||||
console.warn(`Unexpected error status: ${err.status}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
const itemData = extractFacebookItemData(itemHtml);
|
||||
if (!itemData) {
|
||||
logExtractionMetrics(false, itemId);
|
||||
// Enhanced checking for specific failure scenarios
|
||||
if (itemHtml.includes("This listing is no longer available") ||
|
||||
itemHtml.includes("listing has been removed") ||
|
||||
itemHtml.includes("This item has been sold")) {
|
||||
console.warn(`Item ${itemId} appears to be sold or removed from marketplace.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (itemHtml.includes("log in to Facebook") ||
|
||||
itemHtml.includes("You must log in") ||
|
||||
itemHtml.includes("authentication required")) {
|
||||
console.warn(`Authentication failed for item ${itemId}. Cookies may be expired.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.warn(`No item data found in Facebook marketplace page for item ${itemId}. This may indicate:`);
|
||||
console.warn(" - The listing was removed or sold");
|
||||
console.warn(" - Authentication issues");
|
||||
console.warn(" - Facebook changed their API structure");
|
||||
console.warn(" - Network or parsing issues");
|
||||
return null;
|
||||
}
|
||||
|
||||
logExtractionMetrics(true, itemId);
|
||||
console.log(`Successfully extracted data for item ${itemId}`);
|
||||
|
||||
const parsedItem = parseFacebookItem(itemData);
|
||||
if (!parsedItem) {
|
||||
console.warn(`Failed to parse item ${itemId}: Invalid data structure`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check for sold/removed status in the parsed data with proper precedence
|
||||
if (itemData.is_sold) {
|
||||
console.warn(`Item ${itemId} is marked as sold in the marketplace.`);
|
||||
// Still return the data but mark it as sold
|
||||
parsedItem.listingStatus = "SOLD";
|
||||
} else if (!itemData.is_live) {
|
||||
console.warn(`Item ${itemId} is not live/active in the marketplace.`);
|
||||
parsedItem.listingStatus = itemData.is_hidden ? "HIDDEN" :
|
||||
itemData.is_pending ? "PENDING" : "INACTIVE";
|
||||
}
|
||||
|
||||
return parsedItem;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user