cURL in JavaScript: fetch, Axios und Proxy

Veröffentlicht:

9 Min. Lesezeit

Acar Diveroli
Autor: Acar Diveroli
Kommandozeilen-Symbol zwischen geschweiften Klammern

API-Dokumentationen und die Entwicklertools des Browsers geben Beispielanfragen oft als cURL-Befehl an. Für einen schnellen Test im Terminal ist das ideal; müssen Sie dieselbe Anfrage aber in eine Node.js-Anwendung oder ein Scraping-Skript übertragen, müssen Sie den Befehl in JavaScript übersetzen. In diesem Beitrag erklären wir die JavaScript-Entsprechungen der meistgenutzten cURL-Optionen, zeigen Übersetzungsbeispiele, vier Wege für die Proxy-Nutzung in Node.js, die Fehlerbehandlung und häufige Fehler.

Alle Beispiele wurden mit Node.js 24, undici 8 und Axios 1.20 getestet.

Was ist das Gegenstück zu cURL in JavaScript?

Es gibt zwei Hauptoptionen:

  • fetch: Ist in Browsern und seit Node.js 18 nativ eingebaut. Kein zusätzliches Paket nötig. Node.js' fetch-Implementierung nutzt im Hintergrund die Bibliothek undici.
  • Axios: Eine seit Langem verwendete, populäre Bibliothek, die JSON automatisch umwandelt und Interceptor-Unterstützung für Anfragen und Antworten bietet.

Es ist auch möglich, ohne das Terminal zu verlassen, einen cURL-Befehl direkt weiterzuverwenden (mit child_process), doch dieser Weg erschwert Fehlerbehandlung und Portabilität; Sie bleiben davon abhängig, dass cURL auf der Maschine installiert ist. Mit einem nativen Client zu schreiben ist fast immer die bessere Wahl.

JavaScript-Entsprechungen der cURL-Optionen

cURLfetchAxios
curl URLfetch(url)axios.get(url)
-X POSTmethod: "POST"axios.post(url, daten)
-H "Name: Wert"headers: { Name: "Wert" }headers: { Name: "Wert" }
-d '{"a":1}'body: JSON.stringify({ a: 1 })Objekt als zweiter Parameter
-d "a=1&b=2"body: new URLSearchParams({ a: 1, b: 2 })new URLSearchParams(...) als zweiter Parameter
-F "datei=@a.png"body: FormDataFormData als zweiter Parameter
-u benutzer:passwortAuthorization: "Basic ..."-Headerauth: { username, password }
-b "name=wert" (Cookie)headers: { Cookie: "name=wert" }headers: { Cookie: "name=wert" }
-A "UA"headers: { "User-Agent": "UA" }headers: { "User-Agent": "UA" }
-L (Weiterleitung folgen)Folgt standardmäßigFolgt standardmäßig
-x proxydispatcher: new ProxyAgent(...)httpsAgent oder proxy
-m 20 (Timeout)signal: AbortSignal.timeout(20000)timeout: 20000
-i (Antwort-Header)response.headersresponse.headers
-o dateiBody mit fs.writeFile schreibenresponseType: "stream"

Beispiel: eine POST-Anfrage übersetzen

Betrachten wir folgenden cURL-Befehl:

bash
curl -X POST https://httpbin.org/post \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer TOKEN" \
  -d '{"q": 1}'

Mit fetch

javascript
const response = await fetch("https://httpbin.org/post", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer TOKEN",
  },
  body: JSON.stringify({ q: 1 }),
  signal: AbortSignal.timeout(20000),
});

if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
console.log(data.json);

fetch hat eine Besonderheit: Gibt der Server 404 oder 500 zurück, wird keine Exception geworfen. Ob die Antwort erfolgreich war, müssen Sie selbst über response.ok prüfen. Ein Netzwerkfehler (DNS, Verbindungsabbruch, Timeout) kommt hingegen als Exception.

Mit Axios

bash
npm install axios
javascript
import axios from "axios";

const { data } = await axios.post(
  "https://httpbin.org/post",
  { q: 1 },
  { headers: { Authorization: "Bearer TOKEN" }, timeout: 20000 },
);

console.log(data.json);

Axios wandelt das Objekt selbst in JSON um, fügt den Content-Type-Header hinzu und wirft bei Antworten außerhalb des 2xx-Bereichs eine Exception.

Beispiel: Formularübertragung und Datei-Upload

