Volver al blog

Raspado de Web Rnet: Suplantación de huellas dactilares TLS del navegador

Alex Johnson
Alex Johnson

Senior Web Scraping Engineer

23-Jul-2026

TL;DR:

  • rnet es un cliente HTTP asíncrono que imita la huella TLS de un navegador real, por lo que un servidor que perfila el apretón de manos ve Chrome, no un valor predeterminado de Python. Está construido en Rust y expuesto a Python.
  • Lo que rnet no hace es renderizar JavaScript. Una huella perfecta hace que la solicitud sea aceptada; no ejecuta los scripts que crean el contenido de una página.
  • La huella es real en esta guía. Una ejecución en vivo imitando a Chrome produjo un JA3 en forma de navegador y un JA4 t13d1516h2 sobre HTTP/2 — y aún encontró 0 bloques de citas en una página construida por scripts.
  • rnet también es el cliente que llama a Scrapeless. El mismo cliente asíncrono envió una solicitud POST de la URL a la API de Raspado Universal de Scrapeless, obtuvo el HTML renderizado y extrajo todos los 10 autores.
  • Dos problemas diferentes, claramente separados. rnet maneja la capa de huella TLS; Scrapeless maneja el renderizado. Ninguno sustituye al otro.
  • Libre para comenzar en el lado de la obtención. Crea tu clave API de Scrapeless en app.scrapeless.com.

Qué es rnet, y qué no es

rnet es un cliente HTTP asíncrono para Python construido sobre la pila de redes de Rust, y su característica distintiva es la suplantación de huellas TLS y HTTP. Cuando un cliente abre una conexión TLS, la forma exacta de su apretón de manos — orden de cifrado, extensiones, curvas — forma una huella que los servidores pueden perfilar; un cliente estándar de Python tiene una huella que ningún navegador produce, y algunos sitios la rechazan antes de servir un solo byte de HTML. rnet reproduce el apretón de manos de un navegador elegido, por lo que la conexión se presenta como Chrome o Firefox, y lo hace sobre HTTP/2 con una API asíncrona.

Lo que no es, es un renderizador. Una huella coincidente te permite pasar el perfilado a nivel de apretón de manos; no hace nada con JavaScript. La página aún construye su contenido con scripts, y rnet, como cualquier cliente HTTP, devuelve el marcado que el servidor envió — que en una página construida por scripts está vacío de contenido. Por lo tanto, un scraper de rnet separa dos problemas que a menudo se confunden: la capa de huella, donde rnet gana su lugar, y la capa de renderizado, que necesita un navegador. Esta guía utiliza la API de Raspado Universal de Scrapeless para el renderizado, con rnet realizando la llamada. Para tener una visión más amplia, el tutorial de raspado web en Python es el compañero.

Instalar

rnet es toda la cadena de herramientas para esta guía. La versión contra la que fue escrita es rnet 2.4.2:

bash Copy
pip install "rnet==2.4.2"

Mantén tu clave en el entorno, nunca en el código fuente:

bash Copy
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"

Apunta rnet a un punto final de perfilado con un conjunto de suplantación de navegador, y el apretón de manos se lee como ese navegador. JA3 y JA4 son resúmenes estándar del apretón de manos TLS — la huella negociada bajo el estándar TLS 1.3 — y rnet produce valores en forma de navegador. Sin embargo, el mismo cliente en una página construida por scripts no encuentra nada, porque la huella no tiene relación con el renderizado del lado del cliente que llena la página:

python Copy
# fingerprint.py — un apretón de manos de un navegador real, y su límite
import asyncio
import json
import re

import rnet

FINGERPRINT_URL = "https://tls.peet.ws/api/all"
PAGE_URL = "https://quotes.toscrape.com/js/"


async def main() -> None:
    client = rnet.Client(impersonate=rnet.Impersonate.Chrome131)

    resp = await client.get(FINGERPRINT_URL)
    fp = json.loads(await resp.text())
    ja3 = fp.get("tls", {}).get("ja3_hash", "")
    ja4 = fp.get("tls", {}).get("ja4", "")
    print("ja3 es 32-hex:", bool(re.fullmatch(r"[0-9a-f]{32}", ja3)))
    print("prefijo ja4:", ja4.split("_")[0])
    print("versión http:", fp.get("http_version"))

    page_resp = await client.get(PAGE_URL)
    page = await page_resp.text()
    print("bloques de citas en la página js:", page.count('class="quote"'))


asyncio.run(main())

El apretón de manos tiene forma de navegador; la página sigue estando vacía:

text Copy
ja3 es 32-hex: True
prefijo ja4: t13d1516h2
versión http: h2
bloques de citas en la página js: 0

El hash JA3 varía de ejecución a ejecución por diseño — los navegadores aleatorizan una extensión bajo el mecanismo GREASE, y rnet reproduce eso — por lo que un scraper verifica la forma, no un valor fijo. El prefijo JA4 t13d1516h2 es la parte estable: TLS 1.3, 16 suites de cifrado, 16 extensiones, HTTP/2. Lo que nada de esto hace es renderizar la página.

Renderizar con Scrapeless, llamado por rnet

Mantenga el mismo cliente asíncrono y cambie el objetivo: envíe la URL a la API de Raspado Universal de Scrapeless, que renderiza del lado del servidor y devuelve el HTML terminado. rnet maneja esta solicitud como cualquier otra, por lo que un cliente cubre tanto las búsquedas sensibles a la huella digital como las renderizadas:

python Copy
# rendered.py — rnet llama a la API de renderizado, luego extrae
import asyncio
import json
import os
import re

import rnet


