Volver al blog

Playwright + Navegador de Scraping Sin Residuos: Capturar y Reproducir una Sesión HAR Completa

James Thompson
James Thompson

Scraping and Proxy Management Expert

30-Jul-2026

TL;DR:

  • Un archivo HAR es un archivo HTTP estructurado, no una captura de pantalla o un video. Su array log.entries almacena un objeto por cada solicitud HTTP capturada, incluyendo metadatos de solicitud y respuesta y, cuando está disponible, el contenido grabado.
  • Playwright graba archivos HAR a nivel de contexto del navegador. Establezca record_har_path al crear el contexto y llame a context.close() para volcar el archivo en el disco.
  • Scrapeless Scraping Browser proporciona la sesión remota del navegador. Playwright se conecta a él a través de CDP, carga la página, activa solicitudes dinámicas y guarda el tráfico resultante localmente.
  • El análisis HAR no requiere un navegador. Una vez que el archivo existe, el módulo estándar json de Python puede inspeccionar sus URL, métodos, estados, tipos MIME, encabezados y cuerpos de respuesta incrustados.
  • Una solicitud capturada puede ser reemitida sin Playwright. El ejemplo reconstruye una solicitud JSON pública con urllib después de eliminar los pseudo-encabezados de HTTP/2 y renegociar la codificación de contenido.
  • La reproducción de HAR tiene límites. Cookies expiradas, tokens CSRF, credenciales de portador, tramas de WebSocket y datos cambiantes del servidor pueden evitar que una solicitud posterior reproduzca la respuesta original.
  • Gratis para empezar. Las nuevas cuentas de Scrapeless incluyen tiempo de ejecución de Scraping Browser gratuito — regístrate en app.scrapeless.com.

Introducción: Captura Primero, Decide Qué Importa Después

Un archivo HAR es un archivo JSON de transacciones HTTP observadas durante una sesión de navegador. No es una captura de pantalla de una página renderizada, un snapshot del DOM, o un video de la sesión.

Cada solicitud capturada aparece como un objeto dentro de:

text Copy
log.entries

Una entrada puede contener el método de solicitud, URL, encabezados, parámetros de consulta, datos enviados, estado de respuesta, encabezados de respuesta, información de tiempo y contenido de respuesta grabado. El contenido binario puede ser representado con una codificación como Base64, mientras que los cuerpos textuales pueden aparecer directamente en la entrada.

Esta guía conecta Playwright al Scrapeless Scraping Browser a través del Protocolo de Herramientas de Desarrollo de Chrome, graba una carga completa de la página y una secuencia de desplazamiento con record_har_path, y cierra el navegador.

La segunda mitad del flujo de trabajo es libre de navegador:

  1. Inspeccionar la estructura HAR con json.
  2. Encontrar una solicitud API capturada.
  3. Reconstruir sus encabezados reutilizables.
  4. Reemitirla con urllib.
  5. Comparar la nueva respuesta JSON con el cuerpo almacenado en el archivo.

Cada resultado mostrado proviene de la sesión de objetivo capturada.

Lo Que Puedes Hacer Con Un Archivo HAR

La captura HAR es útil cuando el endpoint importante no se conoce antes de cargar una página.

  • Auditar una carga completa de la página. Revisar HTML, hojas de estilo, scripts, fuentes, imágenes y solicitudes API de una sesión.
  • Descubrir endpoints JSON internos. Buscar en el archivo después de la captura en lugar de predecir qué solicitud será importante.
  • Depurar el comportamiento fallido de la página. Inspeccionar URL de solicitud, estados de respuesta, encabezados, tipos MIME y cuerpos después de que el navegador se haya cerrado.
  • Preservar evidencia de red. Almacenar un artefacto JSON portátil que puede ser analizado más tarde o transferido a otra máquina.
  • Comparar el comportamiento de carga de páginas. Capturar sesiones separadas y comparar sus conjuntos de solicitudes, estados o contenido de respuesta.
  • Extraer datos de respuesta capturados. Leer cuerpos JSON o textuales incrustados sin cargar la página nuevamente.
  • Reconstruir solicitudes elegibles. Reemitir solicitudes HTTP públicas o todavía autenticadas con un cliente HTTP estándar.
  • Construir pruebas de navegador deterministas. Usar la función route_from_har() separada de Playwright para servir respuestas grabadas de vuelta a un contexto de navegador en vivo.

Un archivo HAR es más valioso cuando una captura amplia es más útil que adjuntar un oyente a un endpoint ya conocido.

