Zum Hauptinhalt springen

Social Scraping

Die Scraping-Services bilden die Datenbeschaffungsschicht von Postbox. Sie transformieren API-Responses und HTML-Payloads in ein einheitliches ScrapedProfile-DTO und verwalten den Download sowie die Versionierung von Profil-Bildern und Video-Thumbnails.

ScrapedProfile (DTO)

Location: app/Services/Social/Scrapers/ScrapedProfile.php

Readonly Data Transfer Object, das als einheitliche Schnittstelle zwischen Scrapern und dem restlichen System dient. Alle Scraper liefern eine Instanz dieses DTOs zurück.

final readonly class ScrapedProfile
{
public function __construct(
public SocialPlatform $platform,
public string $handle,
public string $canonicalUrl,
public int $followersCount,
public ?string $externalId = null,
public ?int $viewCount = null,
public ?int $videoCount = null,
public ?int $commentCount = null,
public ?string $title = null,
public ?string $description = null,
public ?string $thumbnailUrl = null,
public ?string $customUrl = null,
public ?string $country = null,
public ?string $language = null,
public ?string $publishedAt = null,
public array $profileData = [],
public array $dailyRaw = [],
public ?int $followingCount = null,
public ?int $postCount = null,
public ?bool $isPrivate = null,
public ?bool $isVerified = null,
public ?string $profileType = null,
) {}
}

Plattform-spezifische Felder: viewCount, videoCount, commentCount werden von YouTube befüllt; followingCount, postCount, isPrivate, isVerified, profileType von Instagram.

YouTubeProfileScraper

Location: app/Services/Social/Scrapers/YouTubeProfileScraper.php

Mapped die YouTube Data API v3 Channel-Response auf ein ScrapedProfile. Nutzt den YouTubeChannelResolver zur URL-Auflösung und extrahiert snippet + statistics Daten.

Public Methods

MethodParameterReturnBeschreibung
scrape()string $urlScrapedProfileYouTube-Kanal scrapen und als DTO zurückgeben

Verarbeitungslogik

  • Resolve der URL via YouTubeChannelResolver::resolve()
  • Extraktion von Channel-ID, Handle (mit @-Prefix-Bereinigung), Custom URL
  • Subscriber Count Prüfung: wirft RuntimeException bei hiddenSubscriberCount
  • Thumbnail-Auswahl in Priorität: maxres > standard > high > medium > default
  • dailyRaw enthält die rohen statistics für historische Vergleiche

Abhängigkeiten

  • YouTubeChannelResolver (Constructor Injection)

Verwendet von

  • ScraperFactory, EnsureSocialProfileFromUrl, SocialScrapeDailyFollowers-Command

InstagramProfileScraper

Location: app/Services/Social/Scrapers/InstagramProfileScraper.php

Scraped Instagram-Profile über den externen Collector-Service. Die eigentliche Datenerhebung erfolgt asynchron via CollectorJobDispatcher; der Scraper verarbeitet die Collector-Response.

Public Methods

MethodParameterReturnBeschreibung
scrape()string $urlScrapedProfileInstagram-Profil scrapen und als DTO zurückgeben

Abhängigkeiten

  • Collector-API (externer Service)

Verwendet von

  • ScraperFactory, InstagramDailyScrapeProcessor, InstagramWatcherImportProcessor

ScraperFactory

Location: app/Services/Social/ScraperFactory.php

Factory, die anhand der Plattform (youtube, instagram) den korrekten Scraper instanziiert und den Scrape-Vorgang delegiert.

Public Methods

MethodParameterReturnBeschreibung
scrape()string $platform, string $urlScrapedProfilePlattform-spezifischen Scraper aufrufen

ProfileImageUpdater

Location: app/Services/Social/ProfileImageUpdater.php

Verwaltet den Download und die Speicherung von Profil-Bildern. Bilder werden als lokale Dateien gespeichert. Beim Update wird ein Hash-Vergleich durchgeführt, um unnötige Überschreibungen zu vermeiden. Nach dem Speichern werden automatisch optimierte Varianten (Thumbnail + Medium) über den ProfileImageProcessor erzeugt.

Public Methods

MethodParameterReturnBeschreibung
storeProfileImage()SocialProfile $profile, string $imageUrlboolBild herunterladen, Hash vergleichen, bei Änderung speichern und Varianten generieren

