Zum Hauptinhalt springen

Chrome-Steuerung (puppeteer)

A-Parser + Puppeteer

A-Parser ermöglicht die Verwendung des Chrome (Chromium) Browsers als Engine zum Herunterladen und Rendern von Seiten unter Verwendung der beliebten Bibliothek puppeteer.

Hauptvorteile der Arbeit von puppeteer in Verbindung mit A-Parser:

  • Unterstützung für separate Proxys für jeden Browser-Tab
  • Multithreaded-Verwaltung von Browser-Tabs
  • Abfangen von Anfragen
  • Alle Funktionen von A-Parser zur Verwaltung der Warteschlange, zur Erstellung von Anfragen und zur Verarbeitung von Ergebnissen

Die Verwendung des Chrome Browsers eröffnet folgende Möglichkeiten:

  • Rendering von DOM und JavaScript
  • Möglichkeit zur interaktiven Arbeit mit Website-Elementen:
    • Ausfüllen von Formularen
    • Klicken auf Links
    • drag & drop
    • Hochladen von Dateien
    • Mausemulation
    • und vieles mehr, alle Standardaktionen können automatisiert werden
  • Einfacheres Umgehen verschiedener Schutzmechanismen gegen Datenerfassung, da der Chromium-Browser dem von Benutzern verwendeten Browser maximal ähnlich ist
  • Möglichkeit der Arbeit im Headless-Modus, d. h. ohne grafische Benutzeroberfläche des Browsers, was Ressourcen spart und den Betrieb des Browsers auf Servern ohne grafische Umgebung ermöglicht
Hinweis

Es muss berücksichtigt werden, dass der Betrieb des Browsers wesentlich ressourcenintensiver (CPU, Memory) ist als gewöhnliche A-Parser-Threads.

Abhängig von der Komplexität der Website wird empfohlen, nicht mehr als 1-2 Threads (Browser-Tabs) pro verfügbarem Prozessorkern zu verwenden, zum Beispiel bei 8-Kern-Prozessoren - 8 bis 16 Tabs.

Anwendungsbeispiel

Betrachten wir das Beispiel des Scrapers Chrome::ScreenshotMaker2:

  • Der Scraper erstellt Screenshots von Websites in der angegebenen Größe und kann das Bild optional verkleinern (skalieren)
  • Kann optional Proxys verwenden
  • Erstellt einen separaten Browser-Tab für jeden Thread des A-Parsers
import { BaseParser, PuppeteerTypes } from 'a-parser-types';

let browser: PuppeteerTypes.Browser;
let jimp;

