Ir al contenido

Análisis de sentimientos en Twitter con Python

El análisis de sentimientos en Twitter etiqueta cada tweet como positivo, negativo o neutral con un modelo de puntuación. En 12,300 tweets etiquetados por personas, un modelo RoBERTa entrenado con tweets acertó el 72.3%; las populares reglas de VADER acertaron el 53.0%.

Respuesta corta: Recopila una muestra de tweets con una consulta de búsqueda y puntúa cada uno: un modelo transformer es el que puntúa con más precisión, VADER el más rápido. La API oficial de X cobra $5.00 por 1,000 posts por la recopilación. Con Sorsa API, POST /search-tweets devuelve unos 20 tweets por solicitud desde $0.07 por 1,000, y las 100 solicitudes gratis cubren una primera ejecución de 2,000 tweets.

Endpoint
POST /search-tweets
Tamaño de página
unos 20 tweets por solicitud
Precio
desde $0.07 por 1,000

#Cómo funciona el análisis de sentimientos en Twitter

El análisis de sentimientos lee el texto de un tweet y devuelve una de tres etiquetas, normalmente con una puntuación de confianza, y luego cuenta las etiquetas en unos cientos o unos millones de tweets para medir qué siente la gente sobre un tema. Todo proyecto sigue los mismos cinco pasos:

  1. Recopila los tweets que mencionan el tema, con una consulta de búsqueda y una ventana de fechas.

  2. Limpia el texto: reemplaza las @menciones y los enlaces, elimina los retweets y las copias.

  3. Puntúa cada tweet con un modelo: positivo, neutral o negativo.

  4. Comprueba el modelo con unos cientos de tweets que etiquetaste tú mismo.

  5. Agrega las etiquetas en un número por día, por producto o por cuenta.

    1 El tweet, tal como se recopiló @nadia_reviews_gear oh great, the update broke Bluetooth AGAIN https://t.co/k3v9q2 2 Limpiado para el modelo @user oh great, the update broke Bluetooth AGAIN http Mayúsculas y puntuación se conservan. 3 RoBERTa lo puntúa Negativo 86.0% Neutral 12.5% Positivo 1.5% 4 Etiquetar y contar negativo "oh great" leído como sarcasmo Sentimiento neto del día: % positivo menos % negativo
    El modelo puntúa cada tweet por separado; el número que reportas sale de sumar las etiquetas.

Las marcas lo aplican a las menciones después de un lanzamiento o una caída, los equipos de soporte a las respuestas a sus posts, los analistas a un ticker o un token, y los investigadores a un debate o una elección. Mide hacia dónde se inclina una multitud, no lo que quiso decir una persona: un modelo lee las palabras, así que el sarcasmo y los tweets mixtos ("great camera, awful battery") son donde más falla.

Dos decisiones pesan más que cualquier otra en el resultado: qué tweets recopilas y en qué puntuador confías. Las siguientes secciones las tratan en ese orden.

#Obtén los tweets con Sorsa API

Para el paso de recopilación, usa Sorsa API, una API y scraper de Twitter (X): una solicitud de búsqueda devuelve unos 20 tweets con toda la sintaxis de búsqueda de X, desde $0.07 por 1,000 tweets, a 20 solicitudes por segundo en todos los planes. Necesitas una clave API y ninguna cuenta de desarrollador de X, y Sorsa API nunca te pide un login de X, así que ninguna cuenta tuya corre riesgo.

