Solo existen dos maneras de controlar una cuenta de Twitter, que son mediante una aplicación de Twitter (web, app movil) o mediante el API de Twitter. Todos los scripts que pongo en este blog son para el API de Twitter, todos los bots que existen también.
Para que te concedan acceso al API de Twitter, debes ir a developer.twitter.com y solicitarlo mediante unos formularios, donde te preguntarán para qué lo vas a usar, etc…
El problema es que cada vez están poniendo más pegas para concederlo. Si ven alguna pega en la cuenta con la que intentas acceder al API (cuenta de procedencia extraña, muy reciente, etc…) o bien no contestas lo que quieren leer en el formulario de acceso; no te la conceden.
Entonces, si quieres hacer un bot, sin acceso al API, ¿qué se puede hacer?
Pues usando la otra alternativa: Usando la web de Twitter en un navegador; pero no manejada por nosotros presencialmente, sino controlada mediante Python con Selenium y XPath.
Los requisitos son tener un ordenador que esté encendido todo el tiempo, donde ejecutaremos un Chrome que navegará por Twitter, y ese Chrome estará controlado por Selenium al que le enviaremos ordenes regularmente como a cualquier bot. No podrá hacer las virguerías que se pueden hacer con el API de Twitter, pero podrá hacer bastantes cosas.
Básicamente podrá hacer lo mismo que hacemos nosotros en la web o app de Twitter: Navegar a timelines, perfiles, listas, etc… y una vez en el sitio, usamos XPath para encontrar los datos que queremos leer o los botones que queremos pulsar.
Vamos a hacer como ejemplo un bot que simplemente le da like o retuitea el tweet más reciente de su timeline, y sigue a un perfil de los que Twitter propone en “A quién seguir”.
Lo primero es instalar Selenium en nuestro proyecto en Python. A continuación necesitamos el driver del navegador en el sistema operativo que vayamos a usar. Por mi experiencia, el driver de Firefox falla en ciertas cosas, así que usaremos el de Chrome, que se obtiene aquí. La idea, es que Selenium abrirá una instancia de Chrome manejada por él, al que enviaremos nuestras ordenes. El primer problema que encontramos, es que esa instancia no guarda settings, por lo que cada vez que entremos, necesitaremos logearnos en Twitter. Esto lo podríamos automatizar también con el propio Selenium (como se explica en este Gist, por ejemplo), pero como solo lo vamos a hacer una vez, lo podemos hacer a mano, y a partir de ahí, en cuanto nuestro timeline salga, ya manejar la cuenta a base de programación.
El siguiente problema que nos encontraremos es la sesión. Necesitamos guardarla y reutilizarla, sino, Selenium abrirá un navegador nuevo por cada orden que enviemos. Pero esto se resuelve fácilmente guardando la URL y el ID de sesión en un fichero de texto.
Empezamos:
import time
import os
import random
import datetime as dt
from selenium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException, StaleElementReferenceException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
DRIVER_BIN = os.path.join(PROJECT_ROOT, "chromedriver")
def attach_to_session(executor_url, session_id):
original_execute = WebDriver.execute
def new_command_execute(self, command, params=None):
if command == "newSession":
return {'success': 0, 'value': None, 'sessionId': session_id}
else:
return original_execute(self, command, params)
WebDriver.execute = new_command_execute
driver = webdriver.Remote(command_executor=executor_url, desired_capabilities={})
driver.session_id = session_id
WebDriver.execute = original_execute
return driver
Lenguaje del código: JavaScript (javascript)
Establecemos las variables del driver y usaremos una función para asignar la misma sesión a cada instancia de navegador que Selenium intente abrir, de esa forma evitamos el problema descrito antes. Ahora vamos a ver qué funciones va a tener el bot. La primera va a ser darle like al primer tweet de su timeline:
def like(browser):
print("like")
like_button = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[@data-testid='like']")))
like_button.click()
Lenguaje del código: PHP (php)
Muy sencilla. Usamos WebDriverwait con 10 segundos de timeout hasta que la página se cargue y el primer botón con la propiedad “data-testid” con el valor “like” esté presente. Ese será el botón de like del primer tweet de nuestro timeline. Y le damos click. Y ahora, la siguiente función es hacer retweet:
def retweet(browser):
print("retweet")
retweet_button = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[@data-testid='retweet']")))
retweet_button.click()
# now, confirm the retweet:
retweet_confirm = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[@data-testid='tweetButton']")))
retweet_confirm.click()
Lenguaje del código: PHP (php)
Esta es algo más compleja porque tiene dos fases: La primera es darle al botón “retweet” del primer tweet del timeline, entonces aparecerá el diálogo donde sale el tweet que queremos retuitear y la zona de escritura por si queremos añadir un comentario, cuando salga (volvemos a esperar con WebDriverWait), buscamos el botón de confirmar “tweetButton” y le damos click. Y la tercera función de nuestro bot, será seguir a uno de los usuarios recomendados por Twitter según nuestros gustos:
def follow(browser):
print("follow")
wait = WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.XPATH, "//div[contains(@data-testid, '-follow')]")))
browser.find_elements_by_xpath("//div[contains(@data-testid, '-follow')]")[1].click()
Lenguaje del código: PHP (php)
La idea es que Twitter suele recomendar 3 usuarios a los que deberías seguir. Pero el primer usuario, suele ser un usuario patrocinado, que nada tiene que ver con nuestros gustos. Por eso usamos el “[1]” en el selector, ya que escogemos el segundo y le damos al botón de seguir.
Una vez tenemos las 3 órdenes, hacemos el cuerpo del bot:
hh = dt.datetime.now().hour
s = random.randint(1, 3)
if (hh>=9 and hh<24):
session = ""
cc = os.path.join(PROJECT_ROOT, "session.txt")
with open(cc, 'r') as file:
session = file.read()
if session == "":
browser = webdriver.Chrome(executable_path=DRIVER_BIN)
browser.get("https://twitter.com/home")
with open(cc, 'w+') as fh:
executor_url = browser.command_executor._url
session_id = browser.session_id
fh.write(executor_url+"|"+session_id)
else:
v = session.split("|")
browser = attach_to_session(v[0], v[1])
browser.get("https://twitter.com/home")
try:
if s == 1:
like(browser)
elif s == 2:
retweet(browser)
else:
follow(browser)
except TimeoutException:
print("Está tardando demasiado")
Lenguaje del código: PHP (php)
Hacemos que el bot solo funcione desde las 9 de la mañana hasta las 23, para que parezca “más humano”. Y en cada llamada al script, hacemos que ejecute una de las tres ordenes aleatoriamente. En el código se ve como cargamos y grabamos los datos de la sesión en un fichero de texto.
Yo he tenido un bot funcionando dos semanas ininterrumpidamente con este script y nunca se detuvo, ni Twitter sacó ningún evaluador de captcha ni nada, por lo que pasa como humano.
Deja una respuesta