G / app.py
Scrappy-Doo's picture
Update app.py
d7a0ae5 verified
Raw
History Blame Contribute Delete
6.02 kB
import gradio as gr
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import os
def build_session():
session = requests.Session()
retries = Retry(
total=2,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
)
adapter = HTTPAdapter(max_retries=retries)
session.mount("https://", adapter)
session.mount("http://", adapter)
return session
def extract_images(url):
session = build_session()
try:
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "es-ES,es;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
}
# Timeout separado: (conexión, lectura). Antes era un único valor de 50s,
# lo que dejaba la UI colgada mucho tiempo si el sitio nunca respondía.
response = session.get(url, headers=headers, timeout=(10, 20))
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
base_url = f"{urlparse(url).scheme}://{urlparse(url).netloc}"
images = []
seen = set()
# Buscar imágenes en <img>
for img in soup.find_all("img"):
src = img.get("src") or img.get("data-src") or img.get("data-original")
if not src:
continue
# Convertir URLs relativas a absolutas
img_url = urljoin(base_url, src)
# Filtrar duplicados y URLs inválidas
if img_url in seen or not img_url.startswith(("http://", "https://")):
continue
seen.add(img_url)
# Obtener nombre del archivo
parsed = urlparse(img_url)
filename = os.path.basename(parsed.path) or "imagen.jpg"
if not any(filename.lower().endswith(ext) for ext in [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp"]):
filename += ".jpg"
images.append({
"image": img_url,
"caption": filename
})
# Buscar imágenes de fondo en <div> o elementos con style
for tag in soup.find_all(style=True):
style = tag["style"]
if "url(" in style:
url_start = style.find("url(") + 4
url_end = style.find(")", url_start)
bg_url = style[url_start:url_end].strip('"\'')
bg_url = urljoin(base_url, bg_url)
if bg_url not in seen and bg_url.startswith(("http://", "https://")):
seen.add(bg_url)
images.append({"image": bg_url, "caption": "background.jpg"})
if not images:
return [], "No se encontraron imágenes en esta página."
return [img["image"] for img in images], f"Se encontraron {len(images)} imágenes."
except requests.exceptions.Timeout:
return [], (
"El sitio no respondió a tiempo tras reintentar. Suele pasar cuando "
"el dominio está filtrado a nivel de red/DNS (algunos hosts de archivos "
"terminan en listas de bloqueo por abuso) o el servidor está caído/lento. "
"Prueba el mismo enlace en el navegador desde esta misma red para confirmar."
)
except requests.exceptions.ConnectionError as e:
# requests envuelve un ReadTimeout que agotó los reintentos como
# ConnectionError (no como Timeout) — es un detalle de la librería,
# no un tipo de fallo distinto al de arriba.
if "ReadTimeoutError" in str(e) or "Read timed out" in str(e):
return [], (
"El sitio acepta la conexión pero nunca termina de responder, "
"ni siquiera tras reintentar. Es el patrón de un bloqueo de "
"red/firewall o de un mecanismo anti-bots que corta la respuesta "
"a mitad de camino, no el de un timeout demasiado corto."
)
return [], f"No se pudo conectar con el sitio: {str(e)}"
except requests.exceptions.RequestException as e:
return [], f"Error al acceder a la URL: {str(e)}"
except Exception as e:
return [], f"Error inesperado: {str(e)}"
finally:
session.close()
# Interfaz de Gradio
with gr.Blocks(title="Extractor de Imágenes Web") as demo:
gr.Markdown("""
# 🖼️ Extractor de Imágenes
Pega el enlace de cualquier página web y extrae todas las imágenes disponibles.
""")
with gr.Row():
url_input = gr.Textbox(
label="URL de la página",
placeholder="https://ejemplo.com",
scale=4
)
submit_btn = gr.Button("🔍 Extraer imágenes", scale=1, variant="primary")
status_text = gr.Textbox(label="Estado", interactive=False)
gallery = gr.Gallery(
label="Imágenes encontradas",
show_label=True,
columns=4,
rows=4,
height="auto",
object_fit="contain",
allow_preview=True
)
# Información de descarga
gr.Markdown("""
### 💡 Para descargar:
1. Haz **clic en cualquier imagen** para previsualizarla en tamaño completo.
2. En la vista previa, haz **clic derecho → Guardar imagen como...**.
3. También puedes hacer clic derecho directamente sobre cualquier miniatura.
""")
submit_btn.click(
fn=extract_images,
inputs=url_input,
outputs=[gallery, status_text]
)
# Ejemplo
gr.Examples(
examples=["https://es.wikipedia.org/wiki/Gato"],
inputs=url_input,
label="Ejemplo"
)
if __name__ == "__main__":
demo.launch()