Kinderspiele-Angebote auf Amazon
{''.join(sections) if sections else 'Keine reduzierten Kinderspiele gefunden.
'}Brettspiel-News.de · (C) liegt beim jeweiligen Rechteinhaber
#!/usr/bin/env python3 """Amazon CreatorAPI 3.2 — Kinderspiele-Deals auf amazon.de finden. NUR Kinderspiele behalten, Non-Games entfernen.""" import argparse, os, re, time from datetime import datetime from amazon_creatorsapi import AmazonCreatorsApi, Country from amazon_creatorsapi import models as m CREDENTIAL_ID = "amzn1.application-oa2-client.91799968d0744e66950ea413659197d4" CREDENTIAL_SECRET = "amzn1.oa2-cs.v1.4c41154ecfd1835e00879ad5c8060ef5cd3b209b06077c0af1d868b343cab955" PARTNER_TAG = "60pro05-21" BROWSE_NODE = "1290250031" # Gesellschaftsspiele COUNTRY = Country.DE MAX_PAGES = 8 ITEMS_PER_PAGE = 50 KINDERSPIEL_KEYWORDS = [ "Kinderspiel Brettspiel", "Kinderspiel ab 3 Jahre", "Kinderspiel ab 4 Jahre", "Kinderspiel ab 5 Jahre", "Kinderspiel ab 6 Jahre", "Kinderspiel ab 7 Jahre", "Mitbringspiel", "Vorschulspiel", "Lernspiel Kinder", "Gedächtnisspiel Kinder", "Würfelspiel Kinder", "Kinderkartenspiel", "Bluey Spiel", "Paw Patrol Spiel", "Peppa Pig Spiel", "Sendung mit der Maus Spiel", "Dobble", "Uno Junior", "Lotti Karotti", "Obstgarten Spiel", ] RESOURCES = [ m.SearchItemsResource.ITEM_INFO_DOT_TITLE, m.SearchItemsResource.IMAGES_DOT_PRIMARY_DOT_LARGE, m.SearchItemsResource.OFFERS_V2_DOT_LISTINGS_DOT_PRICE, ] # Patterns für NON-Games (entfernen) NON_GAME_PATTERNS = [ r'\btragetasche\b', r'\bau(fbewahr|fbewahrung)\b', r'\bpuzzle\b', r'\bjigsaw\b', r'\bairtag\b', r'\bwiso\s+steuer\b', r'\bblu-?ray\b', r'\bdvd\b', r'\binsert\b', r'\borgani[sz]er\b', r'\bwellness\b', r'\bclean\s+slate\b', r'\btaschenbuch\b', r'\bcd\b', r'\bbuch\b', r'\bhandgepäck\b', r'\beurowings\b', r'\bryanair\b', r'\bsoaker\b', r'\bnerf\b', r'\bkrang\b', r'\bdr\.?\s*rahm\b', r'\d{2,4}\s*teilig', # Puzzles mit Stückzahl r'\d{3,4}\s*pc\b', # Jigsaw piece count ] NON_GAME_SIMPLE = [ 'tragetasche', 'aufbewahrung', 'airtag', 'wiso', 'blu-ray', 'bluray', 'wellness', 'clean slate', 'dr. rahm', 'krang', 'handgepäck', 'eurowings', 'ryanair', 'soaker', 'nerf', 'taschenbuch', 'insert', 'organizer', 'upgrade kit', ] def search_amazon(api, keyword, page=1): try: kwargs = dict( browse_node_id=BROWSE_NODE, item_count=ITEMS_PER_PAGE, item_page=page, resources=RESOURCES, ) if keyword: kwargs["keywords"] = keyword resp = api.search_items(**kwargs) time.sleep(1.1) return resp except Exception as e: print(f" Error '{keyword}' p{page}: {e}", flush=True) return None def is_nongame(title): """Check if title is a non-game product.""" tl = title.lower() # Simple check for bad in NON_GAME_SIMPLE: if bad in tl: return True # Stückzahl-Puzzle check: "1000 Teile", "500-teilig" etc. if re.search(r'\b(500|1000|1500|2000)\s*[tT]eil', tl): return True return False def is_kinderspiel(title): """Check if title is a genuine children's game.""" tl = title.lower() # Age markers if re.search(r'ab\s+[3-8]\s+jahr', tl): return True # Children's game markers kind_markers = [ 'kinderspiel', 'mitbringspiel', 'vorschul', 'lernspiel', 'gedächtnisspiel', 'kinderkartenspiel', 'würfelspiel', 'junior', 'kids', 'kinder', 'bluey', 'paw patrol', 'peppa pig', 'sendung mit der maus', 'monsterjäger', 'pupsparade', 'sagaland', 'verrückte labyrinth', 'paletti spaghetti', 'tempo kleine schnecke', 'dobble', 'uno junior', 'lotti karotti', 'obstgarten', 'halli galli', 'zicke zacke', 'leo muss zum friseur', 'make n break', 'make \'n\' break', 'memo', 'memory', ] for marker in kind_markers: if marker in tl: return True return False def extract_deals(response): deals = [] if not response or not response.items: return deals for item in response.items: try: title = str(item.item_info.title.display_value) # QC: Non-Games entfernen if is_nongame(title): continue # QC: Nur Kinderspiele behalten if not is_kinderspiel(title): continue image = "" if item.images and item.images.primary and item.images.primary.large: image = str(item.images.primary.large.url) asin = str(item.asin) if not item.offers_v2 or not item.offers_v2.listings: continue listing = item.offers_v2.listings[0] if not listing.price or not listing.price.money: continue price = float(listing.price.money.amount) list_price = None savings_pct = 0 savings_eur = 0 if listing.price.savings and listing.price.savings.money: savings_eur = float(listing.price.savings.money.amount) savings_pct = int(listing.price.savings.percentage) if listing.price.saving_basis and listing.price.saving_basis.money: list_price = float(listing.price.saving_basis.money.amount) # Niedrigere Schwelle für Kinderspiele (auch kleine Rabatte) if savings_pct > 0: if list_price is None: list_price = price deals.append({ "title": title, "image": image, "asin": asin, "price": price, "list_price": list_price, "savings_pct": savings_pct, "savings_eur": savings_eur, "url": f"https://www.amazon.de/dp/{asin}?tag={PARTNER_TAG}", }) except Exception: continue return deals def guess_age_group(title): """Guess age group from title.""" tl = title.lower() if re.search(r'ab\s+3\s+jahr', tl): return "3+" if re.search(r'ab\s+4\s+jahr', tl): return "4+" if re.search(r'ab\s+5\s+jahr', tl): return "5+" if re.search(r'ab\s+6\s+jahr', tl): return "6+" if re.search(r'ab\s+7\s+jahr', tl): return "7+" if 'mitbringspiel' in tl: return "Mitbringspiele" if re.search(r'ab\s+8\s+jahr', tl): return "8+" # Fallback: check for junior/kids markers if 'junior' in tl or 'kids' in tl: return "Mitbringspiele" return "Allgemein" AGE_ORDER = ["3+", "4+", "5+", "6+", "7+", "8+", "Mitbringspiele", "Allgemein"] def generate_html(deals, output_path): seen = set() unique = [] for d in deals: if d["asin"] not in seen: seen.add(d["asin"]) unique.append(d) unique.sort(key=lambda x: x["savings_pct"], reverse=True) top = unique[:20] # Group by age groups = {} for d in top: age = guess_age_group(d["title"]) if age not in groups: groups[age] = [] groups[age].append(d) now = datetime.now().strftime("%d.%m.%Y, %H:%M") sections = [] for age in AGE_ORDER: if age not in groups: continue items = groups[age] age_labels = { "3+": "Für die Kleinsten (ab 3 Jahre)", "4+": "Ab 4 Jahre", "5+": "Ab 5 Jahre", "6+": "Ab 6 Jahre", "7+": "Ab 7 Jahre", "8+": "Ab 8 Jahre", "Mitbringspiele": "Mitbringspiele", "Allgemein": "Weitere Kinderspiele", } label = age_labels.get(age, age) cards = [] for d in items: cards.append(f"""
Keine reduzierten Kinderspiele gefunden.
'}Brettspiel-News.de · (C) liegt beim jeweiligen Rechteinhaber