← Todos los artículos

Cómo Extraer Datos de Interacción de TikTok e Instagram como JSON

Si alguna vez has intentado extraer datos de un vídeo de TikTok o una publicación de Instagram con una simple petición HTTP, ya conoces los dos problemas. La página llega casi vacía y los números que ves están redondeados a cosas como “1,2 M de reproducciones”. Para escucha social, verificación de influencers o informes de campaña, “1,2 M” no es suficiente. Necesitas el número entero exacto.

Esta guía explica por qué estos dos sitios son difíciles de raspar, dónde viven realmente los números dentro de la página y cómo extraer datos de interacción exactos como JSON estructurado. Todo lo aquí descrito es reproducible con herramientas de código abierto, y al final cubrimos el atajo para equipos que preferirían no mantener toda esa fontanería.

Por Qué TikTok e Instagram Se Resisten

Ambas plataformas son aplicaciones de página única. El HTML que recibe tu primera petición es un armazón: algunas etiquetas meta, un título y un paquete de JavaScript. El contenido que ves en un navegador real se construye después de cargar la página, cuando ese JavaScript se ejecuta e hidrata el DOM con los datos.

Eso tiene dos consecuencias para el scraping:

  • Una petición simple no devuelve casi nada. Herramientas como requests o un fetch sin más nunca ejecutan el JavaScript, así que el texto visible - descripciones, recuentos, nombres de usuario - simplemente no está en el cuerpo de la respuesta. Obtienes un título de página y poco más.
  • Los números visibles son para humanos, no para máquinas. Cuando la página sí se renderiza, muestra recuentos abreviados: “1,2 M”, “34,5 K”, “980”. Esas cadenas pierden información. No puedes recuperar el valor exacto a partir de “1,2 M”, y los límites de redondeo cambian con el tiempo.

Además, ambos sitios ejecutan comprobaciones antibot, límites de tasa y muros de inicio de sesión, así que el scraping ingenuo se ve limitado o bloqueado enseguida.

Dónde Viven Realmente los Números

Aquí está la buena noticia. Aunque el texto visible esté redondeado, los enteros exactos casi siempre están presentes en la página, solo que no donde estás mirando. Las SPA modernas envían una carga útil JSON incrustada para que la aplicación se hidrate al instante. Esa carga útil contiene los datos precisos.

  • TikTok incrusta un gran bloque JSON en una etiqueta script, históricamente SIGI_STATE y más recientemente __UNIVERSAL_DATA_FOR_REHYDRATION__. Dentro encontrarás campos como playCount, diggCount (me gusta), commentCount, shareCount y collectCount (guardados) como números reales.
  • Instagram expone los datos de las publicaciones a través de JSON incrustado y de sus respuestas internas de GraphQL. Un objeto de publicación normalmente incluye edge_media_preview_like.count, edge_media_to_comment.count, el username del propietario y taken_at_timestamp.

El truco es dejar de raspar el texto renderizado y empezar a leer la carga útil incrustada. Ahí es donde “1,2 M” se convierte en 1203481.

Extraer el JSON Incrustado Tú Mismo

Aún necesitas renderizar la página, o al menos superar la capa antibot, antes de poder leer esa etiqueta script. Un navegador headless resuelve ambas cosas. Playwright es una buena opción por defecto porque controla una versión real de Chromium y te permite esperar a que aparezca la carga útil.

import json
from playwright.sync_api import sync_playwright

def scrape_tiktok(url: str) -> dict:
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()
        page.goto(url, wait_until="networkidle")

        # Lee la carga de hidratación incrustada en lugar del texto renderizado
        raw = page.locator(
            "script#__UNIVERSAL_DATA_FOR_REHYDRATION__"
        ).inner_text()
        browser.close()

    data = json.loads(raw)
    scope = data["__DEFAULT_SCOPE__"]["webapp.video-detail"]
    stats = scope["itemInfo"]["itemStruct"]["stats"]
    return {
        "plays": int(stats["playCount"]),
        "likes": int(stats["diggCount"]),
        "comments": int(stats["commentCount"]),
        "shares": int(stats["shareCount"]),
        "saves": int(stats["collectCount"]),
    }

La ruta exacta de las claves cambia con el tiempo, así que trátala como algo que tendrás que mantener. Un patrón más resistente es cargar el JSON y recorrerlo en busca del objeto stats en lugar de codificar la ruta completa.

Para Instagram se aplica la misma idea, pero a menudo obtienes los datos más limpios interceptando la respuesta de GraphQL que hace la aplicación, en lugar de analizar una etiqueta script estática:

from playwright.sync_api import sync_playwright

def scrape_instagram(url: str) -> dict:
    result = {}

    def on_response(response):
        if "graphql/query" in response.url and response.status == 200:
            try:
                media = response.json()["data"]["xdt_shortcode_media"]
            except Exception:
                return
            result.update({
                "likes": media["edge_media_preview_like"]["count"],
                "comments": media["edge_media_to_comment"]["count"],
                "author_username": media["owner"]["username"],
                "posted_at": media["taken_at_timestamp"],
            })

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()
        page.on("response", on_response)
        page.goto(url, wait_until="networkidle")
        browser.close()

    return result