La Captura HAR, Grabación de Video y Enrutamiento HAR Son Diferentes

Tres características en esta área utilizan un lenguaje similar pero resuelven problemas diferentes.

Característica Lo que registra o hace ¿Se requiere navegador después?
Playwright record_har_path Archiva datos de solicitudes y respuestas HTTP No, para inspección fuera de línea
Grabación de sesión Scrapeless Crea una reproducción visual de la sesión renderizada No, para reproducción en el panel
Playwright route_from_har() Sirve respuestas grabadas a solicitudes hechas por un contexto de navegador
El ejemplo de urllib en esta guía Reemite una solicitud capturada contra el servidor en vivo No

Intercepción en Vivo

La intercepción en vivo observa solicitudes a medida que ocurren. Funciona bien cuando el endpoint de destino o el patrón de respuesta ya son conocidos.

Una vez que la sesión termina, cualquier cosa que no haya sido capturada por el oyente se pierde.

Captura HAR

La captura HAR registra el tráfico HTTP del contexto de forma amplia. El endpoint que importa puede ser seleccionado después de que el navegador se cierre.

Esto hace que la captura HAR sea una mejor opción para:

  • Depuración posterior a la sesión
  • Inventarios de red
  • Descubrimiento de API
  • Auditoría de todos los recursos cargados por una página
  • Preservar los cuerpos de respuesta para inspección offline

Grabación de Sesiones Sin Residuos

El Navegador de Scraping Sin Residuos soporta una capacidad de grabación de sesiones nativa separada. Esto produce un registro visual reproducible de la sesión del navegador renderizado.

No reemplaza un archivo HAR. Un video muestra lo que apareció en la pantalla; un HAR expone transacciones HTTP estructuradas.

Playwright route_from_har()

El route_from_har() de Playwright envía respuestas HAR guardadas de vuelta a las solicitudes realizadas por un contexto de navegador en vivo. Se utiliza comúnmente para simular el comportamiento del backend en pruebas de navegador.

El ejemplo de reproducción en esta guía hace algo diferente: lee una solicitud del archivo y envía una nueva solicitud HTTP en vivo con urllib.

El Navegador de Scraping Sin Residuos proporciona el entorno de navegador remoto que Playwright controla a través de CDP.

La sesión del navegador se ejecuta en una infraestructura en la nube y soporta la selección de proxy geográfico a través de los parámetros de conexión. Playwright todavía utiliza sus API normales de navegador, contexto, página y HAR.

Para este flujo de trabajo, la división de responsabilidades es simple:

Componente Responsabilidad
Navegador de Scraping Sin Residuos Ejecuta la sesión remota de Chromium y proporciona el punto final de CDP
Playwright Crea el contexto, controla la página y graba el HAR
Sistema de archivos local Almacena session.har
Biblioteca estándar de Python Inspecciona el archivo y vuelve a emitir la solicitud seleccionada

La grabación HAR en sí misma es una característica de Playwright. Conectar Playwright a Scrapeless mueve la etapa de renderizado en vivo a un navegador remoto administrado mientras deja el archivo resultante en la máquina que ejecuta el script de Python.

La conexión utiliza el mismo Dominio de red del Protocolo de herramientas de desarrollo de Chrome que las herramientas del navegador utilizan para observar la actividad de red.

El inicio rápido del Navegador de Scraping cubre el modelo de conexión más amplio de Playwright y Puppeteer.

Requisitos Previos

Necesitas:

  • Python 3.9 o más reciente
  • Playwright 1.59.0 para la ejecución reproducida
  • Una cuenta y clave API de Scrapeless
  • Acceso de escritura al directorio donde se creará session.har
  • Acceso a la red a la página objetivo pública

Playwright 1.59.0 declara Python 3.9 o más reciente. Fijar esa versión hace que la instalación coincida con los resultados capturados en esta guía.

No se requiere ninguna instalación local de Chrome para este flujo de trabajo. connect_over_cdp() se conecta a un navegador que ya se está ejecutando en la infraestructura de Scrapeless.

Los scripts de inspección y reemisión de solicitudes utilizan solo la biblioteca estándar de Python. No importan Playwright ni abren una conexión de navegador.

Paso 1 — Instalar Playwright

Instala la versión utilizada para la ejecución registrada:

bash Copy
pip install "playwright==1.59.0"

Debido a que el script se conecta a un navegador remoto existente a través de CDP, no lanza un ejecutable de navegador instalado localmente.

Establece la clave API de Scrapeless en la terminal:

bash Copy
export SCRAPELESS_API_KEY="tu_clave_api_scrapeless"

