critico o favorable

This commit is contained in:
Your Name
2025-02-05 22:45:41 +01:00
parent 987e24bd00
commit 03d334fade
9 changed files with 65 additions and 287 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
entorno
__pypchache__
__pypchache__
*.pyc

View File

@ -1,4 +1,4 @@
from sqlalchemy import Column, Integer, String, Text, DateTime
from sqlalchemy import Column, Integer, String, Text, DateTime, Boolean
from datetime import datetime
from .database import Base
@ -12,3 +12,5 @@ class NewsItem(Base):
fuente = Column(String(255), nullable=True)
fecha = Column(DateTime, default=datetime.utcnow)
link = Column(String(500), unique=True, nullable=False)
critico = Column(Boolean, default=False)
favorable = Column(Boolean, default=False)

View File

@ -15,6 +15,8 @@ class NewsItemCreate(BaseModel):
fuente: str | None = None
fecha: datetime | None = None
link: str
critico:bool
favorable:bool
@router.post("/news/")
def create_news_item(item: NewsItemCreate, db: Session = Depends(get_db)):
@ -30,7 +32,9 @@ def create_news_item(item: NewsItemCreate, db: Session = Depends(get_db)):
autor=item.autor,
fuente=item.fuente,
fecha=item.fecha or datetime.utcnow(),
link=item.link
link=item.link,
critico=item.critico,
favorable=item.favorable
)
db.add(new_item)

View File

@ -1,93 +0,0 @@
import json
import requests
import sys
from bs4 import BeautifulSoup
def download_html_as_human(url):
"""
Descarga el HTML de una página web simulando un navegador real y usando cookies de sesión.
"""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
}
session = requests.Session()
response = session.get(url, headers=headers)
if response.status_code == 200:
return response.text
else:
return None
def extract_author_from_json(json_data):
"""
Extrae el autor del JSON-LD, incluso si está en una lista.
"""
if isinstance(json_data, list):
for item in json_data:
author = extract_author_from_json(item)
if author:
return author
elif isinstance(json_data, dict):
if 'author' in json_data:
author_data = json_data['author']
if isinstance(author_data, list):
for author in author_data:
if isinstance(author, dict) and 'name' in author:
return author['name']
elif isinstance(author_data, dict) and 'name' in author_data:
return author_data['name']
return None
def get_author_from_json_ld(soup):
"""
Extrae el autor de los metadatos JSON-LD, considerando estructuras con listas y objetos.
"""
scripts = soup.find_all('script', type='application/ld+json')
for script in scripts:
try:
json_data = json.loads(script.string)
author = extract_author_from_json(json_data)
if author:
return author
except json.JSONDecodeError:
continue
return None
def get_author_from_meta(soup):
"""
Extrae el autor de la etiqueta <meta> con el atributo property="nrbi:authors".
"""
meta_author = soup.find('meta', property='nrbi:authors')
if meta_author and 'content' in meta_author.attrs:
return meta_author['content']
return None
def get_author_from_url(url):
"""
Busca el autor en los metadatos JSON-LD y en la etiqueta <meta> de una URL.
"""
html_content = download_html_as_human(url)
if not html_content:
print("error")
return "No se pudo descargar la página."
soup = BeautifulSoup(html_content, 'html.parser')
author = get_author_from_json_ld(soup)
if author:
return author
author = get_author_from_meta(soup)
if author:
return author
return "Autor no encontrado en los metadatos."
if __name__ == "__main__":
if len(sys.argv) > 1:
url = sys.argv[1]
print(get_author_from_url(url))
else:
print("Uso: python autorsearcher.py <URL>")

1
crontab.txt Normal file
View File

@ -0,0 +1 @@
0 1 * * * python3 /app/main.py >> /var/log/cron.log 2>&1 #modificar para ajustar

View File