Funktionsweise

  1. Download des Bildes von der Source-URL (YouTube API Thumbnail oder Instagram CDN)
  2. SHA-256 Hash des neuen Bildes berechnen
  3. Vergleich mit dem gespeicherten Hash aus profile_image_hash
  4. Bei Änderung: neues Bild speichern, Hash aktualisieren, alte Varianten löschen
  5. ProfileImageProcessor::generateVariants() erzeugt Thumbnail (128×128) + Medium (256×256) als WebP
  6. LQIP (4×4 Base64 WebP) wird als Blur-Placeholder gespeichert

Abhängigkeiten

  • Laravel Storage (public disk)
  • SocialProfile, SocialProfileImage Models
  • ProfileImageProcessor (Image Variants + LQIP)

Verwendet von

  • EnsureSocialProfileFromUrl, InstagramDailyScrapeProcessor, Daily Scrape Jobs

VideoThumbnailUpdater

Location: app/Services/Social/VideoThumbnailUpdater.php

Lädt YouTube Video-Thumbnails in maximaler Auflösung (maxres) herunter und speichert sie lokal. Erzeugt automatisch 16:9-Varianten (Medium 640×360, Thumbnail 320×180) über den ProfileImageProcessor.

Public Methods

MethodParameterReturnBeschreibung
storeVideoThumbnail()YouTubeVideo $video, string $thumbnailUrlboolThumbnail herunterladen, speichern und Varianten generieren

Abhängigkeiten

  • Laravel Storage (public disk)
  • YouTubeVideo Model
  • ProfileImageProcessor (Video Thumbnail Variants)

Verwendet von

  • SyncYouTubeVideoStats-Command, Video-Import Jobs

ProfileImageProcessor

Location: app/Services/Images/ProfileImageProcessor.php

Zentraler Service für die Erzeugung optimierter Bild-Varianten. Nutzt intervention/image v3 für serverseitige Bildverarbeitung. Unterstützt WebP und AVIF (konfigurierbar). Erzeugt LQIP (Low Quality Image Placeholder) als 4×4 Base64 WebP Data URI.

Public Methods

MethodParameterReturnBeschreibung
generateVariants()SocialProfileImage $image, string $rawBytesvoidMedium + Thumbnail Varianten für Profilbilder erzeugen
generateVideoThumbnailVariants()YouTubeVideo $video, string $rawBytesvoid16:9-Varianten für Video-Thumbnails erzeugen
regenerateFromDisk()SocialProfileImage $imageboolVarianten aus existierender Datei regenerieren (Backfill)
regenerateVideoThumbnailFromDisk()YouTubeVideo $videoboolVideo-Varianten aus existierender Datei regenerieren
deleteVariants()SocialProfileImage $imagevoidVarianten-Dateien löschen, Original behalten
deleteVideoThumbnailVariants()YouTubeVideo $videovoidVideo-Varianten-Dateien löschen

Konfiguration

Alle Werte über config('postbox.images'):

KeyDefaultBeschreibung
drivergdImage-Driver (gd oder imagick)
quality80Encode-Qualität (0–100)
thumb_size128Thumbnail-Seitenlänge in px
medium_size256Medium-Seitenlänge in px
formatwebpZielformat (webp oder avif)

Varianten-Größen

KontextThumbnailMedium
Profilbilder128×128 (1:1)256×256 (1:1)
Video-Thumbnails320×180 (16:9)640×360 (16:9)

Abhängigkeiten

  • intervention/image v3 (GD oder Imagick)
  • Laravel Storage (public disk)

Verwendet von

  • ProfileImageUpdater, VideoThumbnailUpdater, GenerateImageVariants (Backfill Command)

Zusammenspiel der Scraping-Pipeline

graph TD
A[URL] --> B[ScraperFactory]
B --> C[YouTubeProfileScraper]
B --> D[InstagramProfileScraper]
C --> E[ScrapedProfile DTO]
D --> E
E --> F[EnsureSocialProfileFromUrl]
F --> G[ProfileImageUpdater]
F --> H[SocialProfileDailyMetric]
G --> I[Download + Hash-Dedup]
I --> J[ProfileImageProcessor]
J --> K["Medium WebP (256×256)"]
J --> L["Thumbnail WebP (128×128)"]
J --> M["LQIP Base64 (4×4)"]

Der typische Flow: Eine URL wird via ScraperFactory an den passenden Scraper delegiert, der ein ScrapedProfile zurückgibt. EnsureSocialProfileFromUrl persistiert dann das Profil, erstellt die tägliche Metrik und aktualisiert das Profil-Bild über ProfileImageUpdater. Der ProfileImageUpdater speichert das Original und ruft den ProfileImageProcessor auf, der die optimierten Varianten und das LQIP generiert.