es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Agrega una columna durante la obtención de datos con ThreadPoolExecutor en Python.

Quiero leer diferentes páginas desde el siguiente enlace con números diferentes utilizando ThreadPoolExecutor y guardar los números relacionados en un marco de datos como una nueva columna.

https://booking.snav.it/api/v1/rates/1030/2019-02-25/1042/2019-02-25?lang=1

Los números cambian de la siguiente manera:

from concurrent.futures import ThreadPoolExecutor, as_completed
from pandas import json_normalize
import pandas as pd
import requests


def download_file(url):
    url_info = requests.get(url, stream=True)
    jdata = url_info.json()
    return jdata


nums = [1030, 1031, 1040, 1050, 1020, 1021, 1010, 1023]
urls = [f"https://booking.snav.it/api/v1/rates/{i}/2019-02-25/1042/2019-02-25?lang=1" for i in nums]
with ThreadPoolExecutor(max_workers=14) as executor:
     for url in urls:
         sleep(0.1)
         processes.append(executor.submit(download_file, url))

for index, task in enumerate(as_completed(processes)):
    jdata = task.result()
    tmp = json_normalize(jdata)
    tmp["num"] = nums[index]
df = df.append(tmp)
print(df.head())

En el código anterior, he intentado leer los datos utilizando multi-threading y el número relacionado con cada respuesta json como una nueva columna del marco de datos df. Pero este código no funciona debido al uso de multi-threading, el orden de los números en nums no es el mismo que las respuestas json obtenidas. ¿Qué debo hacer?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Para resolver el problema, se puede utilizar un diccionario para mapear las URL con su número correspondiente. Luego, el diccionario se puede pasar como argumento a la función download_file, que guardará el número como una columna adicional en el dataframe.

    Aquí está el código modificado:

    from concurrent.futures import ThreadPoolExecutor, as_completed
    from pandas import json_normalize
    import pandas as pd
    import requests
    
    
    def download_file(url, num_map):
        url_info = requests.get(url, stream=True)
        jdata = url_info.json()
        num = num_map[url]
        jdata["num"] = num
        return jdata
    
    
    nums = [1030, 1031, 1040, 1050, 1020, 1021, 1010, 1023]
    urls = [f"https://booking.snav.it/api/v1/rates/{i}/2019-02-25/1042/2019-02-25?lang=1" for i in nums]
    url_map = dict(zip(urls, nums))
    
    df = pd.DataFrame()
    processes = []
    with ThreadPoolExecutor(max_workers=14) as executor:
         for url in urls:
             sleep(0.1)
             processes.append(executor.submit(download_file, url, url_map))
    
    for task in as_completed(processes):
        jdata = task.result()
        tmp = json_normalize(jdata)
        df = df.append(tmp)
    
    print(df.head())
    

    En el código modificado, la función download_file toma un argumento adicional num_map, que es un diccionario que asigna cada URL a su número correspondiente. La función luego extrae el número del diccionario y lo guarda como una columna adicional en la respuesta json.

    El diccionario url_map se crea utilizando la función zip, que combina las listas urls y nums en pares, y la función dict, que crea un diccionario a partir de los pares.

    En el bucle que envía las tareas al ThreadPoolExecutor, se llama a la función download_file con dos argumentos: la URL y el diccionario num_map.

    En el bucle que procesa las tareas completadas, el dataframe tmp se agrega al dataframe df. La variable index no es necesaria, ya que el orden de las respuestas en la lista processes corresponde al orden de las URLs en la lista urls.

    La salida debería ser un dataframe con los datos json de cada URL y una nueva columna num que contiene el número correspondiente.

Comments are closed.