парсинг страницы происходит, ошибка при отправке запроса на нейронку, нужно разбираться, возможно из за величины текста или частые запросы
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
113
main.py
113
main.py
@@ -9,7 +9,6 @@ from datetime import datetime, timedelta
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
import zipfile
|
import zipfile
|
||||||
# from rarfile import RarFile
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
# Сторонние библиотеки (third-party)
|
# Сторонние библиотеки (third-party)
|
||||||
@@ -17,17 +16,18 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from docx import Document
|
from docx import Document
|
||||||
|
from newspaper import Article
|
||||||
from fastapi import BackgroundTasks, FastAPI, Query, Request, Depends
|
from fastapi import BackgroundTasks, FastAPI, Query, Request, Depends
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, HttpUrl
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin, urlparse, urldefrag
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
# Локальные импорты
|
# Локальные импорты
|
||||||
import settings_work as sw
|
# import settings_work as sw
|
||||||
import work_parser as wp
|
import work_parser as wp
|
||||||
|
|
||||||
DOCUMENTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "documents")
|
DOCUMENTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "documents")
|
||||||
@@ -206,21 +206,9 @@ def extract_text_from_url(url, timeout=10, verify=True):
|
|||||||
return "\n".join(content_text), time_t
|
return "\n".join(content_text), time_t
|
||||||
|
|
||||||
# Общий запрос на GPT
|
# Общий запрос на GPT
|
||||||
def gpt_response_message(content, ist_number=1):
|
def gpt_response_message(content: str, name_promt: str):
|
||||||
|
|
||||||
# Promts = sw.read_settings().sources
|
contentGPT = wp.get_promt(name_promt).replace('{content}', content)
|
||||||
|
|
||||||
# if ist_number == 1:
|
|
||||||
# contentGPT = Promts[0].prompt.replace('{content}', content)
|
|
||||||
# else:
|
|
||||||
# contentGPT = Promts[1].prompt.replace('{content}', content)
|
|
||||||
|
|
||||||
if ist_number == 1:
|
|
||||||
url_ist = "http://epaper.hljnews.cn/hljrb/pc/layout"
|
|
||||||
else:
|
|
||||||
url_ist = "https://def.ltn.com.tw/breakingnewslist"
|
|
||||||
|
|
||||||
contentGPT = wp.get_promt(url_ist).replace('{content}', content)
|
|
||||||
|
|
||||||
url = 'http://45.129.78.228:8484' #10.8.0.14:5500
|
url = 'http://45.129.78.228:8484' #10.8.0.14:5500
|
||||||
params = {'text': contentGPT}
|
params = {'text': contentGPT}
|
||||||
@@ -242,15 +230,6 @@ def gpt_response_message(content, ist_number=1):
|
|||||||
logger.info(f"Привышен лимит запросов {max_retries}")
|
logger.info(f"Привышен лимит запросов {max_retries}")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# перезапуск сервиса GPT при неудачных попытках запроса
|
|
||||||
# def restart_service(service_name):
|
|
||||||
# try:
|
|
||||||
# subprocess.run(['sudo', 'systemctl', 'restart', service_name], check=True)
|
|
||||||
# time.sleep(30)
|
|
||||||
# print(f"Сервис {service_name} успешно перезапущен")
|
|
||||||
# except subprocess.CalledProcessError:
|
|
||||||
# print(f"Не удалось перезапустить сервес {service_name}")
|
|
||||||
|
|
||||||
# Общие функции проверки ссылок
|
# Общие функции проверки ссылок
|
||||||
def check_url(url):
|
def check_url(url):
|
||||||
print(url)
|
print(url)
|
||||||
@@ -342,7 +321,6 @@ def start_pars_one_istochnik(data_init=""):
|
|||||||
for page_number in range(1, 9):
|
for page_number in range(1, 9):
|
||||||
|
|
||||||
url = f'http://epaper.hljnews.cn/hljrb/pc/layout/{current_year}{current_month}/{current_day}/node_0{page_number}.html'
|
url = f'http://epaper.hljnews.cn/hljrb/pc/layout/{current_year}{current_month}/{current_day}/node_0{page_number}.html'
|
||||||
|
|
||||||
wp.update_task(task_id, status='in_progress', source_url=url, started_at=datetime.utcnow())
|
wp.update_task(task_id, status='in_progress', source_url=url, started_at=datetime.utcnow())
|
||||||
|
|
||||||
print(f"Сбор href из: {url}")
|
print(f"Сбор href из: {url}")
|
||||||
@@ -358,7 +336,7 @@ def start_pars_one_istochnik(data_init=""):
|
|||||||
print(f"Страница {page_number} [{i}/{len(hrefs)}] parsing {link}")
|
print(f"Страница {page_number} [{i}/{len(hrefs)}] parsing {link}")
|
||||||
text = extract_text_from_url_one(link)
|
text = extract_text_from_url_one(link)
|
||||||
if len(text) >= 100:
|
if len(text) >= 100:
|
||||||
response_text = gpt_response_message(text, ist_number=2)
|
response_text = gpt_response_message(text, url_ist = "http://epaper.hljnews.cn/hljrb/pc/layout")
|
||||||
print(response_text)
|
print(response_text)
|
||||||
if response_text:
|
if response_text:
|
||||||
update_bd_and_create_document(response_text=response_text, article_date=f"{current_year}/{current_month}/{current_day}", url=link, parsed_at=str(dt.now()), original_text=text, other=url)
|
update_bd_and_create_document(response_text=response_text, article_date=f"{current_year}/{current_month}/{current_day}", url=link, parsed_at=str(dt.now()), original_text=text, other=url)
|
||||||
@@ -386,7 +364,7 @@ def start_pars_two_istochnik():
|
|||||||
try:
|
try:
|
||||||
text, time_text = extract_text_from_url(hrefs)
|
text, time_text = extract_text_from_url(hrefs)
|
||||||
if len(text) >= 100:
|
if len(text) >= 100:
|
||||||
response_text = gpt_response_message(text)
|
response_text = gpt_response_message(text, url_ist = "https://def.ltn.com.tw/breakingnewslist")
|
||||||
print(response_text)
|
print(response_text)
|
||||||
if response_text:
|
if response_text:
|
||||||
update_bd_and_create_document(response_text=response_text, article_date=time_text, url=hrefs, parsed_at=str(dt.now()), original_text=text, other=url)
|
update_bd_and_create_document(response_text=response_text, article_date=time_text, url=hrefs, parsed_at=str(dt.now()), original_text=text, other=url)
|
||||||
@@ -395,6 +373,64 @@ def start_pars_two_istochnik():
|
|||||||
|
|
||||||
wp.update_task(task_id, status='completed', finished_at=datetime.utcnow())
|
wp.update_task(task_id, status='completed', finished_at=datetime.utcnow())
|
||||||
|
|
||||||
|
#Функции start любого источника
|
||||||
|
def start_pars_all_istochnik(url:str, promt:str):
|
||||||
|
task_id = wp.insert_task(status='queued', source_url=url)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
except requests.RequestException:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
soup = BeautifulSoup(response.text, 'html.parser')
|
||||||
|
base_domain = urlparse(url).netloc
|
||||||
|
|
||||||
|
# links = set()
|
||||||
|
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:'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Приведение к абсолютному URL и удаление якорей (#...)
|
||||||
|
abs_url = urljoin(url, href)
|
||||||
|
abs_url, _ = urldefrag(abs_url)
|
||||||
|
parsed = urlparse(abs_url)
|
||||||
|
|
||||||
|
# Фильтр: ссылка должна быть на тот же домен
|
||||||
|
if parsed.netloc != base_domain:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Фильтрация по ключевым словам (пример для новостных сайтов)
|
||||||
|
# path_lower = parsed.path.lower()
|
||||||
|
# if any(keyword in path_lower for keyword in ['/news/', 'article', '2023', '2024', '/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)
|
||||||
|
|
||||||
|
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())
|
||||||
|
|
||||||
|
# start_pars_all_istochnik("https://www.asahi.com", "japan")
|
||||||
|
|
||||||
# Функции для автоматического запуска
|
# Функции для автоматического запуска
|
||||||
def scheduled_parser_1():
|
def scheduled_parser_1():
|
||||||
start_pars_one_istochnik()
|
start_pars_one_istochnik()
|
||||||
@@ -406,16 +442,25 @@ class ParserOneRequest(BaseModel):
|
|||||||
time: str
|
time: str
|
||||||
|
|
||||||
@app.post("/parser_1", summary="Запуск процесса парсинга первого источника")
|
@app.post("/parser_1", summary="Запуск процесса парсинга первого источника")
|
||||||
async def process_data(data: ParserOneRequest, background_tasks: BackgroundTasks):
|
async def process_parser_one_ist(data: ParserOneRequest, background_tasks: BackgroundTasks):
|
||||||
istochnik = data.time.split("-")
|
istochnik = data.time.split("-")
|
||||||
background_tasks.add_task(start_pars_one_istochnik, istochnik)
|
background_tasks.add_task(start_pars_one_istochnik(istochnik))
|
||||||
return {"message": "Процесс парсинга 1 источника запущен"}
|
return {"message": "Процесс парсинга 1 источника запущен"}
|
||||||
|
|
||||||
@app.post("/parser_2" , summary="Запуск процеса парсинга второго источника")
|
@app.post("/parser_2" , summary="Запуск процеса парсинга второго источника")
|
||||||
async def process_data_gpt(background_tasks: BackgroundTasks):
|
async def process_parser_two_ist(background_tasks: BackgroundTasks):
|
||||||
background_tasks.add_task(start_pars_two_istochnik)
|
background_tasks.add_task(start_pars_two_istochnik)
|
||||||
return {"message": "Процесс парсинга 2 источника запущен"}
|
return {"message": "Процесс парсинга 2 источника запущен"}
|
||||||
|
|
||||||
|
class Parserall(BaseModel):
|
||||||
|
url: HttpUrl
|
||||||
|
promt: str
|
||||||
|
|
||||||
|
@app.post("/parser_all" , summary="Запуск процеса парсинга любого источника")
|
||||||
|
async def process_parser_all_ist(url: Parserall, background_tasks: BackgroundTasks):
|
||||||
|
background_tasks.add_task(start_pars_all_istochnik(str(url.url), url.promt))
|
||||||
|
return {"message": "Процесс парсинга любого источника запущен"}
|
||||||
|
|
||||||
# GET метод для получения
|
# GET метод для получения
|
||||||
@app.get("/get_tasks_offset", summary="Метод получения задач парсинга")
|
@app.get("/get_tasks_offset", summary="Метод получения задач парсинга")
|
||||||
def get_tasks_offset(limit: int = Query(10, gt=0), offset: int = Query(0, ge=0)):
|
def get_tasks_offset(limit: int = Query(10, gt=0), offset: int = Query(0, ge=0)):
|
||||||
@@ -536,7 +581,7 @@ async def download_all(dates: DownloadRange, background_tasks: BackgroundTasks):
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@app.get("/logs")
|
@app.get("/logs", summary="Показать логи")
|
||||||
def get_logs():
|
def get_logs():
|
||||||
with open("app.log", "r") as file:
|
with open("app.log", "r") as file:
|
||||||
lines = file.readlines()[-10:] # последние 10 строк
|
lines = file.readlines()[-10:] # последние 10 строк
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import psycopg2
|
import psycopg2
|
||||||
from psycopg2.extras import RealDictCursor
|
from psycopg2.extras import RealDictCursor
|
||||||
from pydantic import BaseModel, HttpUrl
|
from pydantic import BaseModel
|
||||||
|
|
||||||
# Подключение к БД (укажи свои параметры)
|
# Подключение к БД (укажи свои параметры)
|
||||||
conn = psycopg2.connect(
|
conn = psycopg2.connect(
|
||||||
@@ -90,7 +90,7 @@ def create_table_config_gpt():
|
|||||||
print("Таблица config_gpt создана или уже существует")
|
print("Таблица config_gpt создана или уже существует")
|
||||||
|
|
||||||
class Source (BaseModel):
|
class Source (BaseModel):
|
||||||
url: HttpUrl
|
url: str
|
||||||
name: str
|
name: str
|
||||||
promt: str
|
promt: str
|
||||||
|
|
||||||
@@ -105,12 +105,12 @@ def update_promt(data: Source):
|
|||||||
ON CONFLICT (url) DO UPDATE SET
|
ON CONFLICT (url) DO UPDATE SET
|
||||||
name = EXCLUDED.name,
|
name = EXCLUDED.name,
|
||||||
promt = EXCLUDED.promt
|
promt = EXCLUDED.promt
|
||||||
""", (str(data.url), data.name, data.promt))
|
""", (data.url, data.name, data.promt))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
def get_promt(url):
|
def get_promt(promt_name_url):
|
||||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||||
cur.execute("SELECT promt FROM config_gpt WHERE url = %s", (url,))
|
cur.execute("SELECT promt FROM config_gpt WHERE url = %s", (promt_name_url,))
|
||||||
promt = cur.fetchone()
|
promt = cur.fetchone()
|
||||||
return promt['promt']
|
return promt['promt']
|
||||||
|
|
||||||
@@ -126,9 +126,10 @@ def get_all_promt():
|
|||||||
|
|
||||||
# Пример использования
|
# Пример использования
|
||||||
# if __name__ == "__main__":
|
# if __name__ == "__main__":
|
||||||
# create_table_config_gpt() # <-- раскомментировать эту строку
|
# # create_table_config_gpt() # <-- раскомментировать эту строку
|
||||||
# update_promt({
|
# update_promt({
|
||||||
# "url": "http://epaper.hljnews.cn/hljrb/pc/layout",
|
# "url": "http://korei",
|
||||||
# "name": "source1",
|
# "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\": \"\"}"
|
# "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"))
|
||||||
Reference in New Issue
Block a user