Cómo autenticarse en Meetup
No me gusta Meetup por muchos motivos, pero es un hecho que es la plataforma mayoritaria para organizar reuniones, al menos en las comunidades tecnológicas en las que estoy involucrado. Es curioso, precisamente las comunidades que tendrían capacidad técnica para no tener que depender de Meetup...
En fin, la humanidad es así.
Así que no me queda otra que tener automatismos que consumen datos extraídos de Meetup, como los calendarios de los encuentros. Hay grupos que requieren ser miembro para acceder a sus detalles, pero la mayoría están más que encantados de que sus reuniones sean públicas y se les dé difusión.
Todo fue bien hasta la primavera de 2023, cuando Meetup pasó a requerir acceso autenticado para poder acceder a todos los calendarios.
Les escribí varios correos electrónicos que no tuvieron ningún tipo de respuesta (¿se trataba de un error?), consulté el asunto en varios foros técnicos y todo el mundo estaba igual de perdido. La única alternativa que se proponía era hacer scraping a lo bruto de la web, lo cual es bastante fácil, ciertamente, pero me parecía una solución poco elegante y bastante frágil. La última opción.
En mi caso, preferir no utilizar un acceso autenticado no es una cuestión de privacidad sino de sencillez de programación. Puestos a programar y mantener un scraper, podía echarle un ojo a la autenticación en Meetup, que es bastante más complicada que simplemente enviar una cabecera HTTP.
Y, efectivamente, el código es complejo y con mucho JavaScript, pero la consola de red de Firefox fue de mucha ayuda.
En resumen, autenticarse en Meetup, al menos de momento, es muy fácil: se trata de una petición GraphQL que se extrae de forma trivial desde la consola de red de Firefox.
Aquí muestro mi fragmento de código Python para esto. No incluyo el programa completo porque no es código público (aún).
Lo primero es utilizar sesiones de la biblioteca requests para poder recoger y manejar las cookies HTTP con facilidad. También defino una cabecera HTTP User-Agent porque muchas webs deniegan el acceso si detectan que estás usando herramientas estándar como Python o wget. Al indicar un User-Agent poco común podemos tener suerte y volar bajo el radar.
# Para acumular aquí las cookies session = requests.session() session.headers = {'User-Agent': 'FUSION/1.0'}
La chicha está aquí:
# Esto es para coger las cookies session.get('https://www.meetup.com/', timeout=10) session.post('https://www.meetup.com/gql', timeout=10, headers={'content-type': 'application/json'}, data=r'{"operationName":"login","variables":{"input":{"email":"%s","password":"%s","rememberMe":true}},"query":"mutation login($input: LoginInput!) {\n login(input: $input) {\n verificationRequired\n memberId\n reverifyToken\n error {\n ...Error\n __typename\n }\n __typename\n }\n}\n\nfragment Error on PayloadError {\n code\n message\n field\n __typename\n}\n"}' % (config['meetup']['usuario'], config['meetup']['clave']))
Necesitamos la primera petición para obtener una cookie HTTP de sesión en Meetup, que usaremos en peticiones subsiguientes (esto lo hace el objeto session de requests automáticamente). La madre del cordero es la segunda acción, una petición GraphQL bastante delicada que debemos usar tal cual, sin modificar ni una coma, ni un retorno de carro ni un espacio aparentemente superfluo. Con ella nos autenticamos ante Meetup y, a partir de ese momento, los accesos usando ese objeto session incluirán las credenciales de autenticación y autorización, y podremos hacer peticiones web normales sin tener que volver a preocuparnos del asunto.
Al menos hasta que Meetup haga cambios en su web.