Eine Formularübertragung im Format -d "a=1&b=2" wird in JavaScript mit URLSearchParams abgebildet:

javascript
const response = await fetch("https://httpbin.org/post", {
  method: "POST",
  body: new URLSearchParams({ a: "1", b: "2" }),
});
console.log((await response.json()).form);

Wird ein URLSearchParams-Body übergeben, setzt fetch den Content-Type-Header selbst auf application/x-www-form-urlencoded. Für Datei-Uploads (-F) wird FormData verwendet, auch hier wird die Boundary automatisch eingefügt. Ein Detail: Beziehen Sie die fetch-Funktion aus dem undici-Paket, nehmen Sie auch die FormData-Klasse aus demselben Paket; die eingebaute FormData passt nicht zu undicis fetch, und der Body wird dann als reiner Text übertragen.

javascript
import { openAsBlob } from "node:fs";

const form = new FormData();
form.append("datei", await openAsBlob("./a.png"), "a.png");

const response = await fetch("https://httpbin.org/post", { method: "POST", body: form });
console.log(Object.keys((await response.json()).files));

Vier Wege, in Node.js einen Proxy zu nutzen

Bei cURL ist der Proxy eine einzelne Option -x. In Node.js hängt die Methode davon ab, welchen Client Sie verwenden.

1. fetch und undici ProxyAgent

bash
npm install undici
javascript
import { fetch, ProxyAgent } from "undici";

const dispatcher = new ProxyAgent("http://benutzer:passwort@pr.proxynet.io:8000");

const response = await fetch("https://httpbin.org/ip", { dispatcher });
console.log(await response.text());

Beachten Sie, dass wir auch die fetch-Funktion aus dem undici-Paket importieren. Node.js' eingebautes fetch basiert ebenfalls auf undici, doch da im separat installierten Paket ein Versionsunterschied bestehen kann, ist es am sichersten, beide aus demselben Paket zu beziehen.

2. Globale Einstellung für alle fetch-Aufrufe

Soll jeder fetch-Aufruf der Anwendung denselben Proxy verwenden, können Sie einen globalen Dispatcher definieren:

javascript
import { ProxyAgent, setGlobalDispatcher } from "undici";

setGlobalDispatcher(new ProxyAgent("http://benutzer:passwort@pr.proxynet.io:8000"));

const response = await fetch("https://httpbin.org/ip");
console.log(await response.text());

Diese Methode erfasst auch fetch-Aufrufe, die aus einer Bibliothek eines Drittanbieters heraus erfolgen; ohne den Quellcode der Bibliothek anzurühren, leiten Sie deren Traffic über den Proxy.

3. Über eine Umgebungsvariable, ohne Codeänderung

Aktuelle Node.js-Versionen können bei aktivierter Variable NODE_USE_ENV_PROXY für das eingebaute fetch die üblichen Proxy-Umgebungsvariablen lesen:

bash
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://benutzer:passwort@pr.proxynet.io:8000" node anwendung.mjs

Diese Methode ist nützlich, um ein bestehendes Skript ohne Quellcode-Änderung über einen Proxy zu leiten. Auch die Variable NO_PROXY wird gelesen; interne Netzwerkadressen können Sie dieser Liste hinzufügen, um sie vom Proxy auszunehmen. Ob die Funktion in Ihrer Version verfügbar ist, prüfen Sie in der Node.js-Dokumentation.

4. Axios und HTTPS-Ziele

Die eingebaute proxy-Option von Axios funktioniert für unverschlüsselte HTTP-Adressen problemlos:

javascript
const { data } = await axios.get("http://httpbin.org/ip", {
  proxy: {
    protocol: "http",
    host: "pr.proxynet.io",
    port: 8000,
    auth: { username: "benutzer", password: "passwort" },
  },
});

Bei HTTPS-Adressen ist ein Proxy-Agent verlässlicher:

bash
npm install https-proxy-agent
javascript
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const httpsAgent = new HttpsProxyAgent("http://benutzer:passwort@pr.proxynet.io:8000");

const { data } = await axios.get("https://httpbin.org/ip", { httpsAgent, proxy: false });
console.log(data);

Die Zeile proxy: false ist wichtig; sie schaltet Axios' eigene Proxy-Logik ab und überlässt den Tunnel vollständig dem Agenten. Erstellen Sie den Agenten einmal und binden ihn mit axios.create({ httpsAgent, proxy: false }) an eine Instanz, müssen Sie ihn nicht bei jedem Aufruf erneut schreiben.