El código lee la clave del entorno en lugar de almacenarla en el control de versiones.

Paso 2 — Construir la URL de Scrapeless CDP

La URL de conexión del Navegador de Scraping lleva la clave API, la duración de la sesión y la ubicación del proxy como parámetros de consulta:

python Copy
import os
from urllib.parse import urlencode

API_KEY = os.environ["SCRAPELESS_API_KEY"]


def scraping_browser_url(proxy_country="US", session_ttl=180):
    params = urlencode({
        "token": API_KEY,
        "sessionTTL": session_ttl,
        "proxyCountry": proxy_country,
    })
    return f"wss://browser.scrapeless.com/api/v2/browser?{params}"

La función genera una URL en esta forma:

text Copy
wss://browser.scrapeless.com/api/v2/browser?token=...&sessionTTL=180&proxyCountry=US

Los tres valores configurados son:

  • token: la clave API de Scrapeless
  • sessionTTL: la duración máxima de la sesión
  • proxyCountry: el país del proxy solicitado

Mantener la construcción de URL en una función también facilita aplicar los mismos ajustes de conexión en múltiples scripts de captura.

La configuración record_har_path de Playwright pertenece a browser.new_context(), no a connect_over_cdp().

La conexión del navegador proporciona acceso a Chromium. El contexto define qué se grabará.

python Copy
import os
from urllib.parse import urlencode

from playwright.sync_api import sync_playwright


API_KEY = os.environ["SCRAPELESS_API_KEY"]
HAR_PATH = "session.har"


def scraping_browser_url(proxy_country="US", session_ttl=180):
    params = urlencode({
        "token": API_KEY,
        "sessionTTL": session_ttl,
        "proxyCountry": proxy_country,
    })
    return f"wss://browser.scrapeless.com/api/v2/browser?{params}"

con sync_playwright() como p:
browser = p.chromium.connect_over_cdp(scraping_browser_url())

Copy
context = browser.new_context(
    record_har_path=HAR_PATH,
    record_har_content="embed",
)

page = context.new_page()
page.goto(
    "https://quotes.toscrape.com/scroll",
    wait_until="networkidle",
)

for _ in range(3):
    page.evaluate(
        "window.scrollTo(0, document.body.scrollHeight)"
    )
    page.wait_for_timeout(800)

print(
    "elementos de cita visibles después de desplazarse:",
    page.locator(".quote").count(),
)

context.close()
browser.close()

print(
"HAR escrito:",
HAR_PATH,
"-",
os.path.getsize(HAR_PATH),
"bytes",
)

La ejecución en vivo imprimió:

text Copy
elementos de cita visibles después de desplazarse: 40
HAR escrito: session.har - 333269 bytes

El objetivo inicialmente renderizó diez citas. Cada desplazamiento cerca de la parte inferior desencadenó otra solicitud /api/quotes?page=N, llevando el total visible a 40 citas a través de cuatro páginas de API.

El script no necesitó predecir qué solicitud importaría más tarde. El HAR también capturó el documento, hojas de estilo, JavaScript, fuente y llamadas JSON.

Obtén tu clave API en el plan gratuito: app.scrapeless.com

Por qué se requiere context.close()

El HAR se finaliza cuando se cierra el contexto del navegador.

Playwright documenta record_har_path como una configuración del contexto del navegador y requiere browser_context.close() para que el HAR se guarde. Cerrar solo la conexión del navegador puede dejar artefactos del contexto sin un almacenamiento adecuado.

El orden de apagado correcto es:

python Copy
context.close()
browser.close()

La llamada a context.close() es, por lo tanto, parte del procedimiento de captura, no una limpieza opcional.

record_har_content="embed" almacena el contenido de respuesta grabado dentro del HAR en lugar de en archivos compañeros separados. Esto hace que session.har sea autónomo para la posterior inspección de JSON y comparación de cuerpos.

El borrador del formato HAR histórico define el objeto log de nivel superior y su matriz entries requerida. Cada entrada representa una solicitud HTTP exportada.

Después de que se cierra el contexto, session.har es un documento JSON local.

El siguiente script utiliza solo json, collections y pathlib:

python Copy
import json
from collections import Counter
from pathlib import Path


har = json.loads(Path("session.har").read_text())
entries = har["log"]["entries"]

print("total de entradas:", len(entries))

by_type = Counter(
    entry["response"]["content"]["mimeType"].split(";")[0]
    for entry in entries
)

for mime_type, count in by_type.most_common():
    print(f"  {mime_type}: {count}")

