телеграмм-бот с selenum на сервере
Всем привет.
Стэк: Python 3.11, selenium-stealth, aiogram.
При разработке ТГ бота для парсинга сайта я столкнулся с проблемой в работоспособности скрипта. На моем компьютере программа работает без нареканий, но при выгрузке на сервер она отказывается работать должным образом. При нажатии на кнопку "Старт", программа не запускает chromedriver и не осуществляет парсинг.
При этом, однажды программа все таки заработала на сервере. Это было удивлением для меня, потому что я вообще не запускал код, просто перезапустил проект. Я осуществлял запуск скрипт на деплой хостинге Amvera, а также на арендованном VPS. Везде программа не выдает никаких логов об ошибке, хотя я прописал декоратор, который должен выводить информацию об ошибке.
На сервере запускаю файл new.py, который отвечает за работу телеграмм-бота.
main.py:
def ex(func):
async def show_ex(*args, **kwargs):
try:
await func(*args, **kwargs)
except Exception as e:
print('Ошибка: ', e)
return show_ex
@ex
async def process_user(user_id, products):
conn = await aiosqlite.connect('test.db') # Подключаемся к базе данных асинхронно
async with conn.cursor() as cursor:
tasks = []
for product_key, product_value in products.items():
table_exists = await check_table_exists(conn, product_value)
if not table_exists:
await create_table(cursor, product_value.replace(" ", ''))
url = f'https://example.com'
task = asyncio.create_task(process_product(user_id, cursor, product_value, url))
tasks.append(task)
await asyncio.gather(*tasks)
await conn.commit()
@ex
async def process_product(user_id, cursor, product_value, url):
options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-gpu")
options.add_argument("--remote-debugging-port=9230")
service = Service(executable_path="/home/test/chromedriver")
driver = webdriver.Chrome(service=service, options=options)
try:
driver.maximize_window()
driver.get(url)
card_divs = driver.find_elements(By.XPATH, '//li[@class="sc-bcd1c877-2 gWHcGv"]')[:10]
for card_div in card_divs:
try:
new_price = float(
(card_div.find_element(By.TAG_NAME, 'span').text.replace('¥', '').replace(' ', '').replace(',', '').
replace('\n', '')))
except ValueError:
new_price = 'СКИДКА'
except selenium.common.exceptions.NoSuchElementException:
new_price = '?'
link = card_div.find_element(By.TAG_NAME, 'a').get_attribute('href')
img = card_div.find_element(By.TAG_NAME, 'img').get_attribute('src')
await insert_data(cursor, product_value, new_price, link, img)
print(new_price, link, img)
card = (f"Товар: {link} \n"
f"Цена: {new_price} руб.\n")
if card:
await send_product_messages(user_id, card, img)
else:
print('Товаров нет')
await bot.send_message(user_id, 'Товаров нет')
finally:
driver.stop_client()
driver.close()
driver.quit()
new.py :
tracemalloc.start()
async def run_periodic(user_id, products):
while True:
await process_user(user_id, products)
await asyncio.sleep(300)
@dp.message_handler(Text(equals="Запуск"))
@rights
async def run_program(message: types.Message):
await message.answer("Программа запущена")
user_id = str(message.from_user.id)
with open('users.json', 'r', encoding='utf-8') as file:
users = json.load(file)
if 'Товары' not in users[user_id]:
await message.answer("У Вас нет товаров для запуска программы.")
return
asyncio.create_task(run_periodic(user_id, users[user_id]['Товары']))
Ответы (1 шт):
Облако Амвера использует под капотом контейнеры. Чтобы было возможно использовать selenium в контейнере, надо поставить необходимые библиотеки и указать порт дисплея:
ENV DISPLAY=:99
Есть уже готовые образы python с selenium, например: joyzoursky/python-chromedriver
Таким образом, Dockerfile может быть следующим:
FROM joyzoursky/python-chromedriver:3.9-selenium
ENV PYTHONUNBUFFERED=1 # прокидываем логи из консоли
WORKDIR /app
COPY requirements.txt /app
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "main.py"]