ghstedpixel/app.py
0
1import re2from urllib.parse import urlparse, parse_qs, unquote3 4def parse_size_type_and_variants(size_str: str):5 """Identifies the explicit size category domain and outputs clean structural signatures for cross-matching"""6 if not size_str: return None, []7 s = size_str.upper().strip()8 storage_match = re.search(r'(\d+)\s*(GB|TB)', s)9 if storage_match:10 val = int(storage_match.group(1))11 unit = storage_match.group(2)12 sig = f"{val}GB" if unit == "GB" else f"{val*1024}GB"13 return "storage", [sig]14 15 liquid_match = re.search(r'(\d+(?:\.\d+)?)\s*(?:FL\.?\s*)?(OZ|ML)', s)16 if liquid_match:17 num = float(liquid_match.group(1))18 unit = liquid_match.group(2)19 if unit in ["OZ", "FL"]:20 if abs(num - 3.4) < 0.2: return "liquid", ["3.4OZ", "100ML"]21 if abs(num - 1.7) < 0.2: return "liquid", ["1.7OZ", "50ML"]22 if abs(num - 2.5) < 0.2: return "liquid", ["2.5OZ", "75ML"]23 if abs(num - 4.2) < 0.2: return "liquid", ["4.2OZ", "125ML"]24 if abs(num - 1.0) < 0.2: return "liquid", ["3.0OZ", "30ML"]25 if abs(num - 6.7) < 0.3: return "liquid", ["6.7OZ", "200ML"]26 return "liquid", [f"{num}OZ"]27 else:28 if abs(num - 100) < 6: return "liquid", ["3.4OZ", "100ML"]29 if abs(num - 50) < 4: return "liquid", ["1.7OZ", "50ML"]30 if abs(num - 75) < 5: return "liquid", ["2.5OZ", "75ML"]31 if abs(num - 125) < 6: return "liquid", ["4.2OZ", "125ML"]32 if abs(num - 30) < 3: return "liquid", ["1.0OZ", "30ML"]33 if abs(num - 200) < 11: return "liquid", ["6.7OZ", "200ML"]34 return "liquid", [f"{num}ML"]35 36 clothing_map = {37 "XS": "XS", "EXTRA SMALL": "XS", "EXTRASMALL": "XS",38 "S": "S", "SMALL": "S", "M": "M", "MEDIUM": "M", "L": "L", "LARGE": "L",39 "XL": "XL", "X-LARGE": "XL", "EXTRALARGE": "XL",40 "XXL": "2XL", "2XL": "2XL", "2X-LARGE": "2XL",41 "3XL": "3XL", "3X-LARGE": "3XL", "XXXL": "3XL"42 }43 for k, v in clothing_map.items():44 if f" {k} " in f" {s} " or s == k or s.replace("-", "") == k.replace(" ", ""): return "clothing", [v]45 46 shoe_match = re.search(r'\b(?:SIZE|SZ|US|UK|EU)?\s*(\d+(?:\.5)?)\b', s)47 if shoe_match: return "numeric", [shoe_match.group(1)]48 return "generic", [s.replace(" ", "")]49 50def is_size_mismatched(target_size: str, parsed_text: str) -> bool:51 """Scans and rejects store pages listing alternate competing product configurations automatically"""52 if not target_size or not parsed_text: return False53 category, target_variants = parse_size_type_and_variants(target_size)54 if not category or not target_variants: return False55 56 text_upper = " " + parsed_text.upper() + " "57 for var in target_variants:58 if category == "clothing":59 if re.search(r'\b' + re.escape(var) + r'\b', text_upper): return False60 elif category in ["storage", "liquid"]:61 if var in text_upper.replace(" ", ""): return False62 elif category == "numeric":63 if re.search(r'\b(?:SIZE|SZ|US|UK|EU)?\s*' + re.escape(var) + r'\b', text_upper): return False64 65 if category == "storage":66 found_storage = re.findall(r'(\d+)\s*(GB|TB)', text_upper)67 for val, unit in found_storage:68 sig = f"{val}GB" if unit == "GB" else f"{val*1024}GB"69 if sig not in target_variants: return True70 elif category == "liquid":71 found_liquids = re.findall(r'(\d+(?:\.\d+)?)\s*(?:FL\.?\s*)?(OZ|ML)', text_upper)72 for num, unit in found_liquids:73 _, found_variants = parse_size_type_and_variants(f"{num} {unit}")74 if found_variants and not any(fv in target_variants for fv in found_variants): return True75 elif category == "clothing":76 clothing_words = ["XS", "SMALL", "MEDIUM", "LARGE", "XL", "XXL", "2XL", "3XL"]77 for word in clothing_words:78 _, f_vars = parse_size_type_and_variants(word)79 if f_vars and f_vars[0] != target_variants[0]:80 if re.search(r'\b' + re.escape(word) + r'\b', text_upper) or re.search(r'\b' + re.escape(f_vars[0]) + r'\b', text_upper): return True81 elif category == "numeric":82 found_numbers = re.findall(r'\b(?:SIZE|SZ|US|UK|EU)?\s*(\d+(?:\.5)?)\b', text_upper)83 for num in found_numbers:84 if num not in target_variants: return True85 86 return False87 88def extract_universal_sizing(text: str) -> str:89 """Uses a normalized regex matrix to extract clean formatting standards for product layout rendering"""90 if not text or len(text) > 100: return ""91 text_cleaned = re.sub(r"['’]S\b", "", text.upper())92 text_norm = " " + text_cleaned.strip() + " "93 94 storage_match = re.search(r'(\d+)\s*(GB|TB)\b', text_norm)95 if storage_match: return f"{storage_match.group(1)} {storage_match.group(2)}"96 97 dim_match = re.search(r'(\d+(?:\.\d+)?)\s*(?:FL\.?\s*)?(OZ|ML)\b', text_norm)98 if dim_match: return f"{dim_match.group(1)} {dim_match.group(2)}"99 100 if "EXTRA LARGE" in text_norm or "X-LARGE" in text_norm: return "XL"101 if "LARGE" in text_norm: return "L"102 if "MEDIUM" in text_norm: return "M"103 if "SMALL" in text_norm: return "S"104 clothing_match = re.search(r'\b(XS|S|M|L|XL|2XL|XXL|3XL|4XL)\b', text_norm)105 if clothing_match: return clothing_match.group(1)106 107 shoe_match = re.search(r'\b(?:SIZE|SZ|US|UK|EU)?\s*(\d+(?:\.5)?)\b', text_norm)108 if shoe_match: return shoe_match.group(1)109 110 return ""111 112def clean_google_link(url: str) -> str:113 """114 Aggressively strips tracking redirects from both google.com and googleadservices.com,115 instantly routing users to the direct outbound merchant hosting the product.116 """117 if not url:118 return ""119 120 # Catch both organic shopping gates and paid ad service redirects121 if "google.com" not in url and "googleadservices.com" not in url:122 return url123 124 try:125 # Step 1: Recursively unquote to clear multi-layered encoding tricks (%2F, %3A, etc.)126 decoded_link = unquote(url)127 while "%" in decoded_link:128 prev_link = decoded_link129 decoded_link = unquote(decoded_link)130 if prev_link == decoded_link:131 break132 133 # Step 2: Query parameter interception gate134 parsed_url = urlparse(decoded_link)135 qs = parse_qs(parsed_url.query)136 137 # 'adurl' handles googleadservices/aclk ads; 'url'/'q' handle organic redirects138 for redirect_param in ['adurl', 'url', 'q', 'link', 'shopping_redirect', 'destination']:139 if redirect_param in qs:140 target_url = qs[redirect_param][0].strip()141 if target_url.startswith(("http://", "https://")):142 return target_url143 144 # Step 3: Global Heuristic Regex Fallback145 # Extract any raw embedded HTTP/HTTPS url that skips Google's tracking ecosystem entirely146 found_urls = re.findall(r'(https?://[^\s&"\'<>]+)', decoded_link)147 for sub_url in found_urls:148 if "google.com" not in sub_url and "googleadservices.com" not in sub_url:149 if "?" in sub_url:150 base_part, query_part = sub_url.split("?", 1)151 # Strip downstream marketing bloat tags clean152 query_part = re.sub(r'&?(gclid|gclsrc|utm_[^&=]+|affp|affiliate)[^&]*', '', query_part, flags=re.IGNORECASE)153 sub_url = f"{base_part}?{query_part}".rstrip('?').rstrip('&')154 return sub_url155 156 except Exception as e:157 print(f"⚠️ [Link Extractor Error] Could not unwrap target metadata: {e}")158 159 return url160 161def parse_price_locally(dense_text, product_name):162 """Programmatic textual parsing engine to extract valid retail pricing while isolating shipping costs and spoof values"""163 print("⚙️ [Local Solver] Running regex matrix heuristic extraction on text payload...")164 prices = []165 tokens = dense_text.split(" | ")166 keywords = [k.lower() for k in product_name.split() if len(k) > 2]167 invalid_context_markers = ["shipping", "save", "off", "reduced", "per month", "/mo", "discount", "coupon"]168 169 for token in tokens:170 token_clean = token.lower()171 if any(k in token_clean for k in keywords):172 if any(marker in token_clean for marker in invalid_context_markers): continue173 matches = re.findall(r'\$\s*([\d,]+(?:\.\d{2})?)', token)174 for m in matches:175 try:176 clean_m = m.replace(',', '')177 price_val = float(clean_m)178 if 1.0 < price_val < 9000.0 and price_val not in [1.7, 3.3, 5.0, 6.8, 16.5, 18.0]: prices.append(price_val)179 except: continue180 181 if prices:182 lowest_extracted = min(prices)183 print(f"🎯 [Local Solver Success] Located valid competitive target minimum: ${lowest_extracted}")184 return {"price": lowest_extracted, "found": True}185 return {"price": None, "found": False}