class JS_Chrome_ScreenshotsMaker2 extends BaseParser {
static defaultConf: typeof BaseParser.defaultConf = {
version: '0.2.1',
results: {
flat: [
['screenshot', 'PNG screenshot'],
]
},
results_format: '$screenshot',
load_timeout: 30,
width: 1024,
height: 768,
log_screenshots: 0,
headless: 1,
};

static editableConf: typeof BaseParser.editableConf = [
['log_screenshots', ['checkbox', 'Log Screenshots']],
['width', ['textfield', 'Viewport Width']],
['height', ['textfield', 'Viewport Height']],
['resize_width', ['textfield', 'Resize Width']],
['resize_height', ['textfield', 'Resize Height']],
['headless', ['checkbox', 'Chrome Headless']],
];

async init() {
// Browser initialisieren
browser = await this.puppeteer.launch({
headless: this.conf.headless,
logConnections: false,
defaultViewport: {
width: parseInt(this.conf.width),
height: parseInt(this.conf.height),
}
});

if (this.conf.resize_width) {
// Jimp-Modul einbinden, falls ein Resize des Screenshots erforderlich ist
jimp = require('jimp');
};
};

async destroy() {
// Browser nach Abschluss der Aufgabe schließen
if (browser)
await browser.close();
}

page: PuppeteerTypes.Page;

async threadInit() {
// Browserseite bei Thread-Initialisierung erstellen
this.page = await browser.newPage();

// Standard-Puppeteer-Methoden
await this.page.setCacheEnabled(true);
await this.page.setDefaultNavigationTimeout(this.conf.timeout * 1000);

// A-Parser anweisen, Proxy für diese Seite zu verwenden
await this.puppeteer.setPageUseProxy(this.page);


this.logger.put(`New page created for thread #${this.threadId}`);
}

async parse(set, results) {
const self = this;
const { conf, page } = self;

for (let attempt = 1; attempt <= conf.proxyretries; attempt++) {
try {
self.logger.put(`Attempt #${attempt}`);

// Zur in der Anfrage angegebenen Seite navigieren
await page.goto(set.query);
// Scrollbar für den Screenshot ausblenden
await page.evaluate(() => { document.querySelector('html').style.overflow = 'hidden'; });

// Screenshot aufnehmen
results.screenshot = await page.screenshot();

if (parseInt(conf.resize_width)) {
// Bild bei Bedarf in der Größe anpassen
let image = await jimp.read(results.screenshot);
image.resize(parseInt(conf.resize_width), parseInt(conf.resize_height));
results.screenshot = await image.getBufferAsync('image/png');
}

self.logger.put(`Screenshot(${attempt}): OK, size: ${parseInt("" + (results.screenshot.length / 1024))}KB`);
if (conf.log_screenshots)
self.logger.putHTML("<img src='data:image/png;base64," + results.screenshot.toString('base64') + "'>");

results.success = 1;

// Aktuelle Verbindungen schließen, da der Browser Keep-Alive verwendet
await self.puppeteer.closeActiveConnections();
break;
}
catch (error) {
self.logger.put(`Fetch page error: ${error}`);
// Aktuelle Verbindungen schließen, da der Browser Keep-Alive verwendet
await self.puppeteer.closeActiveConnections();
// Proxy für den Browser-Tab wechseln
await self.proxy.next();
}
}

return results;
}
}

Dieses Beispiel demonstriert die einfache Verwendung verschiedener Proxys für jeden Tab sowie den Multithread-Betrieb (1 Thread = 1 Browser-Tab).

Beschreibung der Methoden

await this.puppeteer.launch(opts?)

Diese Methode ist analog zur Methode .launch der Bibliothek puppeteer; sie startet den Chromium Browser mit den erforderlichen Optionen opts. Der Hauptunterschied besteht in der Integration mit A-Parser und der Unterstützung von Proxys für jeden Tab sowie in der Verfügbarkeit zusätzlicher Optionen:

logConnections?: boolean

Aktiviert die Protokollierung aller Verbindungen (unabhängig davon, ob ein Proxy verwendet wird oder nicht), die Log-Ausgabe erfolgt getrennt nach Threads.

stealth?: boolean

Verwendet das Plugin puppeteer-extra, um Chromium als echtes Chrome zu tarnen.

stealthOpts?: any

Zusätzliche Optionen für das Plugin puppeteer-extra.

extraPlugins?: array

Verwendung zusätzlicher Plugins, wie zum Beispiel puppeteer-extra/packages.

A-Parser + Puppeteer

Weitere Optionen

Alle weiteren Startoptionen können in der Originaldokumentation von puppeteer eingesehen werden.

await this.puppeteer.setPageUseProxy(page)

Diese Methode verknüpft die Browserseite mit dem A-Parser-Thread für den korrekten Betrieb des Proxys; sie muss unmittelbar nach dem Erstellen der Seite aufgerufen werden:

const page = await browser.newPage();
await this.puppeteer.setPageUseProxy(page);

await this.puppeteer.closeActiveConnections(page?)

Diese Methode muss nach Abschluss der Anfrageverarbeitung oder vor dem Wechsel des Proxys für den nächsten Versuch aufgerufen werden.

Der Chrome Browser lässt standardmäßig Verbindungen zu den Websites offen, mit denen er sich verbindet. Diese Methode ermöglicht die Kontrolle über die Anzahl der verwendeten Ressourcen und reduziert die Last auf die Proxys.

Das Argument page ist optional; beim Aufruf ohne Argument schließt A-Parser die Verbindungen für den Tab, der mit dem aktuellen Thread verknüpft ist.

await this.puppeteer.logScreenshot()

Die Methode protokolliert einen Screenshot der aktuellen Seite.