fix: QC-Ratings aus 7 älteren Artikeln entfernt (CHECK 10)
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Kombi-Script: Brettspiel-Deals + Multi-Kategorie. Ausführung bei jeder Top-30-Anfrage."""
|
||||
import os, time
|
||||
from datetime import datetime
|
||||
|
||||
exec(open('/home/hermes/.hermes/skills/finance/amazon-brettspiel-deals/scripts/search_deals.py').read().split('RESOURCES')[0])
|
||||
|
||||
from amazon_creatorsapi import AmazonCreatorsApi, Country
|
||||
from amazon_creatorsapi import models as m
|
||||
|
||||
PARTNER_TAG = "60pro05-21"
|
||||
COUNTRY = Country.DE
|
||||
|
||||
R_COMMON = [
|
||||
m.SearchItemsResource.ITEM_INFO_DOT_TITLE,
|
||||
m.SearchItemsResource.IMAGES_DOT_PRIMARY_DOT_LARGE,
|
||||
m.SearchItemsResource.OFFERS_V2_DOT_LISTINGS_DOT_PRICE,
|
||||
m.SearchItemsResource.ITEM_INFO_DOT_BY_LINE_INFO,
|
||||
]
|
||||
|
||||
MULTI_CATEGORIES = [
|
||||
("Heißluftfritteusen", ["Heißluftfritteuse", "Airfryer", "Air Fryer"], 10, None),
|
||||
("Staubsauger & Wischroboter", ["Staubsauger", "Saugroboter", "Wischsauger", "Akkusauger"], 10, None),
|
||||
("Laptops & Notebooks", ["Laptop", "Notebook"], 5, None),
|
||||
("Apple-Produkte", ["Apple iPhone", "Apple iPad", "Apple Watch", "Apple AirPods"], 5, None),
|
||||
("Tablets", ["Tablet"], 5, None),
|
||||
("Smartphones", ["Smartphone"], 5, None),
|
||||
]
|
||||
|
||||
def search_deals(api, keywords, count, node=None, with_savings=True):
|
||||
deals, seen = [], set()
|
||||
for kw in keywords:
|
||||
if len(deals) >= count * 3: break
|
||||
for page in range(1, 5):
|
||||
if len(deals) >= count * 3: break
|
||||
try:
|
||||
kwargs = dict(keywords=kw, item_count=50, item_page=page, resources=R_COMMON)
|
||||
if node:
|
||||
kwargs["browse_node_id"] = node
|
||||
resp = api.search_items(**kwargs)
|
||||
time.sleep(1.1)
|
||||
if not resp or not resp.items: continue
|
||||
for item in resp.items:
|
||||
if len(deals) >= count * 3: break
|
||||
try:
|
||||
asin = str(item.asin)
|
||||
if asin in seen: continue
|
||||
seen.add(asin)
|
||||
title = str(item.item_info.title.display_value)
|
||||
brand = str(item.item_info.by_line_info.brand.display_value) if item.item_info.by_line_info and item.item_info.by_line_info.brand else ""
|
||||
if not item.offers_v2 or not item.offers_v2.listings: continue
|
||||
li = item.offers_v2.listings[0]
|
||||
if not li.price or not li.price.money: continue
|
||||
pr = float(li.price.money.amount)
|
||||
sp, se, lp = 0, 0, pr
|
||||
if li.price.savings and li.price.savings.money:
|
||||
se = float(li.price.savings.money.amount)
|
||||
sp = int(li.price.savings.percentage)
|
||||
if li.price.saving_basis and li.price.saving_basis.money:
|
||||
lp = float(li.price.saving_basis.money.amount)
|
||||
if not with_savings or sp >= 10:
|
||||
img = ""
|
||||
if item.images and item.images.primary and item.images.primary.large:
|
||||
img = str(item.images.primary.large.url)
|
||||
deals.append({"t":title,"b":brand,"img":img,"pr":pr,"lp":lp,"sp":sp,"se":se,
|
||||
"url":f"https://www.amazon.de/dp/{asin}?tag={PARTNER_TAG}"})
|
||||
except: continue
|
||||
except Exception as e:
|
||||
print(f" E {kw}: {e}")
|
||||
if with_savings:
|
||||
deals.sort(key=lambda x: x["sp"], reverse=True)
|
||||
return deals[:count]
|
||||
|
||||
def generate_combined_html(board_deal_html_path, multi_results, output_path):
|
||||
# Read board game HTML
|
||||
with open(board_deal_html_path) as f:
|
||||
board_html = f.read()
|
||||
|
||||
# Build multi-category sections
|
||||
now = datetime.now().strftime("%d.%m.%Y, %H:%M")
|
||||
sec = ""
|
||||
for cat_name, deals in multi_results.items():
|
||||
if not deals: continue
|
||||
label = f"{cat_name} ({len(deals)})"
|
||||
sec += f'<h2 style="margin-top:2.5rem;color:#c75b3a;border-bottom:2px solid #e8e0d3;padding-bottom:0.3rem;">{label}</h2>\n'
|
||||
for d in deals:
|
||||
img = f'<img src="{d["img"]}" style="width:100px;height:100px;object-fit:contain;" onerror="this.style.display=\'none\'">' if d["img"] else ""
|
||||
br = f'<br><span style="color:#888;font-size:0.85rem;">{d["b"]}</span>' if d["b"] else ""
|
||||
pct = f'-{d["sp"]}%' if d["sp"] > 0 else ''
|
||||
saved = f'({d["se"]:.2f} €)' if d["se"] > 0 else ''
|
||||
has_deal = d["sp"] > 0
|
||||
uvp_line = f'<span style="text-decoration:line-through;color:#999;margin-left:0.5rem;">UVP {d["lp"]:.2f} €</span>' if has_deal else ''
|
||||
badge_line = f'<span style="background:#b12704;color:#fff;padding:2px 6px;border-radius:3px;margin-left:0.5rem;font-size:0.85rem;">{pct}</span><span style="color:#b12704;margin-left:0.3rem;">{saved}</span>' if has_deal else ''
|
||||
sec += f"""<div style="display:flex;gap:1rem;padding:1rem 0;border-bottom:1px solid #eee;">
|
||||
{img}<div style="flex:1;"><strong>{d['t'][:120]}</strong>{br}<br>
|
||||
<span style="color:#b12704;font-size:1.2rem;font-weight:bold;">{d['pr']:.2f} €</span>
|
||||
{uvp_line}
|
||||
{badge_line}<br>
|
||||
<a href="{d['url']}" target="_blank" style="color:#007185;">Zu Amazon →</a></div></div>\n"""
|
||||
|
||||
footer_marker = '<p class="meta" style="margin-top:2rem;">'
|
||||
combined = board_html.replace(footer_marker, sec + footer_marker)
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
f.write(combined)
|
||||
return output_path
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--board-html', type=str, required=True, help='Path to board game deals HTML')
|
||||
parser.add_argument('--output', type=str, default=None)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.output is None:
|
||||
args.output = os.path.expanduser(f"~/workspace/kombi_deals_{datetime.now().strftime('%Y-%m-%d')}.html")
|
||||
|
||||
api = AmazonCreatorsApi(CREDENTIAL_ID, CREDENTIAL_SECRET, "3.2", PARTNER_TAG, country=COUNTRY, throttling=1.0)
|
||||
print("Connected!\n")
|
||||
|
||||
results = {}
|
||||
for cat_name, keywords, count, node in MULTI_CATEGORIES:
|
||||
print(f"--- {cat_name} ---")
|
||||
deals = search_deals(api, keywords, count, node, with_savings=True)
|
||||
results[cat_name] = deals
|
||||
print(f" => {len(deals)}/{count}")
|
||||
for d in deals:
|
||||
print(f" -{d['sp']}% | {d['pr']:.2f}€ | {d['t'][:80]}")
|
||||
|
||||
# Bücher: Top 5 Bestseller (keine Angebots-Filterung)
|
||||
print("--- Bücher-Bestseller ---")
|
||||
books = search_deals(api, ["Bestseller Roman", "Bestseller Buch", "Thriller Bestseller"], 5, "541686", with_savings=False)
|
||||
results["Bücher-Bestseller (Top 5)"] = books
|
||||
print(f" => {len(books)}/5")
|
||||
for d in books:
|
||||
print(f" {d['pr']:.2f}€ | {d['t'][:80]}")
|
||||
|
||||
output = generate_combined_html(args.board_html, results, args.output)
|
||||
total = sum(len(v) for v in results.values())
|
||||
print(f"\n{total} Non-Brettspiel-Deals hinzugefügt → {output}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user