print()

for entry in entries:
    request = entry["request"]
    response = entry["response"]

    print(
        f"{request['method']:4s} "
        f"{response['status']:3d}  "
        f"{request['url']}"
    )

El archivo capturado contenía:

text Copy
total de entradas: 10
  application/json: 4
  text/css: 3
  text/html: 1
  application/javascript: 1
  font/woff2: 1

GET  200  https://quotes.toscrape.com/scroll
GET  200  https://quotes.toscrape.com/static/bootstrap.min.css
GET  200  https://quotes.toscrape.com/static/main.css
GET  200  https://quotes.toscrape.com/static/jquery.js
GET  200  https://fonts.googleapis.com/css?family=Raleway:400,700
GET  200  https://fonts.gstatic.com/s/raleway/v37/1Ptug8zYS_SKggPNyC0ITw.woff2
GET  200  https://quotes.toscrape.com/api/quotes?page=1
GET  200  https://quotes.toscrape.com/api/quotes?page=2
GET  200  https://quotes.toscrape.com/api/quotes?page=3
GET  200  https://quotes.toscrape.com/api/quotes?page=4

Las diez entradas cubren cinco tipos MIME:

  • Un documento HTML
  • Tres respuestas CSS
  • Una respuesta JavaScript
  • Una fuente web
  • Cuatro respuestas JSON

Un oyente en vivo filtrado a /api/quotes habría observado las cuatro solicitudes JSON pero ignorado los otros seis recursos. El HAR preservó los diez para su posterior inspección.

Entendiendo la estructura de entradas del HAR

Cada elemento en har["log"]["entries"] contiene objetos de solicitud y respuesta anidados.

Una entrada simplificada tiene esta forma:

json Copy
{
  "request": {
    "method": "GET",
    "url": "https://example.com/api/data",
    "headers": []
  },
  "response": {
    "status": 200,
    "headers": [],
    "content": {
      "mimeType": "application/json",
      "text": "{}"
    }
  }
}

Los campos de solicitud útiles incluyen:

  • method
  • url
  • headers
  • queryString
  • postData

Los campos de respuesta útiles incluyen:

  • status
  • statusText
  • headers
  • content
  • redirectURL
    El contenido HAR no está garantizado que se almacene como texto legiblemente directo en cada entrada. Dependiendo del recurso y del grabador, content.text puede estar ausente, ser texto decodificado o una representación codificada cuyo campo encoding identifica el formato.

Paso 5 — Manejar los Pseudo-Encabezados de HTTP/2

Los encabezados de solicitud para la llamada API capturada page=1 incluyeron nombres como:

text Copy
:authority
:method
:path
:scheme

Estos son campos de pseudo-encabezado HTTP/2.

Transportan información de control que aparecería en una línea de solicitud HTTP/1.1 o objetivo:

  • :method identifica el método de solicitud.
  • :scheme identifica el esquema de URI.
  • :authority identifica la autoridad objetivo.
  • :path identifica la ruta y la consulta.

Los pseudo-encabezados no son campos de encabezado HTTP ordinarios. Un cliente orientado a HTTP/1.1 como urllib no puede aceptar un nombre de encabezado con prefijo de dos puntos.

Un script de reproducción debe traducir su significado en la URL y el método, y luego omitirlos del mapeo de encabezados ordinarios.

Paso 6 — Reemitir Una Solicitud Capturada Con urllib

La solicitud seleccionada /api/quotes?page=1 es ahora un diccionario dentro de un archivo JSON local.

El script a continuación:

  1. Encuentra la entrada capturada.
  2. Lee su método, URL y encabezados.
  3. Elimina los pseudo-encabezados HTTP/2.
  4. Elimina accept-encoding.
  5. Crea una nueva urllib.request.Request.
  6. Analiza los cuerpos de respuesta en vivo y capturados.
  7. Compara los dos objetos de Python.
python Copy
import json
import urllib.error
import urllib.request
from pathlib import Path


har = json.loads(Path("session.har").read_text())
entries = har["log"]["entries"]

target = next(
    entry
    for entry in entries
    if entry["request"]["url"].endswith("page=1")
)

captured_request = target["request"]

# Los pseudo-encabezados HTTP/2 describen el encuadre del protocolo y no pueden ser
# pasados como encabezados ordinarios de estilo HTTP/1.1.
#
# accept-encoding también se omite para que urllib pueda negociar una
# codificación que el script pueda decodificar directamente.
skip_headers = {"accept-encoding"}