Elige el endpoint según la pregunta que haces:

  1. Consigue una clave. Regístrate y copia tu clave API del panel. Las primeras 100 solicitudes son gratis, sin tarjeta.

  2. Escribe la consulta. Da forma a la muestra antes de pagarla: lang:en conserva un solo idioma para un modelo en inglés, -filter:retweets elimina las copias, -from: deja fuera los posts de la propia marca, y since: con until: fija la ventana. Nuestra lista de operadores de búsqueda de Twitter tiene el resto, y la herramienta gratuita de búsqueda avanzada de Twitter muestra lo que devuelve una consulta antes de que escribas código.

  3. Envía una solicitud de prueba. Una solicitud devuelve unos 20 tweets y un cursor a la página siguiente. Un cursor es un token que apunta a la página siguiente de resultados.

    Terminal
    curl -X POST "https://api.sorsa.io/v3/search-tweets" \
      -H "ApiKey: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"order": "latest",
           "query": "\"battery life\" headphones lang:en -filter:retweets"}'
    
    Respuesta200 OK
    {
      "tweets": [
        {
          "id": "2116943508275302411",
          "full_text": "Two full days of battery on these headphones",
          "created_at": "2026-11-01T18:42:09Z",
          "lang": "en",
          "is_reply": false,
          "likes_count": 31,
          "reply_count": 4,
          "retweet_count": 2,
          "user": {
            "id": "1834470112963551232",
            "username": "nadia_reviews_gear",
            "followers_count": 2318
          }
        }
      ],
      "next_cursor": "DAADDAABCgABHWAk1xBVYAoKAAIdYCTSHxWQ"
    }
    
  4. Reúne las páginas en una tabla. Envía next_cursor de vuelta hasta que llegue vacío, y conserva una fila por ID de tweet. Un 429 significa más de 20 solicitudes en un segundo, así que espera y reintenta; un 403 significa que falta la clave o que no quedan solicitudes.

    Python
    import time
    
    import pandas as pd
    import requests
    
    URL = "https://api.sorsa.io/v3/search-tweets"
    HEADERS = {"ApiKey": "YOUR_API_KEY"}
    
    
    def collect(query, limit=2000):
        body = {"query": query, "order": "latest"}
        rows = {}
        while len(rows) < limit:
            r = requests.post(URL, headers=HEADERS, json=body, timeout=30)
            if r.status_code == 429:
                time.sleep(1)
                continue
            r.raise_for_status()
            page = r.json()
            for t in page.get("tweets", []):
                rows[t["id"]] = {
                    "id": t["id"],
                    "created_at": t["created_at"],
                    "text": t["full_text"],
                    "lang": t.get("lang"),
                    "is_reply": t.get("is_reply", False),
                    "likes": t.get("likes_count", 0),
                    "author": t["user"]["username"],
                }
            if not page.get("next_cursor"):
                break
            body["next_cursor"] = page["next_cursor"]
        return pd.DataFrame(rows.values())
    
    
    df = collect('"battery life" headphones lang:en -filter:retweets')
    print(len(df), "tweets")
    

Cuánto cuestan los tweets

En Sorsa API una muestra de sentimientos cuesta desde $0.07 por 1,000 tweets mediante búsqueda; la API oficial de X cobra $5.00 por 1,000 lecturas de posts.

Tweets que analizar

API oficial de X

$500

para 100K tweets

$0.005 por post

Sorsa API

desde$7

para 100K tweets

desde $0.07 por 1,000

En la API oficial de X la factura además crece con los autores: a partir del 22 de noviembre de 2026, X cobra el perfil que llega con cada post como una lectura de usuario, $0.010 más por post (aviso de X (se abre en una pestaña nueva)), y su plan de pago por uso se detiene en 3 millones de lecturas de posts al mes (lista de precios de X (se abre en una pestaña nueva)). En Sorsa API cada tweet trae el perfil completo de su autor, y las solicitudes fallidas nunca se cobran.

Las páginas de una consulta llegan una tras otra, porque cada una necesita el cursor de la anterior. Para una muestra grande, divide la ventana en días y ejecuta los días en paralelo, hasta 20 solicitudes por segundo con una sola clave. La misma búsqueda llega hasta 2006, algo que nuestra guía de datos históricos de Twitter trata para series de tiempo largas.

#Limpia los tweets como espera el modelo

Limpia menos de lo que hacen la mayoría de los tutoriales: reemplaza las @menciones por @user y los enlaces por http, elimina los retweets y las copias exactas, y conserva los emojis, las mayúsculas y la puntuación. El modelo RoBERTa se entrenó con texto preparado exactamente así, y VADER lee las mayúsculas, los signos de exclamación y los emojis como un sentimiento más fuerte.

Python
import re


def prepare(text):
    text = re.sub(r"@\w+", "@user", text)
    return re.sub(r"https?://\S+", "http", text)


