From 8f53db79bdc81d2717f1ab8465f8211c1255bf1d Mon Sep 17 00:00:00 2001 From: Rich627 Date: Thu, 19 Feb 2026 01:15:47 -0700 Subject: [PATCH] fix: handle edge case annual fee patterns for United Explorer and Marriott Bountiful - Add pattern for '/bin/zsh intro annual fee for the first year, then ' format - Add KNOWN_ANNUAL_FEES fallback for cards where scraping fails - United Explorer: (after intro year) - Marriott Bonvoy Bountiful: --- Functions/scraper/scrapers/chase.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Functions/scraper/scrapers/chase.js b/Functions/scraper/scrapers/chase.js index 321efcd..08f3ad6 100644 --- a/Functions/scraper/scrapers/chase.js +++ b/Functions/scraper/scrapers/chase.js @@ -6,6 +6,12 @@ const https = require('https'); const { generateCardId, mapCategory } = require('../utils/categories'); +// Fallback annual fees for cards where scraping fails +const KNOWN_ANNUAL_FEES = { + 'marriott-bountiful': 250, + 'united-explorer': 150, // After first year intro $0 +}; + // Chase card URLs - organized by product line const CHASE_CARD_PAGES = [ // Sapphire Series @@ -158,6 +164,12 @@ function extractAnnualFee(html) { return parseInt(sectionMatch[1], 10); } + // Look for "$0 intro annual fee for the first year, then $X" pattern (United Explorer style) + const introFeeMatch = html.match(/\$0\s*intro\s*annual\s*fee[^,]*,\s*then\s*\$(\d+)/i); + if (introFeeMatch) { + return parseInt(introFeeMatch[1], 10); + } + // Look for "$X annual fee" in content (not in nav links) // Match only in paragraph or div content, not in links const contentFeeMatch = html.match(/]*>\s*\$(\d+)\s*annual\s*fee/i); @@ -465,7 +477,14 @@ async function scrapeCard(cardInfo) { return null; } - const annualFee = extractAnnualFee(html); + let annualFee = extractAnnualFee(html); + + // Use fallback if annual fee couldn't be scraped + if (annualFee === null && KNOWN_ANNUAL_FEES[cardInfo.slug]) { + console.log(` ℹ️ Using fallback annual fee for ${cardInfo.slug}: $${KNOWN_ANNUAL_FEES[cardInfo.slug]}`); + annualFee = KNOWN_ANNUAL_FEES[cardInfo.slug]; + } + const rewards = extractRewards(html); const imageURL = extractImageUrl(html, cardInfo.url); const rotating = extractRotatingCategories(html, name);