Skip to content

Timeout di Google translate api

Ti consigliamo di testare questa risoluzione in un ambiente controllato prima di inviarla alla produzione, saluti.

Soluzione:

Alcune riflessioni, le API di Google provate prima, possono gestire solo un certo numero di richieste contemporanee, e se il limite viene raggiunto, il servizio restituirà l'errore HTTP 503 "Service Unavailable." E HTTP 403 se il Daily limit is Exceeded o User Rate Limit.

Cercare di implementare i tentativi con un backoff esponenziale. Riprova un'operazione con un tempo di attesa esponenzialmente crescente, fino al raggiungimento di un numero massimo di tentativi. Ciò migliorerà l'utilizzo della larghezza di banda e massimizzerà il throughput delle richieste in ambienti con concorrenza.

E rivedete la pagina Quote e limiti.

  • Backoff esponenziale

Un errore 503 implica che il problema è dal lato di Google, il che mi porta a credere che sia possibile che la tariffa sia limitata. Come menzionato da Raphael, esiste un Retry-After nella risposta? Vi consiglio di dare un'occhiata alle intestazioni di risposta, perché probabilmente vi dirà cosa sta succedendo in modo più specifico e forse vi darà informazioni su come risolvere il problema.

Google API è eccellente nel nascondere le complessità della traduzione di Google. Sfortunatamente, se si entra nel codice di Google API, questo utilizza richieste HTTP standard. Questo significa che quando si eseguono più di 20.000 richieste, indipendentemente dal pooling dei thread, ci sarà un enorme collo di bottiglia.

Considerate la possibilità di creare richieste HTTP usando aiohttp (che dovrete installare da pip) e asyncio. Questo vi permetterà di eseguire richieste HTTP asincrone. (Significa che non è necessario usare google.cloud.translate_v2, multiprocessing o tqdm.notebook).

Basta chiamare un metodo await in asyncio.run() il metodo può creare un array di metodi per preformare aiohttp.session.get(). Quindi chiama asyncio.gather() per raccogliere tutti i risultati.

Nell'esempio seguente sto usando una chiave API https://console.cloud.google.com/apis/credentials (invece di Google Application Credential / Service Accounts).

Utilizzando il vostro esempio con asyncio e aiohttp, è stato eseguito in 30 secondi e senza errori. (Anche se si potrebbe estendere il timeout alla sessione).

Vale la pena sottolineare che Google ha un limite di 6 milioni caratteri al minuto. Il vostro test sta facendo 360,000. Pertanto, raggiungerete il limite se eseguite il test 17 volte in un minuto!

Inoltre la velocità è determinata principalmente dalla macchina e non da Google API. (Ho eseguito i miei test su un pc con 3GHz, 8 core e 16GB di ram).

import asyncio
import aiohttp
from collections import namedtuple
import json
from urllib.parse import quote

TranslateReponseModel = namedtuple('TranslateReponseModel', ['sourceText', 'translatedText', 'detectedSourceLanguage']) # model to store results.

def Logger(json_message):    
    print(json.dumps(json_message)) # Note: logging json is just my personal preference.

async def DownloadString(session, url, index):
    while True: # If client error - this will retry. You may want to limit the amount of attempts
        try:
            r = await session.get(url)
            text = await r.text()
            #Logger({"data": html, "status": r.status}) 
            r.raise_for_status() # This will error if API return 4xx or 5xx status.
            return text
        except aiohttp.ClientConnectionError as e:
            Logger({'Exception': f"Index {index} - connection was dropped before we finished", 'Details': str(e), 'Url': url })
        except aiohttp.ClientError as e:
            Logger({'Exception': f"Index {index} - something went wrong. Not a connection error, that was handled", 'Details': str(e), 'Url': url})

def FormatResponse(sourceText, responseText):
    jsonResponse = json.loads(responseText)
    return TranslateReponseModel(sourceText, jsonResponse["data"]["translations"][0]["translatedText"], jsonResponse["data"]["translations"][0]["detectedSourceLanguage"])

def TranslatorUriBuilder(targetLanguage, sourceText):
    apiKey = 'ABCDED1234' # TODO This is a 41 characters API Key. You'll need to generate one (it's not part of the json certificate)
    return f"https://translation.googleapis.com/language/translate/v2?key={apiKey}={quote(sourceText)}&target={targetLanguage}"

async def Process(session, sourceText, lineNumber):
    translateUri = TranslatorUriBuilder('en', sourceText) # Country code is set to en (English)
    translatedResponseText = await DownloadString(session, translateUri, lineNumber)
    response = FormatResponse(sourceText, translatedResponseText)
    return response

