Проблема с asyncio в телеграм боте на aiogram (python)
Пишу телеграм бота на библиотеке aiogram, и мои команды запускают долгие парсеры сайтов. Проблема в том, что когда один человек запустил такой парсер, у всех остальных перестает работать бот. Решил проблему использованием библиотеки threading, и создания процессов. Вот моя функция:
@dp.message_handler(commands=['test'])
async def function(message: types.Message):
threads = []
cursor.execute('SELECT user_id FROM database')
users = cursor.fetchall()
for n in users:
keyword = 'instagram'
user_id = message.chat.id
t = Thread(target=asyncio.run, args=(send_news(keyword,user_id), ))
threads.append(t)
t.start()
for t in threads:
t.join()
Вот также главная функция send_news, к которой идет запрос:
async def send_news(keyword, user_id):
news = parsers.parse_search_google(keyword)
await bot.send_message(user_id, 'YES')
Вот как работает парсер (если это нужно):
def check_url(url_feed):
"""функция получает линк на рсс ленту, возвращает распаршенную ленту с помощью feedpaeser"""
return feedparser.parse(url_feed)
def getHeadlines(url_feed):
"""функция для получения заголовков новости"""
headlines = []
lenta = check_url (url_feed)
for item_of_news in lenta['items']:
if len(headlines) <= 9:
headlines.append(item_of_news ['title'])
return headlines
def getLinks(url_feed):
"""функция для получения ссылки на источник новости"""
links = []
lenta = check_url(url_feed)
for item_of_news in lenta['items']:
links.append(item_of_news ['link'])
return links
def parse_search_google(keyword):
URL = f'https://news.google.com/rss/search?q={keyword}&hl=ru&gl=RU&ceid=RU:ru'
headlines = getHeadlines(URL)
link = getLinks(URL)
return headlines, link
Но проблема в том, как отправить сообщение пользователю из функции send_news? Я не силен в асинхронности. Я сделал функцию через asynco, а ее запуск через asyncio, и по идее это должно было решить проблему, но я получаю длинную ошибку:
RuntimeError: Task <Task pending name='Task-10' coro=<send_news() running at D:\7on\NewsletterBot\V1\bot.py:681> cb=[_run_until_complete_cb() at C:\Users\ddeme\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py:184]> got Future <Future pending cb=[shield.._outer_done_callback() at C:\Users\ddeme\AppData\Local\Programs\Python\Python39\lib\asyncio\tasks.py:907]> attached to a different loop Future exception was never retrieved future: <Future finished exception=ServerDisconnectedError('Server disconnected')> aiohttp.client_exceptions.ServerDisconnectedError: Server disconnected`
Ответы (1 шт):
Для перехода к обычным таскам вот это:
threads = []
...
for n in users:
...
t = Thread(target=asyncio.run, args=(send_news(keyword,user_id), ))
threads.append(t)
t.start()
for t in threads:
t.join()
можно упростить таким образом:
tasks = []
...
for n in users:
...
t = asyncio.create_task(send_news(keyword,user_id))
tasks.append(t)
for t in asyncio.as_completed(tasks):
await t
Но тут всё стопариться будет на feedparser - тут нужно поставить aiohttp перед ним
import aiohttp
async def send_news(keyword, user_id):
news = await parsers.parse_search_google(keyword)
await bot.send_message(user_id, 'YES')
async def check_url(keyword, session):
...
async with session.get(
"https://news.google.com/rss/search",
params={'q':keyword, 'ceid':'RU:ru' ...}) as r:
data_feed = await r.text()
return feedparser.parse(data_feed)
def getHeadlines(lenta):
...
def getLinks(lenta):
...
async def parse_search_google(keyword):
...
session = aiohttp.ClientSession()
lenta = await check_url(keyword, session)
await session.close()
headlines = getHeadlines(lenta)
link = getLinks(lenta)
...
Попутно исправил двойной заход в check_url с одной и той же ссылкой.