@ -1,27 +0,0 @@
import requests
import json
def is_security_related(prompt):
url = "http://localhost:11434/api/generate"
data = {
"model": "llama3",
"prompt": f"Does the following topic relate to national defense, armed forces, police, espionage, or intelligence? Answer only with 'true' or 'false'. Topic: {prompt}",
}
response = requests.post(url, json=data)
try:
# Dividir la respuesta en líneas y parsear cada una
for line in response.text.strip().split("\n"):
json_data = json.loads(line)
if "response" in json_data and json_data["response"].strip():
return json_data["response"].strip().lower() == "true"
except json.JSONDecodeError as e:
print("JSON Decode Error:", e)
return False
# Prueba con un ejemplo
prompt = "La Laguna Tenerife inició la segunda vuelta de la Liga ACB defendiendo la cuarta plaza de la competición y hoy (12.00 horas), ante el Río Breogán, ese será su objetivo: ganar y seguir estando entre los cuatro primeros y vigilando a los otros tres rivales: Real Madrid, Valencia y Unicaja-, a un solo un triunfo de distancia.\nEn el partido de hoy, el conjunto aurinegro afronta un partido complicado, pues el Río Breogán viene jugando a un buen nivel. De los cuatro últimos partidos ha ganado tres -ante el Bilbao (76-71), Coruña (83-80) y Andorra (93-101)- y han caído frente al Baskonia (97-91).\nSituados en el puesto doce, el Breogán no será un rival fácil para los aurinegros que, pese a que también están jugando bien sobre todo en casa, saben que tendrán que estar al máximo nivel para poder sacar el partido adelante.\nGanar en confianza desde la defensa y jugar con equilibrio en ataque serán dos de los aspectos que deben conseguir los aurinegros en un enfrentamiento complicado, ante un rival que va hacia arriba y que sabe también las necesidades suyas para no complicarse en la zona de farolillo.\nEl entrenador del CB Canarias,Txus Vidorreta, tendrá a todos sus efectivos y, si el partido va como se espera, podrá seguir dando minutos en las rotaciones del equipo a Jaime Fernández, que ha podido entrenar.\nA dos jornadas de la disputa de la Copa del Rey en Gran Canaria, La Laguna Tenerife quiere irse preparando para esa cita con un primer enfrentamiento, en cuartos de final, contra el FC Barcelona.\nHoy será uno de esos “ensayos” importantes para saber cómo se encuentra el equipo para esa cita en la isla vecina, aunque ahora mismo el objetivo sigue estándo en la defensa de ese cuarto lugar de la Liga Endesa.\nUn desafío ante el cuadro de Luis Casimiro que necesitará igualmente del siempre entregado público insular, después de que la afición canarista confirmara desde el lunes pasado un nuevo sold out, el vigésimo primero consecutivo en partidos ACB entre el final de la campaña pasada y el vigente curso."
print(is_security_related(prompt))

View File

@ -22,6 +22,45 @@ def is_security_related(prompt):
return False
# Prueba con un ejemplo
prompt = "La Laguna Tenerife inició la segunda vuelta de la Liga ACB defendiendo la cuarta plaza de la competición y hoy (12.00 horas), ante el Río Breogán, ese será su objetivo: ganar y seguir estando entre los cuatro primeros y vigilando a los otros tres rivales: Real Madrid, Valencia y Unicaja-, a un solo un triunfo de distancia.\nEn el partido de hoy, el conjunto aurinegro afronta un partido complicado, pues el Río Breogán viene jugando a un buen nivel. De los cuatro últimos partidos ha ganado tres -ante el Bilbao (76-71), Coruña (83-80) y Andorra (93-101)- y han caído frente al Baskonia (97-91).\nSituados en el puesto doce, el Breogán no será un rival fácil para los aurinegros que, pese a que también están jugando bien sobre todo en casa, saben que tendrán que estar al máximo nivel para poder sacar el partido adelante.\nGanar en confianza desde la defensa y jugar con equilibrio en ataque serán dos de los aspectos que deben conseguir los aurinegros en un enfrentamiento complicado, ante un rival que va hacia arriba y que sabe también las necesidades suyas para no complicarse en la zona de farolillo.\nEl entrenador del CB Canarias,Txus Vidorreta, tendrá a todos sus efectivos y, si el partido va como se espera, podrá seguir dando minutos en las rotaciones del equipo a Jaime Fernández, que ha podido entrenar.\nA dos jornadas de la disputa de la Copa del Rey en Gran Canaria, La Laguna Tenerife quiere irse preparando para esa cita con un primer enfrentamiento, en cuartos de final, contra el FC Barcelona.\nHoy será uno de esos “ensayos” importantes para saber cómo se encuentra el equipo para esa cita en la isla vecina, aunque ahora mismo el objetivo sigue estándo en la defensa de ese cuarto lugar de la Liga Endesa.\nUn desafío ante el cuadro de Luis Casimiro que necesitará igualmente del siempre entregado público insular, después de que la afición canarista confirmara desde el lunes pasado un nuevo sold out, el vigésimo primero consecutivo en partidos ACB entre el final de la campaña pasada y el vigente curso."
print(is_security_related(prompt))
def is_critico(prompt):
url = "http://localhost:11434/api/generate"
data = {
"model": "llama3",
"prompt": f"Does the following text critics the armed forces, security forces as Guardia Civil or Police, intelligence agencies such as CNI? Answer only with 'true' or 'false'. Topic: {prompt}",
}
response = requests.post(url, json=data)
try:
# Dividir la respuesta en líneas y parsear cada una
for line in response.text.strip().split("\n"):
json_data = json.loads(line)
if "response" in json_data and json_data["response"].strip():
return json_data["response"].strip().lower() == "true"
except json.JSONDecodeError as e:
print("JSON Decode Error:", e)
return False
def is_favorable(prompt):
url = "http://localhost:11434/api/generate"
data = {
"model": "llama3",
"prompt": f"Does the following text favours the armed forces, security forces as Guardia Civil or Police, intelligence agencies such as CNI? Answer only with 'true' or 'false'. Topic: {prompt}",
}
response = requests.post(url, json=data)
try:
# Dividir la respuesta en líneas y parsear cada una
for line in response.text.strip().split("\n"):
json_data = json.loads(line)
if "response" in json_data and json_data["response"].strip():
return json_data["response"].strip().lower() == "true"
except json.JSONDecodeError as e:
print("JSON Decode Error:", e)
return False