df = df[df["lang"] == "en"]
df = df[~df["text"].str.startswith("RT @")]
df["clean"] = df["text"].map(prepare)
df = df.drop_duplicates(subset="clean")

Las copias exactas importan más de lo que parece: los sorteos y las campañas de bots publican la misma frase cientos de veces, y una sola campaña puede mover la puntuación de un día. Para los casi duplicados y un conjunto de referencia con etiquetas hechas a mano, nuestra guía para crear un dataset de Twitter para machine learning tiene el código.

Para tener en cuenta

La expresión regular de limpieza que copian muchos tutoriales, [^a-zA-Z0-9\s], borra todos los emojis y todos los signos de exclamación. Ambos llevan sentimiento: en los propios ejemplos de VADER, una línea con tres emojis y ninguna otra palabra de opinión puntúa +0.70.

#Cuatro formas de puntuar el sentimiento, probadas en 12,300 tweets

En el conjunto de prueba de sentimientos de TweetEval, 12,300 tweets que personas etiquetaron como positivos, neutrales o negativos, el modelo RoBERTa acertó el 72.3%, una regresión logística entrenada con 45,600 tweets etiquetados el 59.6%, VADER el 53.0% y TextBlob el 48.3%. Ejecutamos los cuatro el 2 de noviembre de 2026, en 4 hilos de CPU; la tabla tiene las puntuaciones completas.

MétodoPrecisiónRecall macroClasificados como positivos (personas: 19.3%)Tweets por segundo
RoBERTa (cardiffnlp, latest)72.3%73.4%20.0%7
TF-IDF + regresión logística59.6%60.1%21.1%34.0K
VADER, banda de ±0.0553.0%57.0%38.9%27.6K
TextBlob, signo de la polaridad48.3%49.4%37.2%6,867
  • RoBERTa (cardiffnlp, latest)

    Precisión
    72.3%
    Recall macro
    73.4%
    Clasificados como positivos (personas: 19.3%)
    20.0%
    Tweets por segundo
    7
  • TF-IDF + regresión logística

    Precisión
    59.6%
    Recall macro
    60.1%
    Clasificados como positivos (personas: 19.3%)
    21.1%
    Tweets por segundo
    34.0K
  • VADER, banda de ±0.05

    Precisión
    53.0%
    Recall macro
    57.0%
    Clasificados como positivos (personas: 19.3%)
    38.9%
    Tweets por segundo
    27.6K
  • TextBlob, signo de la polaridad

    Precisión
    48.3%
    Recall macro
    49.4%
    Clasificados como positivos (personas: 19.3%)
    37.2%
    Tweets por segundo
    6,867
Aciertos, de 12.3K tweets etiquetados por personas RoBERTa (Cardiff NLP) modelo entrenado con 124M de tweets 72.3% TF-IDF + logistic regression entrenado con 45.6K tweets etiquetados 59.6% VADER lista de palabras y reglas, banda ±0.05 53.0% TextBlob léxico de adjetivos, signo de polaridad 48.3% decir que todo tweet es neutral: 48.3%
El modelo entrenado con tweets gana por un amplio margen. VADER y TextBlob son instantáneos, pero fallan cerca de la mitad de las etiquetas.

La precisión es la proporción de tweets etiquetados como los etiquetaron las personas. Para tener una escala, llamar neutral a todos los tweets da 48.3%, porque casi la mitad de los tweets de prueba son neutrales, así que VADER suma menos de 5 puntos frente a no hacer nada. El recall macro es la puntuación propia de TweetEval: el promedio de cuántos tweets negativos, neutrales y positivos encontró cada método, de modo que un método no puede ganar llamando neutral a todo. Dos salvedades: el modelo RoBERTa se ajustó con la partición de entrenamiento de TweetEval, así que esta prueba es su terreno, y la regresión logística aprendió de esa misma partición. En tu propio tema, espera que los dos pierdan unos puntos, y compruébalos como indica el paso 4. Las velocidades salen de un servidor compartido de 4 núcleos que estaba ocupado con otras tareas, así que tómalas como un mínimo.

RoBERTa: el más preciso

