Крашится Телеграм бот когда его используют два человека одновременно

Рейтинг: 1Ответов: 1Опубликовано: 21.06.2023

Пишу бота на aiogram, использую Python. Вот код:

import requests
from bs4 import BeautifulSoup 
import asyncio
import logging
from aiogram import Bot, Dispatcher, executor, types
import config


TIMEOUT=30 #Задержка перед поиском новых товаров


logging.basicConfig(level=logging.INFO)
bot = Bot(token=config.BOT_TOKEN)
dp = Dispatcher(bot)

search_dict = {}

@dp.message_handler(commands=['start'])
async def start_message(message: types.message):
    await message.answer('Для начала моей работы напиши команду /search и укажи что хочешь искать. Пример: /search Мозги радика\nДля остановки поиска воспользуйся /stop\n Бот показывает лишь новые поступающие предложения')

@dp.message_handler(commands=['search'])
async def search(message : types.message):  
    try:
        search_dict[message.from_id].close()
        search_dict.pop(message.from_id)
    except:
        pass
    if (message.text=='/search'):
        await message.answer('Вы не написали тему для поиска') 
    else:
        search_dict[message.from_id] = checking(message)
        await search_dict[message.from_id]

   
async def checking(message):
    tag=str(message.text)
    tag=tag[8:]
    last_item =''
    print('[LOG:] '+tag)
    while (True):  
        page = requests.get('https://www.kufar.by/l?cmp=0&query='+tag+'&sort=lst.d')
        if (page.status_code==200):
            soup = BeautifulSoup(page.text, 'html.parser')
            variants = soup.find('div',class_='styles_cards___qpff')
            products=variants.find_all('section')
            i=0
            while(products[i].find('a',class_='styles_polepos__bO53x')!=None):
                i=i+1
            product = products[i]
            url= product.find('a', class_='styles_wrapper__yaLfq').get('href')
            url =url.split('?')[0]
            price=product.find('div',class_='styles_top__HNf3a').find('p',class_='styles_price__9JZaB').find('span').text
            name=product.find('h3',class_='styles_title__ARIVF').text
            image =product.find('div',class_='styles_container__dR7XZ').find('img')['data-src']
            state = product.find('div', class_='styles_secondary__NEYhw').find('p').text
            if not (url==last_item):
                last_item=url
                print('[LOG:] '+url)
                await message.answer_photo(photo=image,caption='Название: '+name+'\nЦена: '+price+'\nг. '+state+'\nПодробнее:\n'+url)
            await asyncio.sleep(TIMEOUT)
        

@dp.message_handler(commands=['stop'])
async def stop(message : types.message):
    try:
        search_dict[message.from_id].close()
        search_dict.pop(message.from_id)
        await message.answer('Поиск остановлен')
    except KeyError:
        await message.answer('Вы ничего не искали')

if __name__ == '__main__':
    executor.start_polling(dp, skip_updates=True)

Бот - парсер сайта kufar.by Команда /search проверяет новые товары каждые 30 секунд. Когда один человек выполняет эту команду, бот работает нормально, когда два - бот останавливается. Код я писал вроде так, чтобы нормально работал в асинхроне, но что-то пошло не так. В общем: помогите пожалуйста)

Ответы

▲ 0Принят

Вы используете не асинхронную библиотеку для запросов, поэтому в момент запроса асинхронность всего кода ломается. Из-за того, что библиотека requests сама по себе не асинхронная, следовательно функция checking(), хоть и имеет префикс async, но работать асинхронно она не может. И поэтому при вызове функции checking() интерпретатор не обрабатывает сообщения в телеграм, а ждет завершения выполнения работы функции.

Чтобы это исправить могу назвать два способа:

  1. Запускать функцию checking() в новом потоке.
  2. Использовать асинхронный аналог библиотеки requests - aiohttp.

Вот пример кода, если последовать второму пункту:

import aiohttp
from bs4 import BeautifulSoup 
import asyncio
import logging
from aiogram import Bot, Dispatcher, executor, types
import config


TIMEOUT=30 #Задержка перед поиском новых товаров


logging.basicConfig(level=logging.INFO)
bot = Bot(token=config.BOT_TOKEN)
dp = Dispatcher(bot)

search_dict = {}

@dp.message_handler(commands=['start'])
async def start_message(message: types.message):
    await message.answer('Для начала моей работы напиши команду /search и укажи что хочешь искать. Пример: /search Мозги радикаnДля остановки поиска воспользуйся /stopn Бот показывает лишь новые поступающие предложения')

@dp.message_handler(commands=['search'])
async def search(message : types.message):  
    try:
        search_dict[message.from_id].close()
        search_dict.pop(message.from_id)
    except:
        pass
    if (message.text=='/search'):
        await message.answer('Вы не написали тему для поиска') 
    else:
        search_dict[message.from_id] = await checking(message)
        await search_dict[message.from_id]

   
async def checking(message):
    tag=str(message.text)
    tag=tag[8:]
    last_item =''
    print('[LOG:] '+tag)
    async with aiohttp.ClientSession() as session:
        while (True):  
            async with session.get('https://www.kufar.by/l?cmp=0&query='+tag+'&sort=lst.d') as response:
                if (response.status==200):
                    soup = BeautifulSoup(await response.text(), 'html.parser')
                    variants = soup.find('div',class_='styles_cards___qpff')
                    products=variants.find_all('section')
                    i=0
                    while(products[i].find('a',class_='styles_polepos__bO53x')!=None):
                        i=i+1
                    product = products[i]
                    url= product.find('a', class_='styles_wrapper__yaLfq').get('href')
                    url =url.split('?')[0]
                    price=product.find('div',class_='styles_top__HNf3a').find('p',class_='styles_price__9JZaB').find('span').text
                    name=product.find('h3',class_='styles_title__ARIVF').text
                    image =product.find('div',class_='styles_container__dR7XZ').find('img')['data-src']
                    state = product.find('div', class_='styles_secondary__NEYhw').find('p').text
                    if not (url==last_item):
                        last_item=url
                        print('[LOG:] '+url)
                        await message.answer_photo(photo=image,caption='Название: '+name+'nЦена: '+price+'nг. '+state+'nПодробнее:n'+url)
                    await asyncio.sleep(TIMEOUT)
        

@dp.message_handler(commands=['stop'])
async def stop(message : types.message):
    try:
        search_dict[message.from_id].close()
        search_dict.pop(message.from_id)
        await message.answer('Поиск остановлен')
    except KeyError:
        await message.answer('Вы ничего не искали')

if __name__ == '__main__':
    executor.start_polling(dp, skip_updates=True)

Вроде бы код решает вашу проблему. Как минимум мне несколько запросов удалось отправить одновременно.

И кстати, лучше создайте новый модуль в котором будут функции для парсинга и просто его импортируйте в файл main, так будет намного лучше.