headers = {
    header["name"]: header["value"]
    for header in captured_request["headers"]
    if not header["name"].startswith(":")
    and header["name"].lower() not in skip_headers
}

print("cuenta de encabezados reproducidos:", len(headers))

request = urllib.request.Request(
    captured_request["url"],
    headers=headers,
    method=captured_request["method"],
)

with urllib.request.urlopen(request, timeout=10) as response:
    if response.status != 200:
        raise urllib.error.HTTPError(
            captured_request["url"],
            response.status,
            "estado inesperado",
            response.headers,
            None,
        )

    live_data = json.loads(response.read())

captured_content = target["response"]["content"]
captured_data = json.loads(captured_content["text"])

print("estado:", response.status, "-- sin proceso de navegador en ejecución")
print(
    "primer autor de la cita:",
    live_data["quotes"][0]["author"]["name"],
)
print(
    "coincide con el cuerpo de respuesta que el HAR ya capturó:",
    live_data == captured_data,
)

La reproducción sin navegador imprimiò:

text Copy
cuenta de encabezados reproducidos: 12
estado: 200 -- sin proceso de navegador en ejecución
primer autor de la cita: Albert Einstein
coincide con el cuerpo de respuesta que el HAR ya capturó: True

El mapeo filtrado contenía 12 encabezados ordinarios. Los pseudo-encabezados HTTP/2 y accept-encoding fueron excluidos.

accept-encoding es un campo de negociación de contenido HTTP. El cliente que reproduce puede anunciar las codificaciones de contenido que admite en lugar de copiar ciegamente la negociación Brotli del navegador.

La respuesta objetivo coincidió con el cuerpo JSON almacenado en el HAR para esta ejecución. Esa comparación se realizó en los objetos de Python analizados en lugar de en su espacio en blanco o formato de clave serializado.

Esta es una reconstrucción semántica de la solicitud, no una reproducción byte por byte del intercambio de red original. La nueva solicitud puede usar una versión diferente de HTTP, orden de encabezados, negociación de compresión, conexión y sesión TLS.

Lo Que Obtienes de Vuelta

El flujo de trabajo produce tres artefactos o resultados reutilizables:

Etapa Salida ¿Se requiere navegador?
Captura session.har
Inspección Inventario de solicitudes y resumen de tipo MIME No
Reemisión Comparación de respuesta en vivo analizada y cuerpo capturado No

La sesión capturada produjo:

text Copy
tamaño HAR: 333269 bytes
entradas HTTP: 10
entradas JSON: 4
Citas visibles después de desplazarse: 40
estado de la solicitud reemitida: 200
coincidencia JSON capturada/viva: True

Estos números describen esta sesión objetivo específica. Una página diferente, versión de navegador, temporización de desplazamiento, ubicación de proxy o respuesta de página pueden producir un conjunto de solicitudes y tamaño de archivo diferentes.

Confirma la Secuencia Completa en Un Solo Script

La captura, inspección y programas de reproducción separados son más fáciles de entender, pero las mismas etapas se pueden combinar:

python Copy
import json
import os
import urllib.error
import urllib.request
from collections import Counter
from pathlib import Path
from urllib.parse import urlencode

from playwright.sync_api import sync_playwright


API_KEY = os.environ["SCRAPELESS_API_KEY"]
HAR_PATH = "session.har"


def scraping_browser_url(proxy_country="US", session_ttl=180):
    params = urlencode({
        "token": API_KEY,
        "sessionTTL": session_ttl,
        "proxyCountry": proxy_country,
    })
    return f"wss://browser.scrapeless.com/api/v2/browser?{params}"


# Etapa 1: captura. Se requiere un navegador.
with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(
        scraping_browser_url()
    )

    context = browser.new_context(
        record_har_path=HAR_PATH,
        record_har_content="embed",
    )

    page = context.new_page()
    page.goto(
        "https://quotes.toscrape.com/scroll",
        wait_until="networkidle",
    )

    for _ in range(3):
        page.evaluate(
            "window.scrollTo(0, document.body.scrollHeight)"
        )
        page.wait_for_timeout(800)

    context.close()
    browser.close()

print("=== captura ===")
print(
    "HAR escrito:",
    HAR_PATH,
    "-",
    os.path.getsize(HAR_PATH),
    "bytes",
)


# Etapa 2: inspección. El navegador está cerrado.
har = json.loads(Path(HAR_PATH).read_text())
entries = har["log"]["entries"]

print("\n=== inspección (sin navegador) ===")
print("total de entradas:", len(entries))