View File

@ -5,7 +5,7 @@ import os
import time
import subprocess
from googlenewsdecoder import gnewsdecoder
from iacorrector import is_security_related # Importa la función desde iacorrector.py
from iacorrector import is_security_related, is_critico, is_favorable # Importa la función desde iacorrector.py
from datetime import datetime
import pytz
@ -86,7 +86,7 @@ def search_news(query):
articles = soup.find_all("item")
news_list = []
for article in articles[:30]: # Limitar a los primeros 30 artículos
for article in articles[:10]: # Limitar a los primeros 30 artículos
try:
title = article.title.get_text(strip=True)
content = article.description.get_text(strip=True) if article.description else "Sin descripción"
@ -95,6 +95,8 @@ def search_news(query):
date = article.pubDate.get_text(strip=True) if article.pubDate else "Fecha no disponible"
date_parsed = datetime.strptime(date, '%a, %d %b %Y %H:%M:%S GMT')
date_parsed = date_parsed.replace(tzinfo=pytz.UTC)
critico = False
favorable = False
# Obtener la URL final del artículo
final_url = get_url_from_google_news(link)
@ -102,6 +104,9 @@ def search_news(query):
# Obtener el autor usando autorsearcher.py
author = get_author_from_script(final_url)
content = get_article_content(final_url)
critico = is_critico(content)
favorable = is_favorable(content)
# Verificar si el artículo es válido usando iacorrector
if is_security_related(content): # Solo si el artículo es válido
@ -111,9 +116,11 @@ def search_news(query):
"autor": author,
"fuente": source_info,
"fecha": date_parsed.isoformat(),
"link": final_url # Guardamos la URL final en lugar de la de Google News
"link": final_url, # Guardamos la URL final en lugar de la de Google News,
"critico": critico,
"favorable":favorable
}
news_list.append(news_item)
insertar_datos(news_item)
except Exception as e:
print(f"Error al procesar un artículo para '{query}': {e}")
@ -130,9 +137,6 @@ def insertar_datos(news_item):
else:
print(f"Error al insertar '{news_item['titulo']}':", response.status_code, response.json())
def search_from_keywords_file():
"""
Lee palabras clave del archivo 'keywords.txt' y realiza búsquedas para cada una.
@ -148,14 +152,9 @@ def search_from_keywords_file():
for keyword in keywords:
print(f"\nBuscando noticias sobre: {keyword}")
news_list = search_news(keyword)
all_news.extend(news_list) # Añadir las noticias encontradas para cada palabra clave
search_news(keyword)
time.sleep(2) # Pausa para evitar bloqueos por demasiadas solicitudes en poco tiempo
# Guardar todas las noticias en un archivo JSON
for news in all_news:
insertar_datos(news)
except FileNotFoundError:
print("No se encontró el archivo 'keywords.txt'.")
except Exception as e:

View File

@ -1,148 +0,0 @@
import requests
from bs4 import BeautifulSoup
import json
import os
import time
import subprocess
from googlenewsdecoder import gnewsdecoder
from iacorrector import is_security_related # Importa la función desde iacorrector.py
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
}
def get_author_from_script(url):
"""
Llama a autorsearcher.py con la URL de la noticia y devuelve el autor encontrado.
"""
try:
result = subprocess.run(["python", "autorsearcher.py", url], capture_output=True, text=True)
author = result.stdout.strip()
return author if author else "Desconocido"
except Exception as e:
print(f"Error al obtener el autor para {url}: {e}")
return "Desconocido"
def get_url_from_google_news(url):
interval_time = 1
try:
decoded_url = gnewsdecoder(url, interval=interval_time)
if decoded_url.get("status"):
return decoded_url["decoded_url"]
else:
return "N/C"
except Exception as e:
print(f"Error occurred: {e}")
def get_article_content(url):
"""
Extrae el texto principal del artículo desde la URL final.
"""
try:
response = requests.get(url, headers=HEADERS)
if response.status_code != 200:
print(f"Error al acceder a {url}: Código {response.status_code}")
return "No se pudo obtener el contenido"
soup = BeautifulSoup(response.text, "html.parser")
# Buscar los elementos más comunes donde se almacena el contenido del artículo
possible_containers = [
soup.find("article"), # Etiqueta <article> (común en blogs y periódicos)
soup.find("div", class_="post-content"), # Clases comunes en WordPress
soup.find("div", class_="entry-content"),
soup.find("div", class_="content"),
soup.find("div", id="article-body")
]
for container in possible_containers:
if container:
paragraphs = container.find_all("p")
article_text = "\n".join(p.get_text(strip=True) for p in paragraphs)
return article_text if article_text else "No se encontró contenido relevante"
return "No se encontró contenido relevante"
except Exception as e:
print(f"Error al extraer contenido de {url}: {e}")
return "Error al extraer contenido"
def search_news(query):
"""
Busca noticias relacionadas con una palabra clave en Google News.
"""
base_url = f"https://news.google.com/rss/search?q={query.replace(' ', '+')}&hl=es&gl=ES&ceid=ES%3Aes"
response = requests.get(base_url, headers=HEADERS)
if response.status_code != 200:
print(f"Error al acceder a la página para la consulta '{query}': {response.status_code}")
return []
soup = BeautifulSoup(response.content, 'xml')
articles = soup.find_all("item")
news_list = []
for article in articles[:30]: # Limitar a los primeros 30 artículos
try:
title = article.title.get_text(strip=True)
content = article.description.get_text(strip=True) if article.description else "Sin descripción"
link = article.link.get_text(strip=True)
source_info = article.source.get_text(strip=True) if article.source else "Desconocido"
date = article.pubDate.get_text(strip=True) if article.pubDate else "Fecha no disponible"
# Obtener la URL final del artículo
final_url = get_url_from_google_news(link)
# Obtener el autor usando autorsearcher.py
author = get_author_from_script(final_url)
content = get_article_content(final_url)
# Verificar si el artículo es válido usando iacorrector
if is_security_related(content): # Solo si el artículo es válido
news_item = {
"titulo": title,
"contenido": content,
"autor": author,
"fuente": source_info,
"fecha": date,
"link": final_url # Guardamos la URL final en lugar de la de Google News
}
news_list.append(news_item)
except Exception as e:
print(f"Error al procesar un artículo para '{query}': {e}")
return news_list
def search_from_keywords_file():
"""
Lee palabras clave del archivo 'keywords.txt' y realiza búsquedas para cada una.
"""
all_news = [] # Lista para almacenar todas las noticias recolectadas
try:
with open("keywords.txt", "r", encoding="utf-8") as file:
keywords = file.readlines()
# Eliminar posibles saltos de línea y espacios extra
keywords = [keyword.strip() for keyword in keywords]
for keyword in keywords:
print(f"\nBuscando noticias sobre: {keyword}")
news_list = search_news(keyword)
all_news.extend(news_list) # Añadir las noticias encontradas para cada palabra clave
time.sleep(2) # Pausa para evitar bloqueos por demasiadas solicitudes en poco tiempo
# Guardar todas las noticias en un archivo JSON
with open('news_data.json', 'w', encoding='utf-8') as json_file:
json.dump(all_news, json_file, ensure_ascii=False, indent=4)
print("\nTodas las noticias han sido guardadas en 'news_data.json'.")
except FileNotFoundError:
print("No se encontró el archivo 'keywords.txt'.")
except Exception as e:
print(f"Error al leer el archivo 'keywords.txt': {e}")
# Ejecutar la búsqueda desde el archivo
search_from_keywords_file()