Obtiene los datos en bruto de la red realizando múltiples requests al tiempo
        Procesa los datos en bruto obtenidos de la red convirtiendo a Pandas DataFrame

def get_data_en_intervalo(d0=None, df=None, date_fmt=DATE_FMT,
                          usar_multithread=USAR_MULTITHREAD, max_threads_requests=MAX_THREADS_REQUESTS,
                          timeout=TIMEOUT, num_retries=NUM_RETRIES,
                          func_procesa_data_dia=None, func_url_data_dia=None, max_act_exec=None, verbose=True,
                          data_extra_request=None):
        """
        Obtiene los datos en bruto de la red realizando múltiples requests al tiempo
        Procesa los datos en bruto obtenidos de la red convirtiendo a Pandas DataFrame
        """
        def _date(dia_string):
            if dia_string is None:
                return dt.date.today()
            elif type(dia_string) is pd.Timestamp:
                return dia_string.to_datetime().date()
            elif type(dia_string) is not dt.date:
                return dt.datetime.strptime(dia_string, date_fmt).date()
            else:
                return dia_string

        def _procesa_merge_datos_dias(lista_m, dict_data_merge):

            def _merge_datos_dias(key_tarea_merge, dict_merge_dias):
                dict_merge_dias[key_tarea_merge] = merge_data(dict_merge_dias[key_tarea_merge])

            if num_dias > 1 and usar_multithread:
                lista_grupos = list()
                grupos_dias = [lista_m[i:i + DIAS_MERGE_MAX] for i in np.arange(0, num_dias, DIAS_MERGE_MAX)]
                for grupo in grupos_dias:
                    lista_dfs = list()
                    for key_g in grupo:
                        lista_dfs.append(dict_data_merge[key_g])
                    lista_grupos.append(lista_dfs)
                keys_grupos = np.arange(len(lista_grupos))
                dict_merge = dict(zip(keys_grupos, lista_grupos))
                procesa_tareas_paralelo(keys_grupos, dict_merge, _merge_datos_dias,
                                        '\nMERGE DATAFRAMES DE DATOS WEB DIARIOS (%lu GRUPOS)',
                                        usar_multithread, MAX_THREADS_MERGE, verbose=verbose)
                dict_merge_final = {0: [dict_merge[k] for k in dict_merge.keys()]}
                _merge_datos_dias(0, dict_merge_final)
                return dict_merge_final[0]
            else:
                return merge_data(list(dict_data_merge.values()))

        def _hay_errores_en_datos_obtenidos(dict_data_obtenida):
            keys = list(sorted(dict_data_obtenida.keys()))
            data_es_none = [dict_data_obtenida[k] is None for k in keys]
            error = False
            if any(data_es_none):
                df_err = pd.DataFrame({'key': keys, 'is_bad': data_es_none})
                df_err['date'] = df_err['key'].apply(lambda x: pd.Timestamp(x))
                df_err['delta'] = (df_err['date'] - df_err['date'].shift(1)).fillna(3600 * 24)
                df_g = df_err[~df_err['is_bad']].copy()
                df_g['delta_g'] = (df_g['date'] - df_g['date'].shift(1)).fillna(3600 * 24)
                # print(df_err)
                # print(df_err['delta'].describe())
                # print(df_g['delta_g'].describe())

                if df_g['delta_g'].max() < pd.Timedelta(2, 'D'):
                    bad_days = df_err[df_err['is_bad']]['key'].tolist()
                    if verbose:
                        print('HAY TAREAS NO REALIZADAS ({}):\n{}'.format(len(bad_days), bad_days))
                    logging.error('HAY TAREAS NO REALIZADAS ({}):\n{}'.format(len(bad_days), bad_days))
                    error = False
                else:
                    if verbose:
                        print('NO HAY NINGUNA TAREA REALIZADA!')
                    logging.error('NO HAY NINGUNA TAREA REALIZADA!')
                    bad_days = df_err['key'].tolist()
                    error = True
                for k in bad_days:
                    dict_data_obtenida.pop(k)
            return error

        def _obtiene_request(url, key, headers=None, p_req=None, json_r=False, **kwargs_r):
            if type(url) is list:
                results = [request_data_url(u, headers, num_retries, timeout, p_req, json_r, **kwargs_r) for u in url]
                dict_data[key] = list(zip(*results))
            else:
                stat_response = request_data_url(url, headers, num_retries, timeout, p_req, json_r, **kwargs_r)
                dict_data[key] = stat_response

        def _obtiene_data_dia(key, dict_data_responses):
            url = func_url_data_dia(key)
            extra = dict_data_responses[key] if type(dict_data_responses[key]) is dict else {}
            headers = extra.pop('headers', None)
            json_req = extra.pop('json_req', False)
            params_request = extra.pop('params_request', None)
            try:
                count_process, ok = 0, -1
                while count_process < num_retries and ok != 0:
                    _obtiene_request(url, key, headers, params_request, json_req, **extra)
                    data_import, ok = func_procesa_data_dia(key, dict_data_responses[key][1])
                    if ok == 0:
                        dict_data_responses[key] = data_import
                    elif ok == -2:  # Código de salida temprana:
                        count_process = num_retries
                    count_process += 1
                if ok != 0:
                    dict_data_responses[key] = None
            except Exception as e:
                if verbose:
                    print('PROCESANDO DATA!???? (Exception: {}; KEY: {}; URL: {})'.format(e, key, url))
                logging.error('PROCESANDO DATA!???? (Exception: {}; KEY: {}; URL: {})'.format(e, key, url))
                dict_data_responses[key] = None

        tic_ini = time.time()
        lista_dias = [dia.strftime(date_fmt) for dia in pd.date_range(_date(d0), _date(df))]
        if max_act_exec:  # BORRAR. Es para limitar el nº de días adquiridos de golpe.
            lista_dias = lista_dias[:max_act_exec]
        num_dias = len(lista_dias)
        if data_extra_request is None:
            dict_data = dict(zip(lista_dias, np.zeros(num_dias)))
        else:
            dict_data = dict(zip(lista_dias, [data_extra_request.copy() for _ in range(num_dias)]))
        # IMPORTA DATOS Y LOS PROCESA
        procesa_tareas_paralelo(lista_dias, dict_data, _obtiene_data_dia,
                                '\nPROCESADO DE DATOS WEB DE %lu DÍAS',
                                usar_multithread, max_threads_requests, verbose=verbose)
        hay_errores = _hay_errores_en_datos_obtenidos(dict_data)
        # MERGE DATOS
        # print(len(lista_dias), len(dict_data.keys()))
        if not hay_errores and num_dias > 0:
            # data_merge = _procesa_merge_datos_dias(lista_dias, dict_data)
            data_merge = _procesa_merge_datos_dias(list(sorted(dict_data.keys())), dict_data)
            str_resumen_import = '\n%lu días importados [Proceso Total %.2f seg, %.4f seg/día]' \
                                 % (num_dias, time.time() - tic_ini, (time.time() - tic_ini) / float(num_dias))
            return data_merge, hay_errores, str_resumen_import
        else:
            return None, hay_errores, 'ERROR IMPORTANDO!!'