Veja o código completo atualizado e uma demonstração!

O Selenium é um framework que consegue automatizar ações no seu navegador, então podemos utilizá-lo para fazer tarefas online em sites complexos, que executam Javascript e outras tecnologias, que outros frameworks como o Scrapy e Beautiful Soup não conseguem, pois tratam apenas de HTML.

Configurando o ambiente no Linux

Veja para Windows ou Mac OS

Primeiro, é necessário que você crie um ambiente virtual do Python em seu sistema, desse modo, você consegue isolar os pacotes instalados e garantir que a execução ocorra normalmente, sem ter chance de entrar em conflito com outros pacotes instalados do Python. Em seu terminal faça:

virtualenv --python=python3.7 venv
source venv/bin/activate

Desse modo, aparecerá (venv) no canto mais à esquerda da tela, isso significa que o ambiente virtual está sendo utilizado e que podemos instalar pacotes do Python de forma isolada.

pip install selenium

Possivelmente será necessário você instalar o webdriver correspondente ao seu navegador, no meu caso:

apt install firefoxdriver

Infelizmente, precisamos instalar o Geckodriver de forma manual em alguns sistemas, no caso, podemos executar em um Linux:

1
2
3
wget https://github.com/mozilla/geckodriver/releases/download/v0.29.1/geckodriver-v0.29.1-linux64.tar.gz
tar xf geckodriver-v0.29.1-linux64.tar.gz
sudo mv geckodriver /usr/bin/

Mão na massa do Selenium

Tenha em mente que realizaremos diversos passados para realizar uma publicação no Twitter utilizando o Selenium, por isso, é muito importante segmentarmos o código o máximo possível, onde teremos vários métodos. Como nosso projeto é algo simples, teremos apenas uma classe. Por mais simples que seja, é importante aplicarmos POO, pois podemos fazer o reúso do software, o que aumenta cada vez mais a velocidade de implementação de tarefas.

Primeiro passo: Criando o esboço

Primeiro vamos apenas importar as bibliotecas utilizadas:

1
2
3
4
5
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from sys import argv

Agora podemos criar nossa classe e os métodos que vamos precisar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Tweet():
    def __init__(self, username, password,
                 text="Fui automatizado! Confira: https://lcsvillela.github.io/publicando-tweet-com-python.html"):
        self.__username = username
        self.__password = password
        self.__text = text
        self.__driver = webdriver.Firefox()
        self.__wait = WebDriverWait(self.__driver, 30)
        self.__actions = ActionChains(self.__driver)
        self.__locations = {'user': 'session[username_or_email]',
                            'password': 'session[password]',
                            'button_login': '//form/div/div[3]',
                            'button_tweet': "//*[@data-testid='tweetButtonInline']"}
        self.start()
	
    def login_twitter(self):
        pass
	
    def make_tweet(self):
        pass
	
    def get_field_text(self):
        pass
    
    def start(self):
        self.login_twitter()
        self.make_tweet()

tweet = Tweet(argv[1], argv[2], argv[3])

Na linha 2 criamos nosso _init_, é um método que é executado assim que a classe é instanciada, então podemos colocar os atributos definidos por lá, algo que nem sempre é feito, é o “self.__” que ajuda muito na leitura e identificação rápida de um atributo no seu programa, sem o risco de confundir com um método! Nele, definimos o recebimento de alguns parâmetros, sendo que um deles pode ser omitido no momento de instanciação da classe, que é o text, que conta com um valor padrão.

Nas linhas de 4 a 6, fazemos com que os atributos recebam os valores das variáveis, isso faz com que nossa classe seja isolada do resto do programa, sem que seja possível alterar os atributos diretamente, o que pode gerar problemas.

Nas linhas 7 a 9, iniciamos a nossa instância do nevagador que será automatizado, definimos um padrão para o WebDriverWait e para as ActionChains (lembre-se do DRY).

Nas linhas 10 a 13, cria-se uma estrutura de dicionário em Python, para centralizar as informações dos identificadores dos campos de usuário, senha, campo de texto e botão do tweet, veja aqui como descobrir essa informação na página. Na 13 é onde nosso programa é executado.

Nas linhas 16, 19 e 22 , criamos nossos métodos, por enquanto, eles não fazem nada (com exceção do 25 que chama os outros) e é para isso que serve o pass nesse caso, apenas fechar um buraco de um método ou função que será implementado. A útilma linha é algo que torna conveniente o uso do programa, não precisamos alterar o nome de usuário, senha e texto para o tweet toda hora, basta chamar o programa com os dados na frente nessa ordem!

Mas você já pode executar nosso programa! Ele não retornará nada, mas muitas coisas já aconteceram :)

python tweet.py usuario senha "O que você quiser publicar"

Segundo passo: Fazendo o Login

Para chegar nas informações do Xpath, CSS Selector, CSS Path ou em algum outro jeito que seja possível identificar de forma única um elemento de uma página web, você precisa se debruçar no código fonte da página, no caso do twitter, a empresa faz com que elementos de sua página sejam aleatórios, tenham restrições de interação e outros jeitos de tentar impedir a gente de automatizar uma das maiores redes sociais do mundo.

Porém, a parte de login é a mais tranquila, nosso método fica:

1
2
3
4
5
6
7
8
9
def login_twitter(self):
    self.__driver.get("https://twitter.com/login")
    self.__wait.until(EC.presence_of_element_located((By.NAME, self.__location['username'])))
    self.__wait.until(EC.presence_of_element_located((By.NAME, self.__location['password'])))
    field_user = self.__driver.find_element_by_name(self.__location['username'])
    field_user.send_keys(self.__username)
    field_pass = self.__driver.find_element_by_name(self.__location['password'])
    field_pass.send_keys(self.__password)
    self.__driver.find_element_by_xpath(self.__location['button_login']).click()