El modelo twitter-roberta-base-sentiment-latest (se abre en una pestaña nueva) de Cardiff NLP es un modelo RoBERTa entrenado con unos 124 millones de tweets de 2018 a 2021 y ajustado después para tres etiquetas. Funciona en una CPU normal y no necesita clave API; a la velocidad que medimos en un servidor ocupado, 10K tweets toman unos 23 minutos, así que para muestras grandes ejecútalo por lotes durante la noche o en una GPU.

Python
from transformers import pipeline

scorer = pipeline(
    "sentiment-analysis",
    model="cardiffnlp/twitter-roberta-base-sentiment-latest",
)
out = scorer(df["clean"].tolist(), batch_size=32, truncation=True)
df["label"] = [o["label"] for o in out]
df["confidence"] = [round(o["score"], 3) for o in out]
Salida
positive 0.978  Battery life on these headphones is great, two full days
positive 0.859  Oh great, another update that breaks Bluetooth. Love ...
neutral  0.789  The firmware update rolls out to all devices on Monday.
positive 0.960  not bad at all tbh, @user was right http
negative 0.860  @user oh great, the update broke Bluetooth AGAIN http

Para tweets en otros idiomas, el modelo twitter-xlm-roberta-base-sentiment (se abre en una pestaña nueva) del mismo equipo se entrenó con unos 198 millones de tweets y se ajustó en 8 idiomas, entre ellos español, portugués, francés y árabe. Para emociones en lugar de polaridad, su modelo de emociones devuelve 11 etiquetas como ira, miedo y alegría.

VADER: reglas instantáneas, una línea neutral débil

VADER es una lista de unas 7,500 palabras puntuadas más reglas para mayúsculas, signos de exclamación, "but" y negación, hecha para redes sociales. Puntuó unos 27,600 tweets por segundo en un solo núcleo, lo que lo vuelve una buena primera mirada a un millón de tweets. Su punto débil es la banda neutral: llamó positivos al 38.9% de los tweets de prueba, el doble del 19.3% que marcaron las personas.

Python
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer

vader = SentimentIntensityAnalyzer()


def vader_label(text, band=0.05):
    c = vader.polarity_scores(text)["compound"]
    if c >= band:
        return "positive"
    return "negative" if c <= -band else "neutral"


df["vader"] = df["text"].map(vader_label)
TweetVADER (compound)RoBERTa
Battery life on these headphones is great, two full dayspositive (+0.62)positive (0.98)
Oh great, another update that breaks Bluetooth. Love that for me.positive (+0.85)positive (0.86)
The firmware update rolls out to all devices on Monday.neutral (+0.00)neutral (0.79)
not bad at all tbh, @kai_builds_tools was right https://t.co/x1y2z3 (se abre en una pestaña nueva)positive (+0.43)positive (0.96)
@nadia_reviews_gear oh great, the update broke Bluetooth AGAIN https://t.co/k3v9q2 (se abre en una pestaña nueva)positive (+0.32)negative (0.86)
  • Battery life on these headphones is great, two full days

    VADER (compound)
    positive (+0.62)
    RoBERTa
    positive (0.98)
  • Oh great, another update that breaks Bluetooth. Love that for me.

    VADER (compound)
    positive (+0.85)
    RoBERTa
    positive (0.86)
  • The firmware update rolls out to all devices on Monday.

    VADER (compound)
    neutral (+0.00)
    RoBERTa
    neutral (0.79)
  • not bad at all tbh, @kai_builds_tools was right https://t.co/x1y2z3 (se abre en una pestaña nueva)

    VADER (compound)
    positive (+0.43)
    RoBERTa
    positive (0.96)
  • @nadia_reviews_gear oh great, the update broke Bluetooth AGAIN https://t.co/k3v9q2 (se abre en una pestaña nueva)

    VADER (compound)
    positive (+0.32)
    RoBERTa
    negative (0.86)

El sarcasmo vence a los dos puntuadores algunas veces. Los dos leen "Love that for me" como elogio; RoBERTa captó el sarcasmo del último tweet, donde VADER vio "great" y lo puntuó como positivo.

Regresión logística: entrénala con tus propias etiquetas