Die vier Methoden im Vergleich

MethodeUmfangCodeänderungGeeignet für
undici ProxyAgentEinzelner Aufrufdispatcher bei jedem AufrufUnterschiedlicher Proxy pro Anfrage
setGlobalDispatcherAlle fetch-AufrufeEine Zeile beim StartEin Proxy für die gesamte Anwendung
NODE_USE_ENV_PROXYAlle eingebauten fetch-AufrufeKeineBestehendes Skript, Deployment-Umgebung
Axios + AgentAxios-InstanzhttpsAgent + proxy: falseWenn Axios im Projekt genutzt wird

Wiederholung und Nebenläufigkeit

In einem echten Scraping-Skript scheitern Anfragen gelegentlich; der Proxy erreicht das Ziel nicht, oder die Website liefert 429. Die folgende Hilfsfunktion wiederholt eine fehlgeschlagene Anfrage mit exponentiellem Backoff und begrenzt die Anzahl gleichzeitig offener Anfragen:

javascript
import { fetch, ProxyAgent } from "undici";

const dispatcher = new ProxyAgent("http://benutzer:passwort@pr.proxynet.io:8000");

async function getir(url, deneme = 3) {
  for (let i = 0; i < deneme; i++) {
    try {
      const r = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20000) });
      if (r.status === 429 || r.status >= 500) throw new Error(`HTTP ${r.status}`);
      return await r.text();
    } catch (hata) {
      if (i === deneme - 1) throw hata;
      await new Promise((coz) => setTimeout(coz, 1000 * 2 ** i));
    }
  }
}

async function topluGetir(urls, esZamanli = 5) {
  const sonuclar = [];
  for (let i = 0; i < urls.length; i += esZamanli) {
    const grup = urls.slice(i, i + esZamanli);
    sonuclar.push(...(await Promise.allSettled(grup.map(getir))));
  }
  return sonuclar;
}

const urls = Array.from({ length: 20 }, (_, i) => `https://httpbin.org/get?i=${i}`);
const sonuclar = await topluGetir(urls);
console.log(sonuclar.filter((s) => s.status === "fulfilled").length, "erfolgreich");

Promise.allSettled verhindert, dass der Fehler einer einzelnen Anfrage die ganze Gruppe zu Fall bringt; Sie können jedes Ergebnis einzeln prüfen. Die Wartezeit verdoppelt sich bei jedem Versuch; in konstanten Abständen zu beharren verschärft eine Website mit Rate-Limiting nur weiter.

Häufige Fehler

Sonderzeichen im Passwort

Enthält Ihr Proxy-Passwort Zeichen wie @, : oder /, kodieren Sie diese innerhalb der Adresse mit encodeURIComponent. Andernfalls wird die Adresse falsch geparst und Sie erhalten den Fehler "407 Proxy Authentication Required".

Anfragen ohne Timeout

In Standardeinstellungen kann eine Anfrage lange auf eine Antwort warten und Ihr Skript bleibt hängen. Setzen Sie bei fetch mit AbortSignal.timeout, bei Axios mit timeout für jede Anfrage eine Zeitgrenze.

Zu viele gleichzeitige Anfragen

Mit Promise.all Hunderte Anfragen auf einmal zu starten, überfordert sowohl Ihre eigenen Verbindungsgrenzen als auch führt es dazu, dass Sie auf der Zielseite ins Rate-Limiting geraten. Begrenzen Sie die Nebenläufigkeit wie oben gezeigt in kleinen Gruppen. Bei Aufgaben mit vielen Anfragen senkt Rotierender Proxy das Risiko einer Sperrung, indem der Traffic auf verschiedene IPs verteilt wird.

agent an das eingebaute fetch übergeben

In Node.js' alter http.request-API wird der Proxy über die Option agent übergeben. Das eingebaute fetch kennt diese Option nicht und ignoriert sie stillschweigend; die Anfrage geht dann ohne Proxy hinaus. Der richtige Schlüssel für fetch ist dispatcher.

Browser-Header unverändert kopieren

Aus den Entwicklertools kopierte Anfragen tragen browserspezifische Header wie sec-ch-ua, sec-fetch-*. In Node.js müssen diese nicht gesendet werden, und manche können auf der Zielseite als Inkonsistenz gewertet werden; lassen Sie nur wirklich benötigte Header übrig.

Seiten, für die eine HTTP-Anfrage nicht ausreicht