La interceptación de respuestas es potente porque captura la carga útil exacta que usa la propia aplicación. Estás leyendo los mismos números que lee la interfaz antes de redondearlos para mostrarlos.

Mantenerse Sin Bloqueos a Escala

Un vídeo funciona bien desde tu portátil. Mil vídeos por hora desde una sola IP no. Si planeas recopilar a gran volumen, presupuesta el problema antibot desde el principio:

  • Rota IP residenciales. Las IP de centro de datos se marcan rápido en ambas plataformas. Enruta las peticiones a través de proxies residenciales rotativos para que cada petición parezca de un usuario corriente distinto.
  • Regula y añade variación. Añade retardos aleatorios entre peticiones. El tráfico predecible y cronometrado por máquina es una de las señales más fáciles de detectar.
  • Reutiliza huellas de navegador realistas. Las versiones de navegador indetectables y las cabeceras coherentes reducen las señales de cliente automatizado. No mezcles un user agent móvil con una ventana de escritorio.
  • Gestiona el caso del armazón vacío. Si tu extractor recibe una página sin carga útil, trátalo como un bloqueo suave y reintenta más tarde, en lugar de registrar un cero.

Nada de esto es exótico, pero es trabajo continuo. Los selectores se desplazan, las formas de GraphQL cambian y el bloqueo se ajusta. Ese mantenimiento es el coste real de hacerlo por tu cuenta.

El Atajo: Pedir JSON Ya Analizado

Si prefieres saltarte la flota de navegadores y la arqueología de cargas útiles, puedes pedir los datos ya analizados directamente. ScrapeUnblocker gestiona por ti la renderización y la capa antibot, y su modo de datos analizados lee la carga útil incrustada en tu nombre para que obtengas enteros exactos en lugar del texto redondeado que pinta la página.

Con parsed_data=true, la URL de un vídeo de TikTok devuelve plays, likes, comments, shares y saves, y una publicación de Instagram devuelve likes y comments exactos, el author.username real y un posted_at real. Envías una URL, obtienes JSON limpio y no eres tú quien mantiene los selectores cuando cambia el marcado.

import requests

resp = requests.get(
    "https://api.scrapeunblocker.com/getPageSource",
    params={
        "url": "https://www.tiktok.com/@user/video/1234567890",
        "parsed_data": "true",
    },
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = resp.json()
print(data["plays"], data["likes"], data["comments"])

Consulta la guía de datos analizados para ver la lista completa de campos y parámetros.

Preguntas Frecuentes

¿Por qué los recuentos que extraigo parecen redondeados? Porque estás leyendo el texto que la interfaz renderiza para humanos, que está abreviado (“1,2 M”). El entero exacto vive en la carga útil JSON incrustada o en la respuesta de GraphQL, no en el texto visible del DOM. Analiza la carga útil y obtendrás el número real.

¿Puedo hacerlo sin un navegador headless? A veces. Si puedes llegar al JSON incrustado o a un endpoint JSON directamente, una petición HTTP bien formada puede funcionar. En la práctica, ambas plataformas protegen esas respuestas tras comprobaciones antibot, así que un navegador real o una API de scraping que renderice la página es el camino fiable.

¿Es legal raspar TikTok e Instagram? Raspar datos públicamente visibles suele tratarse de forma distinta a acceder a contenido privado o tras inicio de sesión, pero las reglas dependen de tu jurisdicción, de los términos de la plataforma y del uso que hagas de los datos. Recopila solo datos públicos, evita datos personales que no necesites y consulta asesoría legal para cualquier fin comercial.

¿Cómo evito que me bloqueen? Rota IP residenciales, añade retardos aleatorios, usa huellas de navegador realistas y reintenta los bloqueos suaves en lugar de registrar ceros. A gran volumen, una API de desbloqueo elimina la mayor parte de este trabajo porque gestiona la renderización y la rotación por ti.

Para Terminar

La razón por la que los datos de interacción social parecen difíciles de raspar es un desajuste: estás leyendo texto redondeado, mientras que los números exactos están una capa más abajo, en una carga útil JSON incrustada. Una vez que lees esa carga en lugar del DOM, TikTok e Instagram se convierten en otra fuente estructurada más - reproducciones, me gusta, comentarios, compartidos, guardados, nombres de usuario y marcas de tiempo, todo como enteros limpios.

Constrúyelo tú mismo con Playwright cuando quieras control total, o deja que ScrapeUnblocker devuelva el JSON analizado directamente cuando prefieras dedicar tu tiempo al análisis en lugar de a la fontanería.

Prueba ScrapeUnblocker gratis

Tasa de éxito del 95%+ · desde 0,55 € por cada 1000 llamadas · 500 solicitudes gratis al registrarte.

Pruébalo gratis → Ver precios