async def main():       
    statements = ["this is another sentence"]*20000

    Logger({'Message': f'Start running Google Translate API for {len(statements)}'})
    results = []
    async with aiohttp.ClientSession() as session:
        results = await asyncio.gather(*[Process(session, val, idx) for idx, val in enumerate(statements)]  )  

    Logger({'Message': f'Results are: {", ".join(map(str, [x.translatedText for x in results]))}'})
    Logger({'Message': f'Finished running Google Translate API for {str(len(statements))} and got {str(len(results))} results'})

if __name__ == '__main__':
    asyncio.run(main())

Test aggiuntivo

Il test iniziale esegue la stessa traduzione. Ho quindi creato un test per verificare che i risultati non vengano memorizzati nella cache di Google. Ho copiato manualmente un eBook in un file di testo. Poi, in Python, il codice apre il file e raggruppa il testo in un array di 100 caratteri, quindi prende i primi 20.000 elementi dall'array e traduce ogni riga. È interessante notare che ci sono voluti meno di 30 secondi.

import asyncio
import aiohttp
from collections import namedtuple
import json
from urllib.parse import quote

TranslateReponseModel = namedtuple('TranslateReponseModel', ['sourceText', 'translatedText', 'detectedSourceLanguage']) # model to store results.

def Logger(json_message):    
    print(json.dumps(json_message)) # Note: logging json is just my personal preference.

async def DownloadString(session, url, index):
    while True: # If client error - this will retry. You may want to limit the amount of attempts
        try:
            r = await aiohttp.session.get(url)
            text = await r.text()
            #Logger({"data": html, "status": r.status}) 
            r.raise_for_status() # This will error if API return 4xx or 5xx status.
            return text
        except aiohttp.ClientConnectionError as e:
            Logger({'Exception': f"Index {index} - connection was dropped before we finished", 'Details': str(e), 'Url': url })
        except aiohttp.ClientError as e:
            Logger({'Exception': f"Index {index} - something went wrong. Not a connection error, that was handled", 'Details': str(e), 'Url': url})

def FormatResponse(sourceText, responseText):
    jsonResponse = json.loads(responseText)
    return TranslateReponseModel(sourceText, jsonResponse["data"]["translations"][0]["translatedText"], jsonResponse["data"]["translations"][0]["detectedSourceLanguage"])

def TranslatorUriBuilder(targetLanguage, sourceText):
    apiKey = 'ABCDED1234' # TODO This is a 41 characters API Key. You'll need to generate one (it's not part of the json certificate)
    return f"https://translation.googleapis.com/language/translate/v2?key={apiKey}={quote(sourceText)}&target={targetLanguage}"

async def Process(session, sourceText, lineNumber):
    translateUri = TranslatorUriBuilder('en', sourceText) # Country code is set to en (English)
    translatedResponseText = await DownloadString(session, translateUri, lineNumber)
    response = FormatResponse(sourceText, translatedResponseText)
    return response

def readEbook():
    # This is a simple test to make sure response is not cached.
    # I grabbed a random online pdf (http://sd.blackball.lv/library/Beginning_Software_Engineering_(2015).pdf) and copied text into notepad.
    with open("C:\Dev\ebook.txt", "r", encoding="utf8") as f:
        return f.read()

def chunkText(text):
    chunk_size = 100
    chunks= len(text)
    chunk_array = [text[i:i+chunk_size] for i in range(0, chunks, chunk_size)]
    formatResults = [x for x in chunk_array if len(x) > 10]
    return formatResults[:20000]

async def main():  
    data = readEbook()
    chunk_data = chunkText(data)

    Logger({'Message': f'Start running Google Translate API for {len(chunk_data)}'})
    results = []
    async with aiohttp.ClientSession() as session:
        results = await asyncio.gather(*[Process(session, val, idx) for idx, val in enumerate(chunk_data)]  )  

    Logger({'Message': f'Results are: {", ".join(map(str, [x.translatedText for x in results]))}'})
    Logger({'Message': f'Finished running Google Translate API for {str(len(chunk_data))} and got {str(len(results))} results'})

if __name__ == '__main__':
    asyncio.run(main())

Infine, è possibile trovare ulteriori informazioni sulla richiesta HTTP dell'API di Google Translate https://cloud.google.com/translate/docs/reference/rest/v2/translate ed eseguire la richiesta tramite Postman.

Ricorda che ti concediamo di apprezzare questo articolo.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.