From ded74b2cc9bd646b1b969353ac900444aac48faf Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 10 Apr 2026 23:01:44 +1000 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=B1=D0=B4=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D1=82=D1=81=D0=B5?= =?UTF-8?q?=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 63 ++++++++++++++++++++++++++++++++------------------ work_parser.py | 41 +++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/main.py b/main.py index 949fa3f..a677714 100644 --- a/main.py +++ b/main.py @@ -26,12 +26,26 @@ import uvicorn import requests +# Добавляем импорт psycopg2 +import psycopg2 + # Локальные импорты # import settings_work as sw import work_parser as wp DOCUMENTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "documents") +# Создаём соединение с PostgreSQL +conn = psycopg2.connect( + dbname="parsed_url", + user="postgres", + password="qwertyqwerty123123", + host="45.129.78.228", + connect_timeout=100, + options="-c statement_timeout=30000" # таймаут запроса 30 сек +) +conn.autocommit = True + @asynccontextmanager async def lifespan(app: FastAPI): """Управление жизненным циклом приложения""" @@ -386,7 +400,7 @@ def start_pars_all_istochnik(url:str, promt:str): soup = BeautifulSoup(response.text, 'html.parser') base_domain = urlparse(url).netloc - # links = set() + # links = [] for a_tag in soup.find_all('a', href=True): href = a_tag['href'].strip() if not href or href.startswith('mailto:') or href.startswith('javascript:'): @@ -402,30 +416,33 @@ def start_pars_all_istochnik(url:str, promt:str): continue # Фильтрация по ключевым словам (пример для новостных сайтов) - # path_lower = parsed.path.lower() - # if any(keyword in path_lower for keyword in ['/news/', 'article', '2023', '2024', '/blog/', '/post/']): - + path_lower = parsed.path.lower() + # if any(keyword in path_lower for keyword in ['/news/', 'article', '2026', '2027', '/blog/', '/post/']): print(f"Парсинг {abs_url}") - try: - article = Article(abs_url) - article.download() - article.parse() - - if len(article.text) > 200 and article.publish_date: - time_text = article.publish_date.strftime("%Y/%m/%d %H:%M:%S") - print("URL:", abs_url) - print("Заголовок:", article.title) - print("Дата публикации:", time_text) - print("Текст статьи:", article.text) - response_text = gpt_response_message(str(article.text), promt) - print(response_text) - if response_text: - update_bd_and_create_document(response_text=response_text, article_date=time_text, url=abs_url, parsed_at=str(dt.now()), original_text=article.text, other=url) + if check_url(abs_url) == False and wp.check_error_url(abs_url): + try: + article = Article(abs_url) + article.download() + article.parse() + + if len(article.text) > 200 and article.publish_date: + time_text = article.publish_date.strftime("%Y/%m/%d %H:%M:%S") + print("URL:", abs_url) + print("Заголовок:", article.title) + print("Дата публикации:", time_text) + print("Текст статьи:", article.text) + response_text = gpt_response_message(str(article.text), promt) + print(response_text) + if response_text: + update_bd_and_create_document(response_text=response_text, article_date=time_text, url=abs_url, parsed_at=str(dt.now()), original_text=article.text, other=url) + else: + wp.add_error_url(url, abs_url) + except Exception as e: + print(f"Ошибка при обработке статьи {abs_url}: {e}") + logger.info(f"Ошибка при обработке статьи {abs_url}: {e}") + continue # Продолжаем со следующей статьей + - except Exception as e: - print(f"Ошибка при обработке статьи {abs_url}: {e}") - logger.info(f"Ошибка при обработке статьи {abs_url}: {e}") - continue # Продолжаем со следующей статьей wp.update_task(task_id, status='completed', finished_at=datetime.utcnow()) diff --git a/work_parser.py b/work_parser.py index 4c8af04..728317c 100644 --- a/work_parser.py +++ b/work_parser.py @@ -124,6 +124,44 @@ def get_all_promt(): return {"sources": sources} + +def create_table_error_url(): + with conn.cursor() as cur: + cur.execute(""" + CREATE TABLE IF NOT EXISTS error_url ( + id SERIAL PRIMARY KEY, + source_url TEXT NOT NULL, + error_sources_url TEXT NOT NULL + ); + """) + print("Таблица error_url создана или уже существует") + + +def add_error_url(source_url: str, error_sources_url: str): + """Добавляет запись об ошибке URL""" + with conn.cursor() as cur: + cur.execute(""" + INSERT INTO error_url (source_url, error_sources_url) + VALUES (%s, %s) + RETURNING id; + """, (source_url, error_sources_url)) + return cur.fetchone()[0] + + +def check_error_url(error_sources_url: str) -> bool: + """Проверяет, есть ли запись в таблице error_url с таким URL""" + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute(""" + SELECT 1 FROM error_url + WHERE error_sources_url = %s + LIMIT 1; + """, (error_sources_url,)) + row = cur.fetchone() + + + return row is None + + # Пример использования # if __name__ == "__main__": # # create_table_config_gpt() # <-- раскомментировать эту строку @@ -132,4 +170,5 @@ def get_all_promt(): # "name": "Корея", # "promt": "Задача: Перевод на русский язык и тематическая фильтрация новостных статей из китайской прессы. \n Необходимо переводить текст статьи и определять, относится ли она к КНР по указанным темам: \n 1. Перевод\n Переведи предоставленный китайский текст на русский язык, сохранив оригинальный смысл, стиль и структуру.\n Текст:\n {content}\n -------------------------------------\n 2. Отбирай исключительно новости, прямо относящиеся к Китаю, его безопасности, соседним странам и территориям, влияющим на интересы Китая.\n Если не относится к Китаю — считаем, что статья НЕ подходит, и отдаем пустой JSON:\n {\"text\": \"\", \"pereskas\": \"\", \"title\": \"\", \"topics\": []}\n Если привязка есть — переходи к шагу 3. \n -------------------------------------\n 3. Тематическая классификация\n Определи, относится ли статья к одной или нескольким темам из списка:\n 1) Военные новости — конфликты, учения, мобилизация, закупки вооружений. \n 2) Пограничная деятельность — охрана границы, пограничные учения, строительство или модернизация пограничной инфраструктуры, техника для пограничников. \n 3) Пункты пропуска на границе с РФ — изменения режима работы, строительство, реконструкция, оборудование, логистика. \n 4) Пограничные реки — состояние рек, экология, инфраструктурные проекты, мониторинг. \n 5) Чрезвычайные ситуации — природные и техногенные происшествия, особенно затрагивающие пограничные реки и прилегающие земли. \n 6) Санитарно-эпидемиологическая обстановка — эпидемии, эпизоотии, эпифитотии, угрозы и меры предотвращения. \n 7) Индустриальные проекты (арктическое/антарктическое направление). \n 8) Индустриальные проекты в приграничных районах — заводы, производства, технопарки, новые технологии. \n 9) Инфраструктурные проекты в приграничных районах — дороги, мосты, транспорт, логистика. \n 10) Культура малочисленных народностей (нанайцы, монголы, уйгуры, нанайцы и хэчжэ) — политика, традиции, бытовая жизнь нанайцев, монголов, уйгуров, и хэчжэ (малочисленных народов).\n\n Отметь только те темы, которым статья действительно соответствует.\n\n -------------------------------------\n 4. Формат ответа \n Вернуть строго JSON без пояснений и дополнительных слов:\n {\n \"translation_text\": \"<перевод текста статьи на русский язык (дословный, точный и без сокращений ) >\",\n \"short_text\": \"<пересказ переведённого текста>\",\n \"title\": \"<краткая суть новости (1–2 предложения)>\",\n \"category\": \"<названий категорий, которым соответствует статья>\"\n }\n Если статья не относится ни к одной теме или не привязана к нужным регионам — вернуть:\n {\"translation_text\": \"\", \"short_text\": \"\", \"title\": \"\", \"category\": \"\"}" # }) - # print(get_promt("japan")) \ No newline at end of file + # print(get_promt("japan")) + # create_table_error_url() \ No newline at end of file