Un modelo TF-IDF con regresión logística aprende de tweets que etiquetas tú mismo, así que se ajusta a las palabras de tu tema: "sick" en un hilo de zapatillas, "bullish" en uno de cripto. Entrenado con 45,600 tweets de TweetEval en 13 segundos, llegó al 59.6%. Vale la pena cuando tienes unos miles de etiquetas y un vocabulario que los modelos generales no conocen.

Python
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline

model = make_pipeline(
    TfidfVectorizer(ngram_range=(1, 2), min_df=2,
                    sublinear_tf=True),
    LogisticRegression(max_iter=2000,
                       class_weight="balanced"),
)
model.fit(train_texts, train_labels)
df["lr"] = model.predict(df["clean"])

TextBlob, el cuarto método, puntúa un léxico de adjetivos en inglés que sus autores probaron con reseñas de películas, y llama neutral a un tweet solo cuando su polaridad es exactamente cero. Obtuvo 48.3%, lo mismo que llamar neutral a todos los tweets, así que no aporta nada con tweets. Un modelo de lenguaje grande es una quinta opción: si le das el tweet y las tres etiquetas, puede captar el sarcasmo que las listas de palabras se pierden, pero cuesta dinero por tweet y puede responder distinto en una nueva ejecución. Compruébalo con la misma muestra etiquetada a mano antes de confiar en él para un dataset completo.

#La banda neutral decide tu titular

Con VADER, el umbral que eliges mueve el resultado más que los propios tweets. Con la banda de ±0.05 de la documentación de VADER, llama positivos al 38.9% de los tweets de prueba; si amplías la banda a ±0.45, la proporción de positivos baja al 21.4% mientras la precisión sube al 58.1%. Mueve el control deslizante para ver cada paso.

-0.05 a +0.05
53.0% de 12.3K tweets etiquetados correctamente
38.9% calificados como positivos, frente al 19.3% de las personas
Positivo 38.9%
Neutro 31.3%
Negativo 29.8%

Barras: la proporción de VADER en cada etiqueta. Línea: la proporción que dieron los evaluadores humanos. Conjunto de prueba de sentimiento de TweetEval, 12.3K tweets, evaluado el 2 de nov de 2026.

Los mismos 12.3K tweets, evaluados una vez por VADER; solo cambia la banda neutra.

Reporta el método y su umbral con cada número que publiques, y nunca compares proporciones de dos puntuadores distintos. Si usas VADER, ajusta la banda con unos cientos de tweets de tu propio tema que hayas etiquetado a mano.

#Convierte las etiquetas en una puntuación que puedas seguir

El número que siguen la mayoría de los equipos es el sentimiento neto: la proporción de tweets positivos menos la de negativos, de -100 a +100. Cuéntalo por día y deja fuera los días con menos de unos 100 tweets, en los que un puñado de posts puede mover la puntuación.

Python
df["day"] = pd.to_datetime(df["created_at"]).dt.date
shares = pd.crosstab(df["day"], df["label"], normalize="index")
daily = pd.DataFrame({
    "tweets": df.groupby("day").size(),
    "net": ((shares["positive"] - shares["negative"]) * 100).round(1),
})
print(daily[daily["tweets"] >= 100])

Mantén separadas las respuestas y los posts originales (el campo is_reply), porque las quejas se concentran en las respuestas a los posts de una marca. Pondera los tweets por likes solo a propósito, y dilo junto al gráfico. Para mantener la puntuación al día, ejecuta la recopilación con un horario y since: fijado en la última ejecución; nuestra guía de monitoreo de Twitter en tiempo real cubre el sondeo a escala.

#Haz hoy tu primer análisis de sentimientos

Recopila unos miles de tweets sobre tu tema con POST /search-tweets de Sorsa API, unos 20 por solicitud desde $0.07 por 1,000, puntúalos con el modelo RoBERTa y comprueba las etiquetas en 200 tweets tuyos. Las 100 solicitudes gratis de una cuenta nueva cubren los primeros 2,000 tweets.

#Preguntas frecuentes

¿Qué tan preciso es el análisis de sentimientos en Twitter?

