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.`);
|
console.log(`\nParsed ${pricedItems.length} Facebook marketplace listings.`);
|
||||||
return pricedItems.slice(0, MAX_ITEMS); // Limit results
|
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