A segunda linha, o webdriver utiliza sua função get para acessar a página, na terceira e quarta, definimos que seja aguardado o carregamento dos elementos que iremos interagir, é pouco recomendado a utilização da biblioteca time.sleep, pois não existem garantias de tempo. Nas linhas 5 e 6, localizamos o elemento para inserir o nome do usuário e logo depois enviamos os dados, o memso é feito com o campo de senha nas linhas 7 e 8. Por fim, realizamos um click no botão de login. Fácil, não é mesmo?

Terceiro passo: Fazendo o tweet

Aqui foi necessário criar dois métodos para dar conta de um problema, porque na realidade, são dois problemas diferentes:

0 - Ao analisar cuidadosamente, percebe-se que o campo de inserção de texto para o tweet tem uma ID, algo muito valioso quando estamos utilizando o Selenium, pois um Xpath pode mudar do dia para a noite, mas um ID geralmente fica um bom tempo. Porém o ID deste elemento do Twitter tem uma parte estática e outra variável. 1 - Realizar a inserção do texto e o clique no botão de tweet

def get_field_text(self):
    code = self.__driver.page_source.split('"')
    return [x for x in code if 'placeholder-' in x][0]

A primeira linha nos faz o favor de trazer o código fonte da página, depois com o método split, fatiamos o código todo e assim obtemos um vetor. De posse desse vetor, na linha 2 fazemos um laço for que filtra os elementos do vetor que interessam e assim obtemos o nosso ID.

1
2
3
4
5
6
7
def make_tweet(self):
    self.__wait.until(EC.presence_of_element_located((By.XPATH, self.__location['button_tweet'])))
    ID = self.get_field_text()
    self.__wait.until(EC.element_to_be_clickable((By.ID, ID)))
    field_text = self.__driver.find_element_by_id(ID)
    self.__actions.move_to_element(field_text).click(field_text).send_keys(self.__text).perform()
    self.__driver.find_element_by_xpath(self.__location['button_tweet']).click()

Na linha dois, novamente utilizamos as ferramentas de análise para esperar a página carregar com sucesso, na linha três executamos o método que busca o ID e armazenamos ele, logo em seguida na linha quatro, utilizamos o verificador da situação da página para saber se o elemento é clicável. Na linha cinco fazemos a localização do elemento, na linha 6 utilizamos a ferramenta de ações do Selenium, que nos possibilita emular movimentos mais complexos, sendo assim, enviamos nosso texto para o campo e na última linha, finalmente clicamos no botão para tweetar!

Conclusões, observações e recomendações

Incrível o que em apenas algum tempo podemos fazer, não é? De posse deste programa, podemos publicar qualquer coisa no Twitter de forma automatizada, não precisamos nos expor a uma ferramenta que causa problemas psicológicos, pois podemos fazer isso pelo terminal e até mesmo agendada utilizando o cron!

As ideias afloram, mas algo que é de suma importância é a necessidade de criarmos um código limpo, que facilite sua manutenção e potencialize sua reuzabilidade! Este código é uma biblioteca que permite a publicação no twitter, uma das várias funções da rede social, para extender este código basta realizar um import dela e utilizar as propriedades da programação orientada a objetos. Quem sabe…pode ser nosso birdpy?

Veja como ficou o código completo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
''' Criado por www.lcsvillela.github.io
www.instagram.com/lcsvillela
www.twitter.com/lcsvillela
www.github.com/lcsvillela '''

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from sys import argv


class Twitter():
    def __init__(self, username, password,
                 text="Fui automatizado! Confira: www.github.io/publicando-tweet-com-python.html"):
        self.__username = username
        self.__password = password
        self.__driver = webdriver.Firefox()
        self.__text = text
        self.__wait = WebDriverWait(self.__driver, 30)
        self.__actions = ActionChains(self.__driver)
        self.__location = {'username': 'session[username_or_email]',
                           'password': 'session[password]',
                           'button_login': '//form/div/div[3]',
                           'button_tweet': "//*[@data-testid='tweetButtonInline']"}
        self.start()

    def login_twitter(self):
        self.__driver.get("https://twitter.com/login")
        self.__wait.until(EC.presence_of_element_located((By.NAME, self.__location['username'])))
        self.__wait.until(EC.presence_of_element_located((By.NAME, self.__location['password'])))
        field_user = self.__driver.find_element_by_name(self.__location['username'])
        field_user.send_keys(self.__username)
        field_pass = self.__driver.find_element_by_name(self.__location['password'])
        field_pass.send_keys(self.__password)
        self.__driver.find_element_by_xpath('//form/div/div[3]').click()

    def make_tweet(self):
        self.__wait.until(EC.presence_of_element_located((By.XPATH, self.__location['button_tweet'])))
        ID = self.get_field_text()
        self.__wait.until(EC.element_to_be_clickable((By.ID, ID)))
        field_text = self.__driver.find_element_by_id(ID)
        self.__actions.move_to_element(field_text).click(field_text).send_keys(self.__text).perform()
        self.__driver.find_element_by_xpath(self.__location['button_tweet']).click()

    def get_field_text(self):
        code = self.__driver.page_source.split('"')
        return [x for x in code if 'placeholder-' in x][0]

    def start(self):
        self.login_twitter()
        self.make_tweet()


Twitter(argv[1], argv[2], argv[3])