by_type = Counter(
    entry["response"]["content"]["mimeType"].split(";")[0]
    for entry in entries
)

for mime_type, count in by_type.most_common():
    print(f"  {mime_type}: {count}")


# Etapa 3: volver a emitir una solicitud. El navegador permanece cerrado.
target = next(
    entry
    for entry in entries
    if entry["request"]["url"].endswith("page=1")
)

captured_request = target["request"]
skip_headers = {"accept-encoding"}

headers = {
    header["name"]: header["value"]
    for header in captured_request["headers"]
    if not header["name"].startswith(":")
    and header["name"].lower() not in skip_headers
}

request = urllib.request.Request(
    captured_request["url"],
    headers=headers,
    method=captured_request["method"],
)

with urllib.request.urlopen(request, timeout=10) as response:
    if response.status != 200:
        raise urllib.error.HTTPError(
            captured_request["url"],
            response.status,
            "estado inesperado",
            response.headers,
            None,
        )

    live_data = json.loads(response.read())

captured_data = json.loads(
    target["response"]["content"]["text"]
)

print("\n=== volver a emitir (sin navegador) ===")
print("estado:", response.status)
print(
    "autor de la primera cita:",
    live_data["quotes"][0]["author"]["name"],
)
print(
    "coincide con el cuerpo capturado:",
    live_data == captured_data,
)

La ejecución combinada imprimió:

text Copy
=== captura ===
HAR escrito: session.har - 333259 bytes

=== inspección (sin navegador) ===
total de entradas: 10
  application/json: 4
  text/css: 3
  text/html: 1
  application/javascript: 1
  font/woff2: 1

=== volver a emitir (sin navegador) ===
estado: 200
autor de la primera cita: Albert Einstein
coincide con el cuerpo capturado: True

Solo la primera etapa importa y utiliza Playwright. Las etapas posteriores operan en el archivo y el punto final HTTP en vivo seleccionado.

Lo que un HAR captura

Un archivo HAR representa transacciones HTTP grabadas por el contexto del navegador.

Dependiendo del grabador y la configuración, una entrada puede contener:

  • Método de solicitud y URL
  • Parámetros de cadena de consulta
  • Encabezados de solicitud
  • Cookies de solicitud
  • Datos publicados
  • Estado de respuesta
  • Encabezados de respuesta
  • Cookies de respuesta
  • Tipo MIME
  • Contenido de respuesta
  • Tamaños de transferencia
  • Información de tiempo
  • Detalles de redirección
  • Información de caché

Con record_har_content="embed", Playwright almacena el contenido de respuesta disponible dentro del HAR. Sin contenido integrado, el inventario de solicitudes aún puede ser útil, pero el archivo puede no contener el cuerpo de respuesta necesario para el análisis o comparación sin conexión.

Lo que un HAR no garantiza

Un archivo HAR es un registro duradero de la actividad HTTP capturada, pero no es un registro completo de cada comportamiento del navegador.

Los marcos de WebSocket no se archivan

HAR modela transacciones de solicitud y respuesta. No preserva la secuencia de mensajes transportados dentro de una conexión WebSocket establecida.

Una página que combina puntos finales HTTP normales con un flujo en vivo de WebSocket necesita mecanismos de captura separados:

  • HAR para tráfico de solicitudes y respuestas HTTP
  • Escuchadores de marcos de WebSocket para mensajes de socket

La conexión inicial puede involucrar una actualización HTTP, pero el flujo de marcos en curso está fuera del modelo normal de solicitud-respuesta de HAR.

Un HAR no es una instantánea del DOM

El archivo no preserva el árbol del documento final de la misma manera que lo haría una instantánea del DOM.
Un cuerpo de respuesta puede contener el HTML o JSON original, pero las mutaciones de JavaScript posteriores, el estado de los elementos, el diseño renderizado y la apariencia visible para el usuario son preocupaciones separadas.

Un HAR No Es Un Video

El archivo de registro no contiene una línea de tiempo visual de lo que apareció en la página.

Utiliza la grabación de sesión de Scrapeless cuando el objetivo es revisar el comportamiento visible del navegador. Utiliza la captura de HAR cuando el objetivo es inspeccionar el tráfico HTTP estructurado.

Puede Faltar Contenido o Estar Codificado

HAR admite campos de contenido opcionales. Un grabador puede omitir cuerpos, y los recursos binarios pueden estar codificados.

Antes de analizar response.content.text, verifica que:

  • El campo text existe.
  • Se espera el tipo de contenido.
  • Se maneja el campo encoding si está presente.
  • El cuerpo no ha sido omitido por la configuración de grabación.