fetch und Axios beziehen nur das vom Server zurückgegebene HTML. Wird der Inhalt im Browser nachträglich per JavaScript geladen, finden Sie auf der Seite nicht, wonach Sie suchen. In diesem Fall müssen Sie entweder die Hintergrund-API-Anfrage finden oder eine Browser-Automatisierung nutzen. Die Optionen haben wir in Web Scraping: JavaScript oder Python? verglichen; die Proxy-Nutzung mit Puppeteer zeigen wir in Puppeteer und CAPTCHA.

fetch oder Axios?

  • Wählen Sie fetch: wenn Sie keine zusätzliche Abhängigkeit wollen, der Code sowohl im Browser als auch in Node.js laufen soll, oder Sie von undicis Verbindungsmanagement profitieren möchten.
  • Wählen Sie Axios: wenn Sie Annehmlichkeiten wie automatische JSON-Umwandlung, Interceptor oder Exceptions bei Antworten außerhalb des 2xx-Bereichs brauchen, oder es im Projekt bereits genutzt wird.

Der Unterschied zwischen beiden ist eher Gewohnheit als Funktionalität. In einem neuen Projekt mit fetch zu beginnen hält die Anzahl der Abhängigkeiten niedrig; in einem großen Axios-Projekt gibt es keinen Grund, zu fetch zu wechseln.

Häufig gestellte Fragen

Kann ich einen cURL-Befehl automatisch in JavaScript übersetzen?

In den Entwicklertools des Browsers können Sie im Netzwerk-Tab eine Anfrage per Rechtsklick mit "Copy as fetch" direkt als fetch-Code kopieren. Prüfen Sie den entstandenen Code trotzdem; browserspezifische Header können in Node.js überflüssig sein.

Kann ich in Node.js einen SOCKS5-Proxy nutzen?

Das eingebaute fetch und der undici-ProxyAgent sind für HTTP-Proxys gedacht. Für SOCKS5 müssen Sie mit einem zusätzlichen Paket wie socks-proxy-agent einen Agenten definieren. Bei cURL wird das Schema socks5h:// dagegen direkt unterstützt; Details in unserem Beitrag Wie nutzt man einen Proxy mit cURL?.

Kann im Browser laufendes JavaScript einen Proxy definieren?

Nein. Das fetch im Browser erlaubt es nicht, die Proxy-Einstellung über Code zu ändern; der Proxy wird in den Einstellungen des Browsers oder des Betriebssystems definiert. Proxy-Verwaltung aus dem Code heraus ist Serverumgebungen wie Node.js vorbehalten. Für Proxy auf Browserebene werden Erweiterungen wie SwitchyOmega genutzt.

Wie behalte ich Cookies zwischen Anfragen bei?

Das eingebaute fetch speichert keine Cookies. Sie müssen den Set-Cookie-Header aus der Antwort lesen und ihn der nächsten Anfrage als Cookie-Header hinzufügen, oder mit Axios ein auf tough-cookie basierendes Cookie-Jar verwenden. Denken Sie daran, dass bei angemeldeten Abläufen auch die IP konstant bleiben muss; Sticky-Proxy ist genau dafür konzipiert.

Kann ich bei jeder Anfrage einen anderen Proxy nutzen?

Ja. Mit undici können Sie jedem Aufruf einen anderen ProxyAgent geben. Statt die Liste selbst zu verwalten, ist es jedoch einfacher, einen Rotating-Proxy zu nutzen: eine Adresse, bei jeder Verbindung eine andere ausgehende IP.

Funktioniert derselbe Code auch in TypeScript?

Ja. undici und Axios liefern Typdefinitionen mit; für die dispatcher-Option müssen Sie undicis eigenen fetch-Typ verwenden, da die Typdefinition des eingebauten fetch diese Option nicht kennt.

Fazit

cURL-Befehle lassen sich in JavaScript eins zu eins mit fetch oder Axios abbilden. Für Proxys in Node.js gibt es vier Wege: den undici-ProxyAgent, einen globalen Dispatcher, die Umgebungsvariable NODE_USE_ENV_PROXY und für Axios einen Proxy-Agenten. Timeout, Fehlerprüfung, Wiederholung mit exponentiellem Backoff und eine von Anfang an eingebaute Nebenläufigkeitsgrenze machen Ihre Skripte deutlich zuverlässiger. Passende Pakete für Web-Traffic finden Sie auf unserer Seite HTTPS-Proxy.

ChatGPT fragenClaude fragen