React Native-Apps, die Webinhalte über react-native-webview laden, stoßen häufig auf CAPTCHAs – reCAPTCHA v2-Kontrollkästchen, Cloudflare Turnstile-Widgets oder Challenge-Seiten, die Formularübermittlungen blockieren.CaptchaAIlöst diese Herausforderungen programmgesteuert, sodass der Ablauf Ihrer mobilen Apps unterbrechungsfrei bleibt.
Diese Anleitung zeigt Ihnen, wie Sie CAPTCHAs in einem React Native WebView erkennen, die erforderlichen Parameter per JavaScript-Injection extrahieren, sie an die CaptchaAI-API senden und das gelöste Token wieder in die Seite einfügen.
Szenario aus der realen Welt
Sie erstellen eine React Native-App, die ein Webformular eines Drittanbieters in ein WebView lädt. Das Formular enthält ein reCAPTCHA v2-Kontrollkästchen, das vor dem Absenden ausgefüllt werden muss. Ihr Ziel ist:
- Erkennen Sie das CAPTCHA-Widget, wenn das Laden von WebView abgeschlossen ist
- Extrahieren Sie den Sitekey aus dem DOM
- Lösen Sie es über die API von CaptchaAI von einem Backend-Dienst aus
- Fügen Sie das Token wieder in WebView ein und senden Sie das Formular ab
Umgebung: React Native 0.72+, React-native-webview 13+, Node.js Backend, CaptchaAI API.
Architekturübersicht
Der Fluss teilt sich in drei Schichten auf:
| Schicht | Verantwortung |
|---|---|
| Reagieren Sie auf natives WebView | Erkennt CAPTCHA, extrahiert den Sitekey und fügt das gelöste Token ein |
| Backend-API (Node.js) | Empfängt Sitekey + PageURL, ruft CaptchaAI auf und gibt Token zurück |
| CaptchaAI-API | Löst das CAPTCHA und gibt das Token zurück |
Das WebView kommuniziert über window.ReactNativeWebView.postMessage() mit Ihrem React Native-Code, und Ihr Backend verarbeitet die CaptchaAI-Interaktion, um API-Schlüssel vom Client fernzuhalten.
Schritt 1: CAPTCHA erkennen und Sitekey in WebView extrahieren
Verwenden Sie die Requisite injectedJavaScript, um nach CAPTCHA-Elementen zu suchen, sobald die Seite geladen ist:
// CaptchaDetector.js — React Native Component
import React, { useRef, useState } from 'react';
import { View, ActivityIndicator } from 'react-native';
import { WebView } from 'react-native-webview';
const CAPTCHA_DETECTION_SCRIPT = `
(function() {
// Detect reCAPTCHA v2
const recaptchaDiv = document.querySelector('.g-recaptcha');
if (recaptchaDiv) {
const sitekey = recaptchaDiv.getAttribute('data-sitekey');
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'captcha_detected',
captchaType: 'recaptcha_v2',
sitekey: sitekey,
pageurl: window.location.href
}));
return;
}
// Detect Cloudflare Turnstile
const turnstileDiv = document.querySelector('.cf-turnstile');
if (turnstileDiv) {
const sitekey = turnstileDiv.getAttribute('data-sitekey');
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'captcha_detected',
captchaType: 'turnstile',
sitekey: sitekey,
pageurl: window.location.href
}));
return;
}
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'no_captcha'
}));
})();
true;
`;
export default function CaptchaWebView({ url }) {
const webviewRef = useRef(null);
const [solving, setSolving] = useState(false);
const handleMessage = async (event) => {
const data = JSON.parse(event.nativeEvent.data);
if (data.type === 'captcha_detected') {
setSolving(true);
try {
const token = await solveCaptchaViaBackend(
data.captchaType,
data.sitekey,
data.pageurl
);
injectToken(data.captchaType, token);
} catch (err) {
console.error('CAPTCHA solve failed:', err.message);
} finally {
setSolving(false);
}
}
};
const solveCaptchaViaBackend = async (captchaType, sitekey, pageurl) => {
const response = await fetch('https://your-backend.com/api/solve-captcha', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ captchaType, sitekey, pageurl }),
});
const result = await response.json();
if (!result.token) throw new Error(result.error || 'No token returned');
return result.token;
};
const injectToken = (captchaType, token) => {
let script;
if (captchaType === 'recaptcha_v2') {
script = `
document.getElementById('g-recaptcha-response').value = '${token}';
if (typeof ___grecaptcha_cfg !== 'undefined') {
Object.keys(___grecaptcha_cfg.clients).forEach(key => {
const client = ___grecaptcha_cfg.clients[key];
Object.keys(client).forEach(k => {
const item = client[k];
if (item && item.callback) {
item.callback('${token}');
}
});
});
}
true;
`;
} else if (captchaType === 'turnstile') {
script = `
const input = document.querySelector('[name="cf-turnstile-response"]');
if (input) input.value = '${token}';
const callback = document.querySelector('.cf-turnstile')
?.getAttribute('data-callback');
if (callback && typeof window[callback] === 'function') {
window[callback]('${token}');
}
true;
`;
}
webviewRef.current?.injectJavaScript(script);
};
return (
<View style={{ flex: 1 }}>
{solving && <ActivityIndicator size="large" />}
<WebView
ref={webviewRef}
source={{ uri: url }}
injectedJavaScript={CAPTCHA_DETECTION_SCRIPT}
onMessage={handleMessage}
javaScriptEnabled={true}
/>
</View>
);
}
Schritt 2: Erstellen Sie den Backend-Solver (Node.js)
Behalten Sie Ihren CaptchaAI-API-Schlüssel auf der Serverseite. Das Backend empfängt den Sitekey und die Seiten-URL, sendet sie an CaptchaAI, fragt das Ergebnis ab und gibt das Token zurück:
// server.js — Express backend
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
const API_KEY = process.env.CAPTCHAAI_API_KEY || 'YOUR_API_KEY';
app.post('/api/solve-captcha', async (req, res) => {
const { captchaType, sitekey, pageurl } = req.body;
try {
// Step 1: Submit task to CaptchaAI
const submitParams = {
key: API_KEY,
pageurl: pageurl,
json: '1',
};
if (captchaType === 'recaptcha_v2') {
submitParams.method = 'userrecaptcha';
submitParams.googlekey = sitekey;
} else if (captchaType === 'turnstile') {
submitParams.method = 'turnstile';
submitParams.sitekey = sitekey;
}
const submitResponse = await axios.get(
'https://ocr.captchaai.com/in.php',
{ params: submitParams }
);
if (submitResponse.data.status !== 1) {
return res.status(400).json({ error: submitResponse.data.request });
}
const taskId = submitResponse.data.request;
// Step 2: Poll for result
const token = await pollForResult(taskId);
res.json({ token });
} catch (error) {
console.error('Solve error:', error.message);
res.status(500).json({ error: 'Failed to solve CAPTCHA' });
}
});
async function pollForResult(taskId, maxAttempts = 30) {
for (let i = 0; i < maxAttempts; i++) {
await new Promise((r) => setTimeout(r, 5000));
const response = await axios.get('https://ocr.captchaai.com/res.php', {
params: {
key: API_KEY,
action: 'get',
id: taskId,
json: '1',
},
});
if (response.data.status === 1) {
return response.data.request;
}
if (
response.data.request !== 'CAPCHA_NOT_READY' &&
response.data.status === 0
) {
throw new Error(response.data.request);
}
}
throw new Error('Polling timeout — CAPTCHA not solved in time');
}
app.listen(3000, () => console.log('Solver backend running on port 3000'));
Schritt 3: Behandeln Sie den Token-Ablauf in WebView
CAPTCHA-Tokens verfallen – reCAPTCHA v2-Tokens dauern ca. 120 Sekunden, Turnstile-Tokens ca. 300 Sekunden. Wenn der Benutzer die Übermittlung des Formulars verzögert, lösen Sie es vor dem Absenden erneut:
// Add to CaptchaWebView component
const [tokenTimestamp, setTokenTimestamp] = useState(null);
const TOKEN_TTL_MS = 110000; // 110 seconds for reCAPTCHA v2
const handleFormSubmit = async (captchaType, sitekey, pageurl) => {
const now = Date.now();
if (!tokenTimestamp || now - tokenTimestamp > TOKEN_TTL_MS) {
const freshToken = await solveCaptchaViaBackend(
captchaType, sitekey, pageurl
);
injectToken(captchaType, freshToken);
setTokenTimestamp(Date.now());
}
webviewRef.current?.injectJavaScript(`
document.querySelector('form').submit();
true;
`);
};
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Token wird erzeugt, aber vom Ziel abgelehnt | sitekey, pageurl oder Session-Kontext stimmen nicht | Erfasse Parameter erneut und verwende den Token in derselben Browser- oder HTTP-Sitzung |
| Polling endet im Timeout | Intervall, Wartezeit oder Fehlerbehandlung sind zu eng gesetzt | Poll alle 5-10 Sekunden, trenne Timeout von echten Fehlercodes und logge die Ursache |
| Beispiel funktioniert lokal, aber nicht im Workflow | Callback, Form-Feld oder Token-Injektion fehlt in der echten Zielkette | Prüfe den exakten Übergabepfad vom Solver bis zur finalen Zielanfrage |
Verwandte Leitfäden
- PWA CAPTCHA-Verwaltung
- Mobile Browser-Automatisierung mit CAPTCHA
- iOS XCUITest CAPTCHA-Verwaltung
Diskussionen (0)
Beteiligen Sie sich an der Unterhaltung
Melden Sie sich an, um Ihre Meinung zu teilen.
AnmeldenNoch keine Kommentare.