Una solicitud capturada puede reemitirse con éxito cuando el servidor en vivo aún acepta la solicitud reconstruida.

El punto final público /api/quotes funciona porque no depende de una sesión autenticada que esté por expirar.

La reemisión se complica más cuando la solicitud original utiliza:

  • Cookies de sesión
  • Tokens CSRF
  • Credenciales de portador de corta duración
  • URLs firmadas
  • Firmas de solicitud por sesión
  • Estado almacenado solo dentro del navegador
  • Datos generados por un paso de navegación anterior
  • Un cuerpo de solicitud cuyo contenido cambia por sesión

El HAR puede preservar los valores de credenciales originales, pero no puede extender su validez. Una vez que esos valores expiran, se puede requerir una nueva sesión de navegador para crear un nuevo estado.

Reproducir Datos de HAR de Forma Segura

Un archivo HAR puede contener datos de sesión sensibles.

Los encabezados de solicitud y respuesta pueden exponer:

  • Cookies
  • Encabezados de autorización
  • Claves de API
  • Identificadores de sesión
  • URLs internas
  • Datos personales devueltos por un punto final

Trata los archivos HAR como artefactos sensibles:

  • No los comprometas en un repositorio público.
  • Elimina credenciales antes de compartirlas.
  • Restringe el acceso a sesiones de producción archivadas.
  • Evita registrar encabezados completos innecesariamente.
  • Almacena solo las capturas necesarias para la tarea de depuración o auditoría.
  • Elimina archivos según los requisitos de retención del proyecto.

El archivo de registro en este tutorial proviene de una página de demostración pública y no autenticada. Las mismas suposiciones no deben aplicarse automáticamente a las aplicaciones autenticadas.

Problemas Comunes

El Archivo HAR No Aparece

La causa más común es cerrar el navegador sin cerrar explícitamente el contexto.

Usa:

python Copy
context.close()
browser.close()

También confirma que el proceso puede escribir en el directorio que contiene HAR_PATH.

El HAR No Contiene Cuerpo de Respuesta

Confirma que el contexto utiliza:

python Copy
record_har_content="embed"

Luego inspecciona si entry["response"]["content"]["text"] existe. Algunos recursos pueden omitirse o representarse con una codificación.

urllib Rechaza Un Nombre de Encabezado

Elimina cualquier encabezado cuyo nombre comience con :. Estos son encabezados pseudo-HTTP/2, no campos de encabezado ordinarios.

La URL y el método de solicitud ya llevan su significado relevante.

La Respuesta Reemitida Está Comprimida Inesperadamente

No copies el valor accept-encoding de un navegador sin pensar a menos que el cliente de reproducción soporte cada codificación de contenido anunciada.

Deja que el cliente HTTP negocie una codificación que pueda decodificar.

La Nueva Respuesta No Coincide con el HAR

Un desajuste no significa necesariamente que el código de reconstrucción sea incorrecto.

El servidor puede devolver contenido cambiante, marcas de tiempo, campos aleatorios, resultados específicos de geolocalización o datos ligados a credenciales que ya no son válidas.

Compara los campos que se espera que permanezcan estables en lugar de asumir que cada punto final siempre devuelve bytes idénticos.

Conclusión

El record_har_path de Playwright convierte un contexto de navegador en un archivo de registro HTTP duradero.

El flujo de trabajo tiene tres fases claras:

  1. Conectar Playwright a Scrapeless Scraping Browser y capturar la sesión de la página.
  2. Cerrar el navegador e inspeccionar log.entries como un JSON ordinario.
  3. Reconstruir una solicitud elegible con urllib y comparar su respuesta en vivo con el cuerpo capturado.

El navegador solo se requiere para producir el archivo de registro. Una vez que context.close() vacía el HAR, el archivo puede ser analizado en una máquina sin instalación de Playwright, proceso de navegador o conexión CDP.

Esa separación hace que la captura de HAR sea útil para la depuración posterior a la sesión, descubrimiento interno de API, auditoría de red y reconstrucción de solicitudes. También mantiene visibles las limitaciones: HAR no preserva tramas de WebSocket, estado visual renderizado, ni la validez futura de las credenciales capturadas.
Revisar la página del producto Scraping Browser para conocer las capacidades de la sesión del navegador detrás del flujo de trabajo de captura, y consultar los planes de precios de Scrapeless al pasar de una sesión de demostración a una carga de trabajo de captura más grande.

El explicador del Protocolo de Chrome DevTools cubre la superficie del protocolo debajo de la conexión del navegador.

