Automatiser des profils de navigateur avec une API locale

Démarrez un profil depuis un script, récupérez un point de terminaison de débogage, et connectez Playwright ou Puppeteer à un navigateur qui a déjà son identité et son proxy.
On présente souvent l'automatisation d'un navigateur antidétection comme un problème difficile. Ce n'est pas le cas, à condition que l'outil vous remette la seule chose que votre bibliothèque d'automatisation sait déjà utiliser : un point de terminaison de débogage.
Le principe
PowerOps exécute une API HTTP liée à l'adresse de bouclage. Votre script lui demande de démarrer un profil ; elle lance le navigateur avec l'identité, le stockage et le proxy de ce profil déjà en place, et renvoie le point de terminaison WebSocket du navigateur en cours d'exécution. Votre script s'y connecte avec Playwright, Puppeteer ou tout autre outil qui parle le protocole DevTools.
La bibliothèque d'automatisation n'a jamais besoin de savoir ce qu'est une empreinte. Quand elle se connecte, l'environnement est déjà établi.
Démarrer un profil
curl -s -H "Authorization: Bearer $POWEROPS_API_KEY" \
http://127.0.0.1:50326/status
Chaque route, sauf la vérification d'état, exige une clé bearer. Démarrer un navigateur renvoie le point de terminaison auquel se connecter :
curl -s -X POST \
-H "Authorization: Bearer $POWEROPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"profileId": "..."}' \
http://127.0.0.1:50326/browser/start
Connecter votre bibliothèque
import { chromium } from "playwright";
const res = await fetch("http://127.0.0.1:50326/browser/start", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.POWEROPS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ profileId }),
});
const { ws } = await res.json();
// The profile's identity, storage and proxy are already in place.
const browser = await chromium.connectOverCDP(ws);
const [context] = browser.contexts();
const page = context.pages()[0] ?? await context.newPage();
await page.goto("https://example.com");
La ligne importante est le commentaire. Il n'y a aucune configuration d'empreinte dans votre script, parce que configurer l'environnement n'est pas le travail de votre script. Cela appartient au profil, et reste identique que le profil soit ouvert par une personne qui clique sur une ligne ou par une tâche planifiée à trois heures du matin.
Ce que couvre l'API
- La route d'état est une vérification de santé, et la seule qui n'exige aucune clé.
- Les routes de navigateur démarrent, arrêtent et interrogent les navigateurs actuellement en cours d'exécution.
- Les routes de profil créent, modifient, interrogent et suppriment des profils.
- Les routes de proxy gèrent la bibliothèque de proxys et attribuent les routes.
- Les routes de groupe organisent les profils en masse.
- Les routes d'extension importent et attribuent des extensions.
Comment elle reste locale
Une API qui pilote des navigateurs contenant des comptes connectés mérite qu'on parle de ses limites.
- Elle se lie à 127.0.0.1. Aucun réglage ne la fait écouter sur une autre interface.
- Une clé bearer est exigée sur chaque route sauf la vérification d'état.
- Elle valide l'en-tête Host et n'envoie aucun en-tête CORS, de sorte qu'une page web ouverte dans votre navigateur habituel ne peut pas l'atteindre.
- Les secrets sont en écriture seule : les mots de passe de profil, les secrets d'authentification à deux facteurs et les mots de passe de proxy ne sont jamais renvoyés par une route de consultation.
Un schéma à reprendre
Interrogez les profils d'un groupe, démarrez-les un à un, faites le travail, arrêtez-les. Lancez-en quelques-uns à la fois plutôt que tous en même temps. La contrainte est la mémoire de votre propre machine, puisque chaque profil est un véritable processus de navigateur.
const { profiles } = await api("/profiles?groupId=" + groupId);
for (const batch of chunk(profiles, 5)) {
await Promise.all(batch.map(async (profile) => {
const { ws } = await api("/browser/start", { profileId: profile.id });
try {
await doTheWork(ws);
} finally {
await api("/browser/stop", { profileId: profile.id });
}
}));
}
Arrêter le profil dans un `finally` compte plus qu'il n'y paraît : une fermeture propre est ce qui écrit l'état du navigateur, et un profil tué en pleine session peut perdre la session qu'il vient d'établir.
