Ollama Vision Web Scraping: Leer una captura de pantalla con un modelo local
Senior Cybersecurity Analyst
TL;DR:
- Esta es la combinación de modelo local más captura de pantalla, y ninguna guía existente en este sitio la cubre por sí sola. Ollama Web Scraping ejecuta un modelo local en texto HTML renderizado, no en una captura de pantalla; una API de visión en la nube lee una captura de pantalla pero cobra por imagen y necesita una clave. Esta guía ejecuta un modelo local, capaz de visión, contra una captura de pantalla real de un navegador — sin clave en la nube, sin costo por token y sin HTML en absoluto.
- Cada ejecución aquí utilizó Ollama solo en CPU, y
ollama pslo confirma. No hay descarga de GPU disponible para Ollama en este entorno, y el tiempo varía ampliamente entre llamadas — desde segundos de un solo dígito hasta un par de minutos — dependiendo de si el modelo aún está residente en memoria. - El campo de imagen de Ollama no es el formato de la nube. El campo
imagesen/api/generatede Ollama toma una lista de cadenas base64 crudas sin prefijodata:image/png;base64,— lo contrario de la convención compatible con OpenAI que utilizan la mayoría de las APIs de visión en la nube. - Un modelo de visión de menos de 2B es poco confiable en ambos extremos de la tarea, de diferentes maneras. Al pedirle que describa la página en una oración, mencionó un detalle estructural que no coincide con la página real. Al pedir un registro JSON estructurado — la tarea exacta que el par de visión en la nube maneja con facilidad — repitió el texto de marcador de posición del aviso, y luego se desvió hacia una salida que no era JSON en absoluto.
- La captura de pantalla aún proviene de un navegador renderizado real. El Scrapeless Scraping Browser captura la página a través de CDP de la misma manera que lo haría una tubería de visión en la nube; solo cambian los lugares donde se leen los píxeles después.
- Gratis para comenzar en el lado de la obtención. Crea tu clave de API Scrapeless en app.scrapeless.com.
Por qué emparejar un modelo local con una captura de pantalla
Dos cosas sobre "raspado web de IA" suelen ser ciertas por separado, y rara vez ciertas juntas. Un modelo local, ejecutado a través de Ollama, no cuesta nada por llamada y no envía nada a un tercero — pero la guía de modelo local en este sitio hasta ahora lee texto HTML renderizado, no píxeles. Un modelo de visión lee una captura de pantalla en lugar del DOM — pero la guía del modelo de visión en este sitio hasta ahora llama a una API en la nube que cobra por imagen y necesita una clave de proveedor de nube. Esta publicación es la combinación que ninguna de las dos cubre: un modelo capaz de visión que se ejecuta completamente en la máquina frente a ti, leyendo una captura de pantalla real de un navegador, sin factura de inferencia en la nube en ninguna parte de la tubería.
Las dos guías existentes son útiles para lo que cada una hace bien. Ollama Web Scraping es la opción correcta cuando el valor que necesitas ya está en texto en el DOM renderizado — un modelo local es rápido y preciso en un fragmento de HTML recortado. La guía de raspado web de GPT Vision de este sitio es la opción correcta cuando el valor es genuinamente visual y deseas un modelo capaz y de pago para leerlo. Esta guía es para un tercer caso: el valor es visual, pero deseas cero dependencia de la nube y estás dispuesto a intercambiar capacidad por eso. Ese intercambio es real, y esta guía lo reporta honestamente en lugar de solo mostrar la parte que funciona.
Prerrequisitos
- Ollama instalado y en funcionamiento, con un modelo capaz de visión descargado:
ollama pull moondream. - Python 3.9 o posterior con
playwrightyrequests. - Una clave de API Scrapeless del panel de control, exportada como
SCRAPELESS_API_KEY. Consigue una gratis en app.scrapeless.com. - No se requiere GPU. Cada ejecución en esta guía utilizó Ollama solo en CPU.
Instalar
bash
pip install playwright requests
ollama pull moondream
playwright se conecta a un navegador remoto a través de CDP en esta guía, por lo que no se necesita una descarga local de Chromium. moondream es un modelo compacto capaz de visión — lo suficientemente pequeño como para descargar y ejecutar sin una GPU discreta, que es el objetivo de esta guía.
Configurar
bash
export SCRAPELESS_API_KEY="your_scrapeless_api_key"
Esa es la única clave que necesita esta tubería. La API local de Ollama no requiere ninguna credencial.
Capturar la captura de pantalla con el Scraping Browser
La captura de pantalla aún debe ser fiel, por lo que proviene de un navegador real. Conéctate al Scrapeless Scraping Browser a través del Protocolo de DevTools de Chrome, abre la página y captura la vista renderizada — el mismo paso de captura que utiliza el par de visión en la nube, a través de el Protocolo de DevTools de Chrome:
python
import os
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
def browser_url():
return "wss://browser.scrapeless.com/api/v2/browser?" + urlencode(
{"token": os.environ["SCRAPELESS_API_KEY"], "sessionTTL": 180, "proxyCountry": "US"})
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(browser_url())
page = browser.new_page(viewport={"width": 1280, "height": 900})
page.goto("https://quotes.toscrape.com/", wait_until="networkidle")
png = page.screenshot()
browser.close()
print("screenshot bytes:", len(png))
print("PNG signature ok:", png[:8] == b"\x89PNG\r\n\x1a\n")
text
screenshot bytes: 55462
PNG signature ok: True
Obtén tu clave API en el plan gratuito: app.scrapeless.com
Lee la Captura de Pantalla Con un Modelo de Visión Local
Codifica en base64 el PNG y publícalo directamente en el endpoint local /api/generate de Ollama. Aquí es donde realmente se encuentran los dos ejes: la imagen provino de un navegador renderizado en la nube real, pero el modelo que la lee nunca sale de esta máquina. La forma de la solicitud está documentada en la referencia API de Ollama — nota que el campo images toma una cadena base64 simple, no una URL data:image/png;base64,...:
python
import base64, os, time
from urllib.parse import urlencode
import requests
from playwright.sync_api import sync_playwright
def browser_url():
return "wss://browser.scrapeless.com/api/v2/browser?" + urlencode(
{"token": os.environ["SCRAPELESS_API_KEY"], "sessionTTL": 180, "proxyCountry": "US"})
t0 = time.time()
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(browser_url())
page = browser.new_page(viewport={"width": 1280, "height": 900})
page.goto("https://quotes.toscrape.com/", wait_until="networkidle")
png = page.screenshot()
browser.close()
capture_s = round(time.time() - t0, 1)
img_b64 = base64.b64encode(png).decode()
t1 = time.time()
resp = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "moondream",
"prompt": "Describe what this webpage shows in one sentence.",
"images": [img_b64],
"stream": False,
"options": {"temperature": 0, "num_predict": 60},
},
timeout=300,
)
resp.raise_for_status()
data = resp.json()
inference_s = round(time.time() - t1, 1)
print("capture seconds:", capture_s)
print("vision inference seconds:", inference_s)
print("description:", data["response"].strip())
text
capture seconds: 11.5
vision inference seconds: 54.7
description: A webpage titled "Quotes to Scrape" displays a list of quotes and their corresponding tags, organized into five sections.
El título, la presencia de comillas y las etiquetas debajo de cada uno son elementos reales de la página renderizada. La estructura específica que agrega, "organizada en cinco secciones", no es verificable contra la página real: quotes.toscrape.com renderiza diez bloques de citas en la página principal, sin agrupación en cinco de nada. El modelo capta correctamente el tema general de la página, un sitio de citas con etiquetas, y luego declara un detalle estructural específico que no coincide con lo que realmente hay.
ollama ps durante esta clase de llamada informa 100% CPU: no hay descarga de GPU disponible para Ollama en este entorno. El tiempo de una llamada como esta también varía con el estado del modelo. El endpoint /api/generate de Ollama mantiene un modelo residente en memoria durante keep_alive después de cada solicitud, 5 minutos por defecto, documentado en la referencia API de Ollama arriba; una llamada mientras el modelo aún es residente omite volver a cargarlo desde el disco, y una llamada fría, sin nada residente, paga ese tiempo de carga antes de que vuelva el primer token.
Dónde la Extracción Estructurada Se Descompone
Una descripción funcional no es lo mismo que una extracción confiable. Pregunta al mismo modelo por la tarea exacta que el hermano de visión en la nube maneja en una sola pasada limpia — un arreglo JSON de registros de citas — y el resultado honesto también pertenece a esta guía:
python
import base64, json, os, time
from urllib.parse import urlencode
import requests
from playwright.sync_api import sync_playwright
def browser_url():
return "wss://browser.scrapeless.com/api/v2/browser?" + urlencode(
{"token": os.environ["SCRAPELESS_API_KEY"], "sessionTTL": 180, "proxyCountry": "US"})
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(browser_url())
page = browser.new_page(viewport={"width": 1280, "height": 900})
page.goto("https://quotes.toscrape.com/", wait_until="networkidle")
png = page.screenshot()
browser.close()
img_b64 = base64.b64encode(png).decode()
t1 = time.time()
resp = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "moondream",
"prompt": 'From this screenshot of a quotes page, return JSON '
'{"quotes":[{"author":"...", "text":"..."}]} for the first 3 quotes '
'you can see. Return ONLY JSON.',
"images": [img_b64],
"stream": False,
"format": "json",
"options": {"temperature": 0, "num_predict": 150},
},
timeout=600,
)
resp.raise_for_status()
data = resp.json()
inference_s = round(time.time() - t1, 1)
print("vision inference seconds:", inference_s)
print("raw response:", data["response"][:200])
try:
parsed = json.loads(data["response"])
print("parsed quotes:", len(parsed.get("quotes", [])))
except json.JSONDecodeError as e:
print("JSON parse failed:", e)
text
vision inference seconds: 79.6
raw response: {"quotes":[{"author":"...","text":"..."},{"author":"","text":"","}}] } [0.12, 0.13, 1.0, 0.87] ) ; return [0.12, 0.13, 1.0, 0.87] ; return [0.12, 0.13, 1.0, 0.87] ; return [0.12, 0.13, 1.0, 0.87] ; re
JSON parse failed: Unterminated string starting at: line 1 column 65 (char 64)
El primer objeto repite la propia sintaxis de marcador de posición "..." del aviso textualmente, como si los puntos suspensivos en las instrucciones fueran un valor a copiar en lugar de un espacio a llenar. El segundo objeto nunca se cierra correctamente, y la respuesta luego deriva en arreglos repetidos de cuatro números y declaraciones return extraviadas que no tienen nada que ver con citas, un autor o JSON en absoluto — una salida que se lee como sobras de una tarea diferente que como un intento roto en esta. A través de cada ejecución en vivo detrás de esta guía, el patrón subyacente se mantuvo incluso cuando la basura exacta cambió de forma: nunca volvió texto de cita real o nombre de autor, y la respuesta nunca se cerró como un JSON válido. Ese es un resultado genuino, no un registro truncado: moondream es un modelo de aproximadamente 1 mil millones de parámetros, y el comportamiento de seguimiento de esquema que permite que un gran modelo de visión en la nube devuelva tres registros limpios y correctamente atribuidos en unos pocos segundos no es algo que un modelo tan pequeño reproduzca. Aumentar el límite de longitud de salida no arregla el patrón; principalmente le da al modelo más espacio para desviarse.
La guía de raspado web de GPT Vision de este sitio muestra el otro extremo de ese intercambio. Un modelo multimodal alojado, GPT-4o-mini, ejecutado a través de la API de OpenAI, convierte una captura de pantalla de Playwright en campos de producto y precio estructurados que realmente completa. Esa comparación, un modelo de pago mucho más capaz contra uno local de 1 mil millones de parámetros gratuito, es parte del objetivo de ejecutarlo localmente.
Visión Local vs. Visión en la Nube vs. Texto Local
Tres guías en este sitio ahora cubren tres diferentes compensaciones para obtener datos estructurados de una página sin selectores escritos a mano:
| Ubicación del modelo | Lee | Costo por llamada | Precisión del JSON estructurado | |
|---|---|---|---|---|
| Raspado Web de Ollama | Local | Texto HTML renderizado | Gratis | Confiable en HTML recortado |
| Guía de raspado web de GPT Vision | Nube | Captura de pantalla | Por imagen, se requiere clave en la nube | Un modelo de pago mucho más grande — no evaluado en esta guía |
| Esta guía | Local | Captura de pantalla | Gratis | No confiable a este tamaño de modelo |
| Ninguno de los tres es universalmente correcto. Si el valor es texto en el DOM, analiza el DOM: un modelo de texto local o un selector simple es más rápido y barato que cualquier pipeline de visión. Si el valor es genuinamente visual y la precisión importa, un modelo de visión en la nube justifica su costo por imagen. Un modelo de visión local tan pequeño se adapta a un caso más estrecho que cualquiera de ellos: lecturas exploratorias o de bajo riesgo donde un humano o un control posterior pueden detectar un detalle incorrecto ocasional, no un pipeline que confía en la salida sin supervisión. Según la evidencia anterior, esto es cierto tanto para una descripción suelta de una oración como para un esquema estricto de múltiples campos: el modo de fallo simplemente se ve diferente en cada uno. |
Conclusión
Combinar un modelo local con una captura de pantalla cierra una brecha real entre las dos guías existentes de este sitio. Nada en el pipeline toca un modelo en la nube: el Scrapeless Scraping Browser aún renderiza y captura la página de la misma manera que lo haría un pipeline de visión en la nube, y Ollama lee el PNG resultante completamente en esta máquina. Para lo que esa combinación es realmente buena, según la evidencia presentada aquí, es más estrecha que un modelo de visión en la nube en ambas direcciones: una descripción de una oración agregó un detalle estructural que la página no tiene, y un esquema JSON estricto repitió su propio texto de marcador antes de desviarse hacia una salida que no era JSON en absoluto. Usa un modelo de este tamaño para lecturas exploratorias que planeas verificar, no para un pipeline que confía en el resultado sin supervisión, y recurre a la guía de visión en la nube cuando la extracción necesita ser exacta.
Crea una cuenta gratuita en Scrapeless para obtener una clave API, revisa la página del producto Scraping Browser para ver lo que admite la sesión CDP, y revisa los precios de Scrapeless antes de ejecutar el navegador a gran escala.
Únete a nuestra comunidad para comparar notas con otros desarrolladores que construyen pipelines de extracción locales: Discord · Telegram.
FAQ
P: ¿Cuál es la diferencia real entre esta guía y la guía de visión GPT en la nube?
Dónde se ejecuta el modelo y cuánto cuesta. La guía de scraping web GPT Vision de este sitio envía la captura de pantalla a un modelo multimodal alojado a través de una API y cobra por imagen; esta guía envía el mismo tipo de captura de pantalla a un modelo que se ejecuta en localhost a través de Ollama, sin clave API y sin costo por llamada. Ambos leen píxeles, no HTML.
P: ¿Cuál es la diferencia entre esta guía y la guía de extracción de texto local de Ollama?
Lo que lee el modelo. Ollama Web Scraping obtiene HTML renderizado y le entrega al modelo local el texto para analizar. Esta guía nunca envía HTML al modelo en absoluto: le entrega al modelo local una captura de pantalla y le pide que lea la imagen.
P: ¿Necesito una GPU para ejecutar un modelo de visión local?
No, pero espera que el tiempo de espera varíe mucho sin ella. Cada ejecución en esta guía utilizó moondream solo en CPU, confirmado por ollama ps reportando 100% CPU durante la inferencia, y las llamadas detrás de esta guía variaron de segundos de dos dígitos a un par de minutos para el mismo tipo de solicitud. Ollama mantiene un modelo residente por keep_alive después de cada solicitud (5 minutos por defecto), por lo que una llamada mientras todavía está cargado omite el tiempo de carga desde el disco que una llamada en frío paga; eso solo explica la mayor parte de la variación. Una GPU a la que Ollama pueda acceder reduciría sustancialmente cada caso.
P: ¿Por qué el campo images en la API de Ollama se ve diferente de una solicitud de visión en la nube?
Porque utiliza una convención diferente. El /api/generate de Ollama toma images como una lista de cadenas base64 sin prefijo. La mayoría de las APIs de visión en la nube compatibles con OpenAI, incluida la de la guía de scraping web GPT Vision de este sitio, esperan una URL completa de data:image/png;base64,... dentro de una parte de contenido image_url. Portar código entre los dos formatos sin ajustar esto es lo primero que falla.
P: ¿Por qué la extracción estructurada devolvió campos vacíos en lugar de un error?
La solicitud en sí tuvo éxito: Ollama devolvió un 200 con un campo response, por lo que raise_for_status() nunca se activa. El modelo llenó la forma JSON que se le indicó producir, pero no pobló los valores, y siguió repitiendo esa forma vacía hasta que el límite de longitud de salida lo cortó a mitad de cadena. Eso es una falla de capacidad del modelo, no una falla de HTTP, y es exactamente por eso que el código anterior verifica json.loads() por separado en lugar de suponer que un 200 significa datos válidos y completos.
P: ¿Puede un modelo de visión local más grande solucionar el problema de extracción estructurada?
Probablemente, a un costo. Un modelo de visión local de clase 7B, como llava:7b, tiene más capacidad para contener un esquema JSON y llenarlo correctamente, pero también necesita significativamente más RAM o VRAM y es más lento por token en el mismo hardware. El intercambio en el que se basa esta guía - genuinamente gratuito, genuinamente local - se vuelve más difícil de mantener a medida que el modelo crece; en algún momento, el costo por imagen de un modelo de visión en la nube se vuelve más económico que el hardware que necesita un gran modelo local para funcionar bien.
P: ¿Debería usar esto para una canalización de scraping en producción?
No de manera no supervisada, a este tamaño de modelo. Según la evidencia aquí, ni una descripción de una sola oración ni un esquema estructurado volvieron completamente confiables: la descripción añadió un detalle estructural que la página no tiene, y el esquema repitió su propio texto de marcador de posición antes de desviarse a una salida no JSON. Dirija la extracción de esquema estricto a la guía de visión en la nube o al análisis del DOM con Ollama Web Scraping en su lugar, y mantenga un modelo de visión local como este para lecturas exploratorias sin costo donde un humano aún verifica el resultado, o cambie a un modelo local más grande si tiene que seguir siendo local.
P: ¿Es legal leer una captura de pantalla con un modelo local?
Ejecutar el modelo localmente no cambia las reglas de recolección para la página en sí. Capture solo páginas públicas, respete los términos del sitio y las directrices de robots estandarizadas por el Protocolo de Exclusión de Robots, y mantenga el volumen de solicitudes limitado sin importar dónde se ejecute el modelo que lee el resultado.
En Scrapeless, solo accedemos a datos disponibles públicamente y cumplimos estrictamente con las leyes, regulaciones y políticas de privacidad del sitio web aplicables. El contenido de este blog es sólo para fines de demostración y no implica ninguna actividad ilegal o infractora. No ofrecemos garantías y renunciamos a toda responsabilidad por el uso de la información de este blog o enlaces de terceros. Antes de realizar cualquier actividad de scraping, consulte a su asesor legal y revise los términos de servicio del sitio web de destino u obtenga los permisos necesarios.



