Como parte de su Google Cloud, Google ofrece la API de traducción de Google con una estructura de costos basada en el uso. También hay una API no documentada que se puede usar sin una clave , pero que se niega a funcionar después de unas pocas solicitudes. Cuando se utiliza la función de traducción de sitios web de Google Chrome, se nota que las páginas se pueden traducir con muy buena calidad sin limitaciones apreciables.
Aparentemente, el modelo nmt avanzado ya se está utilizando aquí. Pero, ¿qué API utiliza Google Chrome internamente para traducir el contenido? ¿Se puede abordar esta API también directamente, incluso en el lado del servidor? Para analizar el tráfico de la red, se recomiendan herramientas como Wireshark o Telerik Fiddler , que también pueden analizar el tráfico cifrado. Pero Chrome incluso entrega las solicitudes que envía para la traducción de la página de forma gratuita : son fácilmente accesibles a través de Chrome DevTools:
Si realiza una traducción, capture la solicitud POST crucial a https://translate.googleapis.com a través de "Copiar> Copiar como cURL (bash)" y ejecútela en una herramienta como Postman , por ejemplo, puede enviar la solicitud nuevamente sin ningún problema:
El significado de los parámetros de URL también es bastante obvio:
Llave | Valor de ejemplo | Sentido |
anno | 3 | Modo de anotación (afecta el formato de devolución) |
cliente | te_lib | Información del cliente (varía, el valor es "webapp" a través de la interfaz web de Google Translate; tiene un efecto sobre el formato de devolución y la limitación de la tasa) |
formato | html | Formato de cadena (importante para traducir etiquetas HTML) |
v | 1.0 | Número de versión del Traductor de Google |
llave | AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw | Clave API (ver más abajo) |
logld | vTE_20200210_00 | Versión del protocolo |
sl | Delaware | Lenguaje fuente |
tl | en | Lengua de llegada |
sp | nmt | Modelo ML |
tc | 1 | desconocido |
sr | 1 | desconocido |
tk | 709408.812158 | Token (ver más abajo) |
Moda | 1 | desconocido |
También se establecen algunos encabezados de solicitud, pero en su mayoría se pueden ignorar. Después de deseleccionar manualmente todos los encabezados, incluidos los del agente de usuario , se descubre un problema de codificación al ingresar caracteres especiales (aquí al traducir " Hola mundo "):
Si reactiva el agente de usuario (que generalmente no causa ningún daño), la API entrega caracteres codificados en UTF-8:
¿Ya estamos allí y tenemos toda la información para usar esta API fuera de Google Chrome? Si cambia la cadena a traducir (campo de datos q de la solicitud POST) de, por ejemplo, “Hola mundo” a “¡Hola mundo ! ", Recibimos un mensaje de error:
Ahora traducimos este modificado nuevamente dentro de Google Chrome usando la función de traducción del sitio web y encontramos que, además del parámetro q , el parámetro tk también ha cambiado (todos los demás parámetros han permanecido igual):
Al parecer, es un token que depende de la cadena, cuya estructura no es fácil de ver. Cuando inicia la traducción del sitio web, se cargan los siguientes archivos:
- 1 archivo CSS: translateelement.css
- 4 gráficos: translate_24dp.png (2x), gen204 (2x)
- 2 archivos JS: main_de.js , element_main.js
Los dos archivos JavaScript están ofuscados y minimizados. Herramientas como JS Nice y de4js ahora nos ayudan a hacer que estos archivos sean más legibles. Para depurarlos en vivo, recomendamos la extensión Chrome Requestly, que canaliza archivos remotos localmente sobre la marcha:
Ahora podemos depurar el código (primero debe activarse CORS en el servidor local). La sección de código relevante para generar el token parece estar oculta en esta sección en el archivo element_main.js:
b7739bf50b2edcf636c43a8f8910def9
Aquí, el texto está codificado con la ayuda de algunos cambios de bits . Pero lamentablemente todavía nos falta una pieza del rompecabezas: además del argumento a (que es el texto a traducir), se pasa otro argumento b a la función Bp () - una especie de semilla que parece cambiar de vez en cuando y que también incluye fluye a hash. ¿Pero de dónde viene? Si saltamos a la llamada de función de Bp () , encontramos la siguiente sección de código:
b7739bf50b2edcf636c43a8f8910def9
La función Hq se declara de antemano de la siguiente manera:
b7739bf50b2edcf636c43a8f8910def9
Aquí el Deobfuscater dejó algunos desperdicios; Después de haber reemplazado String.fromCharCode ('...') con las respectivas cadenas de caracteres, elimine el obsoleto a () y junte las llamadas de función [c (), c ()] , el resultado es:
b7739bf50b2edcf636c43a8f8910def9
O incluso más fácil:
b7739bf50b2edcf636c43a8f8910def9
La función yq se define previamente como:
b7739bf50b2edcf636c43a8f8910def9
La semilla parece estar en el objeto global google.translate._const._ctkk , que está disponible en tiempo de ejecución. Pero, ¿dónde está ubicado? En el otro archivo JS main_de.js , previamente cargado , al menos también está disponible al principio. Agregamos lo siguiente al principio:
b7739bf50b2edcf636c43a8f8910def9
En la consola obtenemos la semilla actual:
Esto deja al propio Google Chrome, que aparentemente proporciona la semilla, como última opción. Afortunadamente, su código fuente (Chromium, incluido el componente Translate) es de código abierto y, por lo tanto, está disponible públicamente. Extraemos el repositorio localmente y encontramos la llamada a la función TranslateScript :: GetTranslateScriptURL en el archivo translate_script.cc en la carpeta components / translate / core / browser:
b7739bf50b2edcf636c43a8f8910def9
La variable con la URL está definida en el mismo archivo.:
b7739bf50b2edcf636c43a8f8910def9
Si ahora examinamos el archivo element.js más de cerca (después de desofuscar nuevamente), encontramos la entrada c._ctkk - el objeto google.translate también se configura en consecuencia y se activa la carga de todos los activos relevantes (que ya hemos descubierto anteriormente):
b7739bf50b2edcf636c43a8f8910def9
Ahora la clave del parámetro permanece en consideración (con el valor AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw). Parece ser una clave de API de navegador genérica (que también se puede encontrar en algunos resultados de Google ). Está configurado en Chromium en el archivo translate_url_util.cc en la carpeta componentes / translate / core / browser:
b7739bf50b2edcf636c43a8f8910def9
La clave se genera en google_apis / google_api_keys.cc a partir de un valor ficticio:
b7739bf50b2edcf636c43a8f8910def9
Sin embargo, una prueba muestra que las llamadas a la API funcionan igual sin este parámetro clave. Si experimenta con la API, obtendrá el código de estado 200 si tiene éxito. Si luego se encuentra con un límite, obtiene el código de estado 411 con el mensaje "Las solicitudes POST requieren un encabezado de longitud de contenido ". Por lo tanto, es recomendable incluir este encabezado (que se configura automáticamente como un encabezado temporal en Postman).
El formato de retorno de las cadenas traducidas es inusual cuando hay varias oraciones en una solicitud. Las oraciones individuales están encerradas por las etiquetas i- / b-HTML:
Además, Google Chrome no envía todo el HTML a la API, pero guarda valores de atributos como href en la solicitud (y en su lugar establece índices para que las etiquetas se puedan asignar posteriormente en el lado del cliente):
Si cambia el valor del cliente clave POST de te_lib (Google Chrome) en webapp ( sitio web de traducción de Google ), obtiene la cadena traducida final:
El problema es que es mucho más probable que te encuentres con una limitación de velocidad que a través de te_lib (a modo de comparación: con webapp, esto se alcanza después de 40.000 caracteres, con te_lib no hay limitación de velocidad). Por lo tanto, debemos observar más de cerca cómo Chrome analiza el resultado. Lo encontraremos aquí en element_main.js:
b7739bf50b2edcf636c43a8f8910def9
Si envía el código HTML completo a la API, deja los atributos en la respuesta traducida. Por lo tanto, no tenemos que imitar todo el comportamiento de análisis, solo extraer la cadena final traducida de la respuesta. Para hacer esto, creamos un pequeño analizador de etiquetas HTML que descarta las etiquetas <i> más externas, incluido su contenido, y elimina las etiquetas <b> más externas. Con este conocimiento (después de instalar las dependencias con composer, se requiere fzaninotto / faker vielhuber / stringhelper ) ahora podemos construir una versión del lado del servidor de la API de traducción:
b7739bf50b2edcf636c43a8f8910def9
Los siguientes son los resultados de una prueba inicial que se llevó a cabo en cinco sistemas diferentes con diferentes anchos de banda y direcciones IP.:
Personaje | Caracteres por solicitud | Duración | Tasa de error | Costo vía API oficial |
13.064.662 | ~250 | 03: 36: 17h | 0% | 237,78€ |
24.530.510 | ~250 | 11: 09: 13h | 0% | 446,46€ |
49.060.211 | ~250 | 20: 39: 10h | 0% | 892,90€ |
99.074.487 | ~1000 | 61: 24: 37h | 0% | 1803,16€ |
99.072.896 | ~1000 | 62: 22: 20h | 0% | 1803,13€ |
Σ284.802.766 | ~ Ø550 | Σ159: 11: 37h | 0% | Σ 5183,41 € |
Nota: Esta publicación de blog que incluye todos los scripts se escribió solo con fines de prueba. No utilice los guiones para uso productivo, en lugar de trabajar con el funcionario API Google Traductor .