Estou desenvolvendo um script em Python que faz requisições assíncronas a uma API, mas estou enfrentando o erro 429 Too Many Requests. O site limita o número de requisições, e estou tentando burlar isso usando headers aleatórios e trocando de IP, mas mesmo assim algumas requisições repetem números aleatórios e continuam retornando o erro. Aqui está um trecho do meu código: @app.route('/imoveis', methods=['POST']) async def listar_imoveis(): with lock: data = request.get_json() estado = data.get('estado') cidades = data.get('cidades', []) modalidade_venda = data.get('modalidadeVenda') tipo_imovel_filtro = data.get('tipoImovel') page = data.get('page', 1) limit = 15 minimo_porcentagem = data.get('minimoPorcentagem') maximo_porcentagem = data.get('maximoPorcentagem') if not estado: return jsonify({'error': 'O parâmetro "estado" é obrigatório.'}), 400 try: df = baixar_e_processar_csv(estado) # Aplicando filtro de cidades if cidades: cidades_normalizadas = [cidade.upper() for cidade in cidades] df = df[df['Cidade'].str.upper().isin(cidades_normalizadas)] # Aplicando filtro de modalidade de venda modalidades_validas = [ "Leilão SFI - Edital Único", "Licitação Aberta", "Venda Direta Online", "Venda Online" ] if modalidade_venda: modalidade_venda_normalizada = remover_acentos(modalidade_venda.strip().lower()) if modalidade_venda_normalizada not in [remover_acentos(mod.lower()) for mod in modalidades_validas]: return jsonify({'error': 'Modalidade de venda inválida. As opções válidas são: ' + ', '.join(modalidades_validas)}), 400 if 'Modalidade de venda' in df.columns: df = df[df['Modalidade de venda'].str.lower().apply(remover_acentos).str.contains(modalidade_venda_normalizada, na=False)] if df.empty: return jsonify({'error': 'Nenhum imóvel encontrado com os critérios especificados.'}), 404 # Identificação do tipo de imóvel diretamente no DataFrame df = df.copy() df.loc[:, 'tipoImovel'] = df['Descrição'].str.split(',').str[0].str.strip() # Aplicando filtro de tipo de imóvel, se fornecido if tipo_imovel_filtro: df = df[df['tipoImovel'] == tipo_imovel_filtro] # Conversão para dicionário após processamento no DataFrame imoveis_dict = df.to_dict(orient='records') erro_count = 0 # Extração dos detalhes do imóvel em paralelo detalhes_imoveis = await asyncio.gather( *(extrair_detalhes_imovel(imovel.get('Link de acesso')) for imovel in imoveis_dict if imovel.get('Link de acesso')) ) # Atualiza o dicionário de imóveis com os detalhes extraídos for imovel, detalhes in zip(imoveis_dict, detalhes_imoveis): if detalhes: imovel.update(detalhes) # Convertendo para DataFrame novamente após a extração dos detalhes detalhes_df = pd.DataFrame(imoveis_dict) # Filtrando pela porcentagem, caso seja aplicável if 'porcentagemSegundoLeilaoAvaliacao' in detalhes_df.columns and minimo_porcentagem is not None and maximo_porcentagem is not None: # Remover '%' e filtrar valores válidos detalhes_df['porcentagemSegundoLeilaoAvaliacao'] = detalhes_df['porcentagemSegundoLeilaoAvaliacao'].str.replace('%', '') # Filtrando apenas valores que são numéricos detalhes_df = detalhes_df[detalhes_df['porcentagemSegundoLeilaoAvaliacao'] != 'Valor não encontrado.'] # Converter para float detalhes_df['porcentagemSegundoLeilaoAvaliacao'] = pd.to_numeric(detalhes_df['porcentagemSegundoLeilaoAvaliacao'], errors='coerce') # Aplicando o filtro, ignorando valores NaN resultantes da conversão detalhes_df = detalhes_df[detalhes_df['porcentagemSegundoLeilaoAvaliacao'].between(minimo_porcentagem, maximo_porcentagem)] # Paginação após os filtros total_imoveis = len(detalhes_df) total_pages = (total_imoveis // limit) + (1 if total_imoveis % limit > 0 else 0) # Aplicando limite e offset para paginação start = (page - 1) * limit end = start + limit imoveis_paginados = detalhes_df.iloc[start:end] print(f"Número de imóveis com erro: {erro_count}") return app.response_class( response=json.dumps({ 'imoveis': imoveis_paginados.to_dict(orient='records'), 'total_imoveis': total_imoveis, 'pagina_atual': page, 'imoveis_por_pagina': limit, 'total_paginas': total_pages }, ensure_ascii=False, indent=4), status=200, mimetype='application/json; charset=utf-8' ) except Exception as e: return jsonify({'error': str(e)}), 500 Funcao que faz as requisições: async def extrair_detalhes_imovel(link): global erro_count try: user_agent = f'Mozilla/5.0 (Windows NT {random.randint(5, 12)}.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{random.randint(40, 120)}.0.{random.randint(0, 10000)}.{random.randint(0, 200)} Safari/537.36' headers = { 'User-Agent': user_agent, 'Accept-Language': f'en-US,en;q={random.uniform(0.5, 1.0)}', # Variável 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', } async with aiohttp.ClientSession() as session: async with session.get(link, headers=headers) as resposta: resposta.raise_for_status() soup = BeautifulSoup(await resposta.text(), 'html.parser') dados_imovel_div = soup.find('div', id='dadosImovel') if not dados_imovel_div: return { 'minimoVendaPrimeiroLeilao': "Valor não encontrado.", 'minimoVendaSegundoLeilao': "Valor não encontrado.", 'dataSegundoLeilao': "Data não encontrada.", 'linkImagem': "Imagem não encontrada.", 'diferencaSegundoLeilaoAvaliacao': 'Valor não encontrado.', 'porcentagemSegundoLeilaoAvaliacao': 'Valor não encontrado.', } texto = dados_imovel_div.get_text() valor_primeiro_leilao = re.search(r'Valor mínimo de venda 1º Leilão:\s*(R\$ \d{1,3}(?:\.\d{3})*,\d{2})', texto) valor_segundo_leilao = re.search(r'Valor mínimo de venda 2º Leilão:\s*(R\$ \d{1,3}(?:\.\d{3})*,\d{2})', texto) data_segundo_leilao = re.search(r'Data do 2º Leilão - (\d{2}/\d{2}/\d{4}) - \d{2}h\d{2}', texto) valor_avaliacao = re.search(r'Valor de avaliação:\s*(R\$ \d{1,3}(?:\.\d{3})*,\d{2})', texto) imagem_tag = soup.find('img', id='preview') link_imagem = None if imagem_tag and 'src' in imagem_tag.attrs: src = imagem_tag['src'] link_imagem = f"https://venda-imoveis.caixa.gov.br{src}" if src.startswith('/') else src # Garantir que os valores estão disponíveis antes de calcular a diferença diferenca, porcentagem = "Valor não encontrado.", "Valor não encontrado." if valor_segundo_leilao and valor_avaliacao: try: valor_segundo_leilao_float = float(re.sub(r'[^\d,]', '', valor_segundo_leilao.group(1)).replace(',', '.')) valor_avaliacao_float = float(re.sub(r'[^\d,]', '', valor_avaliacao.group(1)).replace(',', '.')) if valor_avaliacao_float != 0: diferenca = valor_avaliacao_float - valor_segundo_leilao_float porcentagem = (diferenca / valor_avaliacao_float) * 100 porcentagem = f"{porcentagem:.2f}%" else: porcentagem = "0.00%" except ValueError: diferenca, porcentagem = "Valor inválido.", "Valor inválido." return { 'minimoVendaPrimeiroLeilao': valor_primeiro_leilao.group(1) if valor_primeiro_leilao else "Valor não encontrado.", 'minimoVendaSegundoLeilao': valor_segundo_leilao.group(1) if valor_segundo_leilao else "Valor não encontrado.", 'dataSegundoLeilao': data_segundo_leilao.group(1) if data_segundo_leilao else "Data não encontrada.", 'linkImagem': link_imagem if link_imagem else "Imagem não encontrada.", 'diferencaSegundoLeilaoAvaliacao': locale.currency(diferenca, grouping=True) if isinstance(diferenca, (int, float)) else diferenca, 'porcentagemSegundoLeilaoAvaliacao': porcentagem } except Exception as e: erro_count += 1 return None Continue reading...