Depende del modelo. En 12,300 tweets etiquetados por personas (el conjunto de prueba de TweetEval), el modelo RoBERTa de Cardiff NLP etiquetó correctamente el 72.3%, una regresión logística entrenada con 45,600 tweets el 59.6%, VADER el 53.0% y TextBlob el 48.3%. El sarcasmo, las opiniones mixtas y la jerga del tema causan la mayoría de los errores, así que comprueba cualquier modelo con unos cientos de tweets de tu propio tema.

¿Cómo obtengo tweets para un análisis de sentimientos?

Búscalos mediante una API con una consulta y una ventana de fechas. POST /search-tweets de Sorsa API acepta los operadores de búsqueda de X, devuelve unos 20 tweets por solicitud con texto completo y autor, y cuesta desde $0.07 por 1,000 tweets. La API oficial de X necesita una cuenta de desarrollador y cobra $5.00 por 1,000 lecturas de posts. Para un conjunto ya etiquetado con el que entrenar, mira datasets públicos como TweetEval.

¿Qué API de Twitter debo usar para el análisis de sentimientos?

Usa Sorsa API para recopilar los tweets: devuelve unos 20 tweets por solicitud de búsqueda desde $0.07 por 1,000, frente a $5.00 en la API oficial de X, con toda la sintaxis de búsqueda de X hasta 2006, una clave API y ninguna cuenta de desarrollador, y 20 solicitudes por segundo en todos los planes. La puntuación se ejecuta después en tu propio código con el modelo que elijas.

¿Puedo hacer análisis de sentimientos en Twitter gratis?

Sí, para un primer proyecto. VADER, TextBlob y los modelos RoBERTa de Cardiff NLP son gratis y funcionan en una laptop. Para los tweets, cada cuenta nueva de Sorsa API recibe 100 solicitudes gratis: unos 2,000 tweets por búsqueda, sin tarjeta, y nunca vencen. Los conjuntos de referencia etiquetados, como TweetEval, se pueden descargar gratis para probar un modelo.

¿Puedo analizar tweets en otros idiomas?

Sí, con un modelo multilingüe. twitter-xlm-roberta-base-sentiment, de Cardiff NLP, se entrenó con unos 198 millones de tweets y se ajustó para sentimientos en 8 idiomas, entre ellos español, portugués, francés y árabe. VADER y el modelo RoBERTa en inglés leen solo inglés, así que filtra por idioma con el operador lang: en tu consulta y el campo lang de cada tweet.

¿Sorsa API devuelve una puntuación de sentimiento?

No: Sorsa API devuelve los tweets, con texto completo, idioma, conteos de engagement y autor, y la puntuación se ejecuta en tu código. Así la elección del modelo es tuya, y puedes volver a puntuar tweets viejos cuando salga un modelo mejor. Sorsa API se llamó TweetScout API hasta 2025; los endpoints de tweets y de búsqueda funcionan de la misma manera.

#Cómo lo comprobamos

El 2 de noviembre de 2026 descargamos los datos de sentimientos de TweetEval del repositorio de Cardiff NLP y puntuamos el conjunto de prueba de 12,300 tweets con VADER 3.3.2, TextBlob 0.20.1, un modelo TF-IDF con regresión logística (scikit-learn 1.9.1) entrenado con los 45,600 tweets de entrenamiento, y twitter-roberta-base-sentiment-latest (transformers 5.22.1, PyTorch 2.15 en 4 hilos de CPU); los ejemplos de salida de esta página salen de la misma ejecución. La forma de las solicitudes y las respuestas sigue la referencia de la API en vivo de Sorsa API, y los precios de la API oficial de X salen de docs.x.com, leído el mismo día.

Por el equipo de Sorsa API. Creamos y operamos Sorsa API, la API y scraper de Twitter/X de solo lectura, en producción desde 2022 (antes TweetScout API) y con más de 6 mil millones de solicitudes procesadas. Acerca de Sorsa API

Qué cambió
  • Reescrita: cuatro métodos puntuados en 12,300 tweets etiquetados por personas, un control deslizante del umbral de VADER, la forma de recopilar tweets en 2026 y los precios actuales de la API de X.
  • Primera publicación.