fix: QC-Ratings aus 7 älteren Artikeln entfernt (CHECK 10)

This commit is contained in:
Hermes Agent
2026-06-23 23:47:51 +02:00
parent e73e0e88de
commit 4d90e4249d
656 changed files with 2602398 additions and 3 deletions
+200
View File
@@ -0,0 +1,200 @@
#!/usr/bin/env python3
"""Scrape YouTube trending videos for Germany using InnerTube API with curl_cffi."""
import json
import re
import sys
from curl_cffi import requests
def get_api_key(session):
"""Extract the InnerTube API key from YouTube's homepage."""
r = session.get("https://www.youtube.com/", impersonate="chrome120")
if r.status_code != 200:
print(f"ERROR: Got status {r.status_code} from YouTube homepage", file=sys.stderr)
return None
match = re.search(r'"INNERTUBE_API_KEY"\s*:\s*"([^"]+)"', r.text)
if match:
return match.group(1)
# Try alternate pattern
match = re.search(r'"innertubeApiKey"\s*:\s*"([^"]+)"', r.text)
if match:
return match.group(1)
return None
def get_trending_videos(session, api_key):
"""Fetch trending videos via InnerTube API."""
url = f"https://www.youtube.com/youtubei/v1/browse?key={api_key}"
headers = {
"Content-Type": "application/json",
"Accept-Language": "de-DE,de;q=0.9",
"X-YouTube-Client-Name": "1",
"X-YouTube-Client-Version": "2.20260609.07.00",
"Origin": "https://www.youtube.com",
"Referer": "https://www.youtube.com/feed/trending"
}
body = {
"context": {
"client": {
"hl": "de",
"gl": "DE",
"clientName": "WEB",
"clientVersion": "2.20260609.07.00",
"utcOffsetMinutes": 120
}
},
"browseId": "FEtrending"
}
r = session.post(url, headers=headers, json=body, impersonate="chrome120")
if r.status_code != 200:
print(f"ERROR: Got status {r.status_code} from trending API", file=sys.stderr)
print(f"Response: {r.text[:500]}", file=sys.stderr)
return None
return r.json()
def extract_videos(data):
"""Extract video information from the browse response."""
videos = []
def find_video_renderers(obj):
if isinstance(obj, dict):
if 'videoRenderer' in obj:
vr = obj['videoRenderer']
video_id = vr.get('videoId', 'N/A')
title_runs = vr.get('title', {}).get('runs', [])
title = title_runs[0].get('text', 'N/A') if title_runs else 'N/A'
owner_runs = vr.get('ownerText', {}).get('runs', [])
channel = owner_runs[0].get('text', 'N/A') if owner_runs else 'N/A'
views_text = vr.get('viewCountText', {})
views = views_text.get('simpleText', 'N/A')
if views == 'N/A':
views_runs = views_text.get('runs', [])
views = ''.join(r.get('text', '') for r in views_runs) if views_runs else 'N/A'
desc_snippet = vr.get('descriptionSnippet', {})
desc_runs = desc_snippet.get('runs', [])
desc = ''.join(r.get('text', '') for r in desc_runs) if desc_runs else ''
videos.append({
'videoId': video_id,
'title': title,
'channel': channel,
'views': views,
'description_snippet': desc
})
for key, value in obj.items():
if key != 'videoRenderer':
find_video_renderers(value)
elif isinstance(obj, list):
for item in obj:
find_video_renderers(item)
find_video_renderers(data)
return videos
def get_video_description(session, api_key, video_id):
"""Fetch full video description via InnerTube player API."""
url = f"https://www.youtube.com/youtubei/v1/player?key={api_key}"
headers = {
"Content-Type": "application/json",
"Accept-Language": "de-DE,de;q=0.9",
"X-YouTube-Client-Name": "1",
"X-YouTube-Client-Version": "2.20260609.07.00",
"Origin": "https://www.youtube.com",
"Referer": f"https://www.youtube.com/watch?v={video_id}"
}
body = {
"context": {
"client": {
"hl": "de",
"gl": "DE",
"clientName": "WEB",
"clientVersion": "2.20260609.07.00",
"utcOffsetMinutes": 120
}
},
"videoId": video_id,
"racyCheckOk": True
}
r = session.post(url, headers=headers, json=body, impersonate="chrome120")
if r.status_code != 200:
print(f" ERROR: Got status {r.status_code} for video {video_id}", file=sys.stderr)
return None
try:
data = r.json()
except:
return None
# Extract description from videoDetails
video_details = data.get('videoDetails', {})
short_desc = video_details.get('shortDescription', '')
# Also try microformat
microformat = data.get('microformat', {}).get('playerMicroformatRenderer', {})
full_desc = microformat.get('description', {}).get('simpleText', '')
if not full_desc:
desc_runs = microformat.get('description', {}).get('runs', [])
full_desc = ''.join(r.get('text', '') for r in desc_runs)
return full_desc or short_desc
def main():
session = requests.Session()
session.headers.update({
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
})
print("Step 1: Getting API key...", file=sys.stderr)
api_key = get_api_key(session)
if not api_key:
print("ERROR: Could not get API key", file=sys.stderr)
sys.exit(1)
print(f"Got API key: {api_key[:15]}...", file=sys.stderr)
print("Step 2: Fetching trending videos...", file=sys.stderr)
data = get_trending_videos(session, api_key)
if not data:
print("ERROR: Could not fetch trending data", file=sys.stderr)
sys.exit(1)
videos = extract_videos(data)
print(f"Found {len(videos)} videos", file=sys.stderr)
# Print all found videos for debugging
for i, v in enumerate(videos[:10]):
print(f" {i+1}. [{v['videoId']}] {v['title'][:60]} - {v['channel']} ({v['views']})", file=sys.stderr)
top5 = videos[:5]
print("\nStep 3: Fetching full descriptions...", file=sys.stderr)
results = []
for i, v in enumerate(top5):
print(f" Fetching description for video {i+1}: {v['title'][:50]}...", file=sys.stderr)
full_text = get_video_description(session, api_key, v['videoId'])
desc_de = v.get('description_snippet', '')
if not desc_de and full_text:
sentences = re.split(r'(?<=[.!?])\s+', full_text)
desc_de = ' '.join(sentences[:3])
results.append({
"title": v['title'],
"channel": v['channel'],
"views": v['views'],
"description_de": desc_de[:300] if desc_de else "Keine Beschreibung verfügbar.",
"source_url": f"https://www.youtube.com/watch?v={v['videoId']}",
"full_text": full_text or "N/A",
"tags": ["YOUTUBE", "TRENDING", "DEUTSCHLAND"]
})
print("\n=== RESULTS ===", file=sys.stderr)
print(json.dumps(results, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()