287 lines
12 KiB
Python
287 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""Build combined Joomla article from 4 Prime Day deal files."""
|
|
|
|
import re
|
|
import json
|
|
|
|
def extract_deal_blocks(html_content):
|
|
"""Extract all deal div blocks from HTML content."""
|
|
# Find all deal div blocks (flex containers with images)
|
|
blocks = re.findall(
|
|
r'<div style="display:flex;gap:1rem;padding:1rem 0;border-bottom:1px solid #eee;">(.*?)</div>\s*</div>',
|
|
html_content, re.DOTALL
|
|
)
|
|
# Also try the card class variant from devices file
|
|
if not blocks:
|
|
blocks = re.findall(
|
|
r'<div class="card">(.*?)</div>\s*</div>',
|
|
html_content, re.DOTALL
|
|
)
|
|
return blocks
|
|
|
|
def extract_deal_blocks_full(html_content):
|
|
"""Extract full deal div blocks including the outer div."""
|
|
blocks = re.findall(
|
|
r'(<div style="display:flex;gap:1rem;padding:1rem 0;border-bottom:1px solid #eee;">.*?</div>\s*</div>)',
|
|
html_content, re.DOTALL
|
|
)
|
|
if not blocks:
|
|
blocks = re.findall(
|
|
r'(<div class="card">.*?</div>\s*</div>)',
|
|
html_content, re.DOTALL
|
|
)
|
|
return blocks
|
|
|
|
def extract_deal_blocks_devices(html_content):
|
|
"""Extract deal blocks from the devices file (uses class='card')."""
|
|
blocks = re.findall(
|
|
r'<div class="card">(.*?)</div>(?=\s*(?:<h2>|<p style|<div class="card">|</body>))',
|
|
html_content, re.DOTALL
|
|
)
|
|
return blocks
|
|
|
|
def convert_device_block_to_standard(block_html):
|
|
"""Convert a device card block to standard deal format."""
|
|
# Extract image
|
|
img_match = re.search(r'<img src="([^"]+)"', block_html)
|
|
img_src = img_match.group(1) if img_match else ''
|
|
|
|
# Extract title
|
|
title_match = re.search(r'<strong>(.*?)</strong>', block_html)
|
|
title = title_match.group(1) if title_match else ''
|
|
|
|
# Extract price
|
|
price_match = re.search(r'<span class="price">(.*?)</span>', block_html)
|
|
price = price_match.group(1) if price_match else ''
|
|
|
|
# Extract UVP
|
|
uvp_match = re.search(r'<span class="uvp">(.*?)</span>', block_html)
|
|
uvp = uvp_match.group(1) if uvp_match else ''
|
|
|
|
# Extract badge (discount %)
|
|
badge_match = re.search(r'<span class="badge">(.*?)</span>', block_html)
|
|
badge = badge_match.group(1) if badge_match else ''
|
|
|
|
# Extract link
|
|
link_match = re.search(r'<a href="([^"]+)"[^>]*>Zu Amazon →</a>', block_html)
|
|
link = link_match.group(1) if link_match else ''
|
|
|
|
# Calculate savings
|
|
savings = ''
|
|
if price and uvp:
|
|
try:
|
|
price_val = float(price.replace('€', '').replace(',', '.').strip())
|
|
uvp_val = float(uvp.replace('UVP ', '').replace('€', '').replace(',', '.').strip())
|
|
saved = round(uvp_val - price_val, 2)
|
|
savings = f'({saved:.2f} € gespart)'
|
|
except:
|
|
pass
|
|
|
|
# Build standard block
|
|
block = f'''<div style="display:flex;gap:1rem;padding:1rem 0;border-bottom:1px solid #eee;">
|
|
<img src="{img_src}" alt="" style="width:100px;height:100px;object-fit:contain;">
|
|
<div style="flex:1;"><strong>{title}</strong><br>
|
|
<span style="color:#b12704;font-size:1.2rem;font-weight:bold;">{price}</span>
|
|
<span style="text-decoration:line-through;color:#999;margin-left:0.5rem;">{uvp}</span>
|
|
<span style="background:#b12704;color:#fff;padding:2px 6px;border-radius:3px;margin-left:0.5rem;font-size:0.85rem;">{badge}</span>'''
|
|
|
|
if savings:
|
|
block += f'\n<span style="color:#b12704;margin-left:0.3rem;">{savings}</span>'
|
|
|
|
block += f'<br>\n<a href="{link}" target="_blank" style="color:#007185;">Zu Amazon →</a></div></div>'
|
|
|
|
return block
|
|
|
|
def main():
|
|
# Read all 4 files
|
|
files = {
|
|
'mass_market': '/home/hermes/workspace/amazon_deals_2026-06-23.html',
|
|
'kennerspiele': '/home/hermes/workspace/kennerspiele_deals_2026-06-23.html',
|
|
'top_deals': '/home/hermes/workspace/top_deals_2026-06-23.html',
|
|
'devices': '/home/hermes/workspace/amazon_devices_prime_2026-06-23.html',
|
|
}
|
|
|
|
contents = {}
|
|
for key, path in files.items():
|
|
with open(path, 'r') as f:
|
|
contents[key] = f.read()
|
|
|
|
# Extract deal blocks from each file
|
|
mass_market_blocks = extract_deal_blocks_full(contents['mass_market'])
|
|
kennerspiele_blocks = extract_deal_blocks_full(contents['kennerspiele'])
|
|
|
|
# For top_deals, extract blocks by category
|
|
top_html = contents['top_deals']
|
|
|
|
# Extract sections from top_deals
|
|
# Heißluftfritteusen
|
|
hf_section = re.search(r'<h2[^>]*>Heißluftfritteusen.*?</h2>(.*?)(?=<h2)', top_html, re.DOTALL)
|
|
hf_blocks = extract_deal_blocks_full(hf_section.group(1)) if hf_section else []
|
|
|
|
# Staubsauger
|
|
st_section = re.search(r'<h2[^>]*>Staubsauger.*?</h2>(.*?)(?=<h2)', top_html, re.DOTALL)
|
|
st_blocks = extract_deal_blocks_full(st_section.group(1)) if st_section else []
|
|
|
|
# Laptops
|
|
lp_section = re.search(r'<h2[^>]*>Laptops.*?</h2>(.*?)(?=<h2)', top_html, re.DOTALL)
|
|
lp_blocks = extract_deal_blocks_full(lp_section.group(1)) if lp_section else []
|
|
|
|
# Tablets
|
|
tb_section = re.search(r'<h2[^>]*>Tablets.*?</h2>(.*?)(?=<h2)', top_html, re.DOTALL)
|
|
tb_blocks = extract_deal_blocks_full(tb_section.group(1)) if tb_section else []
|
|
|
|
# Smartphones
|
|
sp_section = re.search(r'<h2[^>]*>Smartphones.*?</h2>(.*?)(?=<h2)', top_html, re.DOTALL)
|
|
sp_blocks = extract_deal_blocks_full(sp_section.group(1)) if sp_section else []
|
|
|
|
# Apple
|
|
ap_section = re.search(r'<h2[^>]*>Apple.*?</h2>(.*?)(?=<p class="meta")', top_html, re.DOTALL)
|
|
ap_blocks = extract_deal_blocks_full(ap_section.group(1)) if ap_section else []
|
|
|
|
# Devices - extract all card blocks
|
|
device_blocks_raw = extract_deal_blocks_devices(contents['devices'])
|
|
device_blocks = [convert_device_block_to_standard(b) for b in device_blocks_raw]
|
|
|
|
# Count totals
|
|
total = (len(kennerspiele_blocks) + len(mass_market_blocks) +
|
|
len(hf_blocks) + len(st_blocks) + len(lp_blocks) +
|
|
len(tb_blocks) + len(sp_blocks) + len(ap_blocks) +
|
|
len(device_blocks))
|
|
|
|
print(f"Kennerspiele: {len(kennerspiele_blocks)}")
|
|
print(f"Mass-Market Brettspiele: {len(mass_market_blocks)}")
|
|
print(f"Heißluftfritteusen: {len(hf_blocks)}")
|
|
print(f"Staubsauger: {len(st_blocks)}")
|
|
print(f"Laptops: {len(lp_blocks)}")
|
|
print(f"Tablets: {len(tb_blocks)}")
|
|
print(f"Smartphones: {len(sp_blocks)}")
|
|
print(f"Apple: {len(ap_blocks)}")
|
|
print(f"Devices: {len(device_blocks)}")
|
|
print(f"TOTAL: {total}")
|
|
|
|
# Build the article
|
|
article = []
|
|
|
|
# Title and intro
|
|
article.append('<h1>Prime Days Sommer 2026: Die große Brettspiel- & Technik-Deal-Übersicht</h1>')
|
|
article.append('')
|
|
article.append('<p>Die Amazon Prime Days Sommer 2026 sind da! Wir haben für euch die besten Deals aus den Bereichen Brettspiele, Kennerspiele, Haushaltselektronik und Technik zusammengestellt. Insgesamt erwarten euch <strong>über 100 Angebote</strong> — von familienfreundlichen Brettspiel-Klassikern über anspruchsvolle Kennerspiele mit BGG-Ranking bis hin zu Heißluftfritteusen, Staubsaugern, Laptops, Tablets, Smartphones und Apple-Produkten. Alle Preise sind Momentaufnahmen und können sich schnell ändern — also schnell zugreifen!</p>')
|
|
article.append('')
|
|
article.append('<hr id="system-readmore" />')
|
|
article.append('')
|
|
|
|
# Section 1: Kennerspiele
|
|
article.append('<h2>🔥 Kennerspiele mit BGG-Ranking</h2>')
|
|
article.append('<p>Für alle Vielspieler und Strategen: Hier kommen die anspruchsvolleren Brettspiele mit BGG-Weight-Rating. Viele davon sind exklusive Prime-Day-Angebote (⚡) — also nur für Prime-Mitglieder!</p>')
|
|
article.append('')
|
|
for block in kennerspiele_blocks:
|
|
article.append(block)
|
|
article.append('')
|
|
|
|
# Section 2: Mass-Market Brettspiele
|
|
article.append('<h2>🎲 Alle Brettspiel-Angebote</h2>')
|
|
article.append('<p>Familienspiele, Klassiker und Partyspiele — hier ist für jeden Spieleabend etwas dabei. Von Monopoly über Catan bis hin zu UNO und Trivial Pursuit.</p>')
|
|
article.append('')
|
|
for block in mass_market_blocks:
|
|
article.append(block)
|
|
article.append('')
|
|
|
|
# Section 3: Heißluftfritteusen
|
|
article.append('<h2>🍟 Heißluftfritteusen</h2>')
|
|
article.append('<p>Knusprig, schnell und energiesparend: Die besten Airfryer-Deals der Prime Days. Von Tefal über Ninja bis Philips — hier sind die Top-Angebote.</p>')
|
|
article.append('')
|
|
for block in hf_blocks:
|
|
article.append(block)
|
|
article.append('')
|
|
|
|
# Section 4: Staubsauger
|
|
article.append('<h2>🧹 Staubsauger & Wischroboter</h2>')
|
|
article.append('<p>Saugroboter, Akku-Staubsauger und klassische Bodenstaubsauger — die besten Deals für ein sauberes Zuhause.</p>')
|
|
article.append('')
|
|
for block in st_blocks:
|
|
article.append(block)
|
|
article.append('')
|
|
|
|
# Section 5: Laptops
|
|
article.append('<h2>💻 Laptops</h2>')
|
|
article.append('<p>Notebooks und Laptops für Arbeit, Studium und Freizeit — mit kräftigen Rabatten zum Prime Day.</p>')
|
|
article.append('')
|
|
for block in lp_blocks:
|
|
article.append(block)
|
|
article.append('')
|
|
|
|
# Section 6: Tablets
|
|
article.append('<h2>📱 Tablets</h2>')
|
|
article.append('<p>Android-Tablets und Samsung Galaxy Tabs — perfekt für Unterhaltung unterwegs.</p>')
|
|
article.append('')
|
|
for block in tb_blocks:
|
|
article.append(block)
|
|
article.append('')
|
|
|
|
# Section 7: Smartphones
|
|
article.append('<h2>📱 Smartphones</h2>')
|
|
article.append('<p>Samsung, Xiaomi und Motorola — die besten Smartphone-Deals zum Prime Day.</p>')
|
|
article.append('')
|
|
for block in sp_blocks:
|
|
article.append(block)
|
|
article.append('')
|
|
|
|
# Section 8: Apple
|
|
article.append('<h2>🍎 Apple-Produkte</h2>')
|
|
article.append('<p>iPhones und iPads zu reduzierten Preisen — die besten Apple-Deals der Prime Days.</p>')
|
|
article.append('')
|
|
for block in ap_blocks:
|
|
article.append(block)
|
|
article.append('')
|
|
|
|
# Section 9: Amazon Devices & Gaming
|
|
article.append('<h2>📦 Gaming & Zubehör</h2>')
|
|
article.append('<p>Gaming-Headsets, Controller, Nintendo Switch 2 und weiteres Zubehör — die besten Deals für Gamer.</p>')
|
|
article.append('')
|
|
for block in device_blocks:
|
|
article.append(block)
|
|
article.append('')
|
|
|
|
# Footer
|
|
article.append('<p style="margin-top:2rem;color:#999;font-size:0.85rem;">Quellen: Amazon.de über CreatorAPI. Preise Stand 23.06.2026 07:00 Uhr. Preise und Verfügbarkeit können sich schnell ändern. Über die Partner-Links (Tag 60pro05-21) kauft ihr zum gleichen Preis — Amazon zahlt eine kleine Provision.</p>')
|
|
|
|
full_article = '\n'.join(article)
|
|
|
|
# Split at readmore
|
|
parts = full_article.split('<hr id="system-readmore" />')
|
|
introtext = parts[0].strip() + '\n<hr id="system-readmore" />'
|
|
fulltext = parts[1].strip() if len(parts) > 1 else ''
|
|
|
|
# Save the article
|
|
with open('/home/hermes/workspace/combined_article_introtext.html', 'w') as f:
|
|
f.write(introtext)
|
|
with open('/home/hermes/workspace/combined_article_fulltext.html', 'w') as f:
|
|
f.write(fulltext)
|
|
with open('/home/hermes/workspace/combined_article_full.html', 'w') as f:
|
|
f.write(full_article)
|
|
|
|
print(f"\nIntrotext length: {len(introtext)} chars")
|
|
print(f"Fulltext length: {len(fulltext)} chars")
|
|
print(f"Total article length: {len(full_article)} chars")
|
|
|
|
# Save JSON payload for Joomla API
|
|
payload = {
|
|
"title": "Prime Days Sommer 2026: Die große Brettspiel- & Technik-Deal-Übersicht",
|
|
"introtext": introtext,
|
|
"fulltext": fulltext,
|
|
"catid": 61,
|
|
"state": 0,
|
|
"access": 6,
|
|
"created_by": 560,
|
|
"language": "de",
|
|
"featured": 0
|
|
}
|
|
|
|
with open('/home/hermes/workspace/joomla_payload.json', 'w') as f:
|
|
json.dump(payload, f, ensure_ascii=False, indent=2)
|
|
|
|
print("\nPayload saved to joomla_payload.json")
|
|
|
|
if __name__ == '__main__':
|
|
main()
|