async def main() -> None:
    client = rnet.Client(impersonate=rnet.Impersonate.Chrome131)
    resp = await client.post(
        "https://api.scrapeless.com/api/v2/unlocker/request",
        headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"], "Content-Type": "application/json"},
        json={"actor": "unlocker.webunlocker", "input": {"url": "https://quotes.toscrape.com/js/", "js_render": True}},
    )
    html = json.loads(await resp.text())["data"]

    authors = re.findall(r'<small class="author">(.*?)</small>', html)
    print("bloques de cita renderizados:", html.count('class="quote"'))
    print("primer autor:", authors[0])


asyncio.run(main())

Ahora el contenido está presente:

text Copy
bloques de cita renderizados: 10
primer autor: Albert Einstein

Esa es toda la herramienta de raspado, y permaneció en rnet todo el tiempo: el cliente asíncrono que lleva una huella digital de navegador también hace la llamada de renderizado. La API Universal de Raspado hace el renderizado; rnet hace el HTTP.

Obtenga su clave API en el plan gratuito: app.scrapeless.com

Patrones avanzados

  • Ajuste la suplantación a un navegador actual. rnet.Impersonate expone versiones específicas de navegadores; elija una reciente, ya que una huella digital de una versión antigua es en sí misma una señal. La API del cliente no cambia con el objetivo.
  • Reutilice un cliente. Un rnet.Client agrupa conexiones; créelo una vez y compártalo entre un grupo de tareas asíncronas en lugar de por solicitud, donde es donde el transporte asíncrono da sus frutos.
  • Verifique la forma, no el valor. Debido a que GREASE aleatoriza el JA3 por conexión, afirme el patrón de 32 hexadecimales y el prefijo JA4, como lo hace el primer script: nunca un hash codificado de manera fija.
  • Agregue un analizador cuando la forma crezca. La expresión regular mantiene esto en una dependencia; para registros anidados, alimente el HTML renderizado a una biblioteca de selectores y seleccione los campos allí.

Solución de problemas

  • Un sitio aún bloquea la solicitud. Una huella digital TLS es una señal entre muchas. Si un apretón de manos con forma de navegador no es suficiente, el bloqueo está por encima del transporte: reputación IP, encabezados o un desafío, y la solicitud pertenece al camino de Scrapeless, que maneja eso.
  • Cero bloques de una página que puedes ver en un navegador. El contenido está renderizado en JavaScript; la huella digital aceptó la solicitud pero los scripts nunca se ejecutaron. Rote esa URL a través de la llamada de Scrapeless con js_render.
  • El hash JA3 cambia en cada ejecución. Eso es correcto: GREASE lo aleatoriza, exactamente como lo hace un navegador real. Afirme el patrón y el prefijo JA4 en su lugar.
  • Errores de await en la respuesta. rnet es asíncrono: tanto la solicitud como la lectura de .text() se esperan, y todo se ejecuta dentro de asyncio.run, como muestran los ejemplos.

Conclusión

rnet se gana su lugar como la capa que hace que el apretón de manos se vea bien: un cliente asíncrono, respaldado por Rust, que transporta la huella digital TLS de un navegador real, y que también hace la llamada de renderizado. El problema que no resuelve es la página en sí: el resultado de cero bloques del script de huellas digitales lo determina — y una POST de Scrapeless, enviada por rnet, cierra la brecha. Conecte la capa de huellas digitales y la capa de renderizado en un solo cliente y los diez autores de la página de demostración regresan de una conexión que un servidor lee como Chrome.

Crea una cuenta gratuita en Scrapeless para obtener una clave API, y la documentación para desarrolladores cubre los parámetros de unlocker.webunlocker. Consulte precios de Scrapeless cuando planee un trabajo recurrente.

Preguntas frecuentes

P: ¿Puede rnet raspar páginas renderizadas en JavaScript por sí mismo?

No. rnet es un cliente HTTP con suplantación de huellas digitales; acepta una solicitud en la capa TLS pero no ejecuta JavaScript. En una página construida por un script, la búsqueda devuelve un marcado con el contenido faltante — el resultado de cero bloques de la guía. Rote esas páginas a través de la API Universal de Raspado de Scrapeless, que las renderiza, con rnet realizando la llamada.

P: ¿Qué hace realmente la suplantación de huellas digitales TLS?
Moldea el apretón de manos TLS de rnet para que coincida con un navegador elegido, de modo que un servidor que perfila la huella digital JA3 o JA4 vea Chrome o Firefox en lugar de un cliente Python genérico. Eso elimina el filtrado a nivel de apretón de manos; no elimina los bloqueos basados en IP, las páginas de desafío o la necesidad de renderizar JavaScript.

P: ¿Cómo se diferencia rnet de curl_cffi?

Ambos impersonan huellas digitales de navegadores. rnet está construido en Rust y es asíncrono por diseño; curl_cffi envuelve curl-impersonate y es sincrónico. Elige según si tu scraper es asíncrono y qué objetivos de impersonación necesitas; el patrón de dos capas con una API de renderizado es el mismo de cualquier manera.

P: ¿Por qué cambia el hash JA3 en cada ejecución?

Porque los navegadores reales aleatorizan una extensión TLS a través del mecanismo GREASE, y rnet reproduce ese comportamiento. Un JA3 fijo se vería artificial. Verifica el patrón de 32 hexadecimales y el prefijo JA4 en lugar de un valor específico.

P: ¿Es legal raspar con rnet?

El cliente HTTP no cambia las reglas de recolección. Solo obtén páginas públicas, respeta los términos del sitio y las directivas de robots estandarizadas por el Protocolo de Exclusión de Robots, mantiene los volúmenes acotados y maneja cualquier dato personal bajo las leyes que te sean aplicables.

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