Únete a la comunidad de Scrapeless para comparar patrones de captura de Playwright y flujos de trabajo de depuración de navegadores con otros desarrolladores: Discord · Telegram.

Regístrate en app.scrapeless.com para obtener la ejecución gratuita de Scraping Browser y luego adapta los pasos de captura, inspección y reconstrucción de solicitudes a una página pública relevante para tu proyecto.


FAQ

P: ¿Qué es un archivo HAR?

Un archivo HAR es un archivo JSON de archivos de transacciones HTTP capturados durante una sesión del navegador. Su matriz log.entries contiene un objeto por cada solicitud grabada, con información anidada de solicitud, respuesta, temporización y contenido.

Un archivo HAR no es una captura de pantalla, un video de página, ni una instantánea completa del DOM.

P: ¿En qué se diferencia la captura HAR de la interceptación de solicitudes en vivo?

La captura HAR registra el tráfico HTTP del contexto del navegador de manera amplia, mientras que la interceptación en vivo observa las solicitudes coincidentes a medida que ocurren.

La interceptación en vivo es útil cuando el punto final ya es conocido. La captura HAR es útil cuando la solicitud importante puede descubrirse solo después de que la sesión ha terminado.

P: ¿Necesitas un navegador para leer un archivo HAR?

No. Un archivo HAR completado es un documento JSON ordinario que puede leerse con el módulo json de Python.

El navegador es necesario durante la captura, pero no durante la inspección fuera de línea.

P: ¿Incluye la captura HAR tráfico de WebSocket?

La captura HAR no archiva los mensajes intercambiados dentro de una conexión de WebSocket establecida.

Usa un listener de frames de WebSocket cuando la página dependa de un feed de socket en vivo. HAR aún puede cubrir el tráfico HTTP ordinario utilizado por la misma página.

P: ¿La grabación HAR de Playwright es lo mismo que la grabación de sesión de Scrapeless?

No. La grabación HAR de Playwright crea un archivo de red estructurado, mientras que la grabación de sesión de Scrapeless crea una reproducción visual de la sesión del navegador renderizada.

Usa HAR para el análisis de solicitudes y respuestas. Usa la grabación de sesión cuando el comportamiento visible de la página sea el objeto de la investigación.

P: ¿Es el ejemplo de urllib lo mismo que route_from_har() de Playwright?

No. route_from_har() sirve respuestas grabadas a solicitudes realizadas dentro de un contexto de navegador Playwright en vivo.

El ejemplo de urllib lee una solicitud del HAR y envía una nueva solicitud al servidor en vivo sin iniciar un navegador.

P: ¿Por qué un HAR contiene encabezados como :authority y :method?

Esos nombres son campos de pseudo-encabezado HTTP/2 utilizados para transportar datos de control de solicitudes dentro del protocolo HTTP/2.

No son campos de encabezado ordinarios, por lo que un cliente de estilo HTTP/1.1 debe representar su significado a través del método de solicitud y la URL en lugar de copiar los nombres con prefijo de dos puntos.

P: ¿Se puede volver a emitir cada solicitud capturada con éxito?

No. Una solicitud solo puede volver a emitirse mientras el servidor en vivo acepte el método, URL, cuerpo, encabezados y credenciales reconstruidos.

Las solicitudes que dependen de cookies expiradas, tokens CSRF, URLs firmadas o credenciales de portador de corta duración pueden requerir una nueva sesión de navegador.

P: ¿Necesitas un proxy separado para este flujo de trabajo?

No se requiere configuración de proxy separado cuando la conexión del Scraping Browser de Scrapeless ya especifica el país del proxy deseado.

El ejemplo establece proxyCountry=US en la URL de conexión de CDP, por lo que la sesión del navegador utiliza esa salida configurada.

P: ¿Es seguro compartir un archivo HAR?

Un archivo HAR debe considerarse sensible hasta que su contenido haya sido revisado y desinfectado.

Puede contener cookies, encabezados de autorización, claves API, endpoints internos, datos de formularios enviados o contenido de respuesta privado. Elimina valores sensibles antes de compartir o comprometer el archivo.

P: ¿La grabación HAR solo funciona con Scrapeless Scraping Browser?

No. record_har_path es una opción de contexto de navegador de Playwright y se puede utilizar con navegadores locales o remotos compatibles.

Scrapeless Scraping Browser proporciona el entorno remoto de Chromium gestionado y la configuración de proxy utilizados durante la etapa de captura.

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.

Artículos más populares

Catalogar