Green Crack
Optimo Sweet
Red Bull

Twitter "Like" Bot

I was on Twitter and became angry after seeing a website charge for a course that teaches how to "like" using Python and Selenium. Yes, the exact same thing this tutorial teaches for free. First off, do not pay for this type of course! There's tons of documentation available to learn on your own. This post is a breakdown of my personal and professional use of Selenium.

Full script available here.

Step 1: Requirements

Step 2: Gather Elements

I prefer to find elements using iPython and then wrap them into classes. Here's an example:

In [1]: from selenium import webdriver

In [2]: driver = webdriver.Chrome()

In [3]: driver.get('http://twitter.com')
drive
In [4]: username = driver.find_element_by_name('session[username_or_email]')

In [5]: username.send_keys('test-user')

As you can see, I've found the username input field and successfully sent test-user to the element, so I add username as an attribute on the Locator class. I've done the same for all elements that I'll interact with throughout this example

class TwitterLocator:
    username = (By.NAME, "session[username_or_email]")
    password = (By.NAME, "session[password]")
    submit_btn = (By.CLASS_NAME, "js-submit")
    search_input = (By.ID, "search-query")
    search_btn = (By.ID, "nav-search")
    tweets = (By.CLASS_NAME, "js-stream-item")
    like_btn = (By.CLASS_NAME, "HeartAnimation")
    latest_tweets = (By.PARTIAL_LINK_TEXT, 'Latest')

Step 3: Define Constants

I've identified the below three attributes as constant variables (not required - I prefer the organization):

class Constants:
    USERNAME = os.environ.get("TWITTER_USERNAME")
    PASSWORD = os.environ.get("TWITTER_PASSWORD")
    GLOBAL_ENTRY_Q = '#globalentry'

Step 4: Bot

Now that we have the above out of the way, let's create the Bot class to piece this all together.

class LikeBot(object):

    def __init__(self):
        self.locator_dictionary = TwitterLocator.__dict__
        self.browser = webdriver.Chrome() 
        self.browser.get(URL.TWITTER)
        self.timeout = 10

Take a look at locator_dictionary. It's a dictionary of TwitterLocator attributes, attached to the LikeBot class. We've basically given LikeBot the same attributes as TwitterLocator (as a variable). More on this later...

It's important to instantiate with a webdriver, to visit the url intended (http://twitter.com) in this case, and to set the timeout (in seconds).

Getattr

From here we can start creating methods, such as login, that will login to an account as long as the browser is on the login page. A convenient. syntax-smart way of writing methods is to include a find_element along with a __getattr__ method that looks at the locator dictionary.

    def _find_element(self, *loc):
        return self.browser.find_element(*loc)
        
    def __getattr__(self, what):
        try:
            if what in self.locator_dictionary.keys():
                try:
                    element = WebDriverWait(self.browser, self.timeout).until(
                        EC.presence_of_element_located(self.locator_dictionary[what])
                    )
                except(TimeoutException, StaleElementReferenceException):
                    traceback.print_exc()

                try:
                    element = WebDriverWait(self.browser, self.timeout).until(
                        EC.visibility_of_element_located(self.locator_dictionary[what])
                    )
                except(TimeoutException, StaleElementReferenceException):
                    traceback.print_exc()
                # I could have returned element, however because of lazy loading, I am seeking the element before return
                return self._find_element(*self.locator_dictionary[what])
        except AttributeError:
            super(LikeBot, self).__getattribute__("method_missing")(what)

    def method_missing(self, what):
        print "No %s here!" % what

Now we can write methods using the below syntax in the methods:

    def login(self, username=Constants.USERNAME, password=Constants.PASSWORD):
        self.username.send_keys(username)
        self.password.send_keys(password)
        self.submit_btn.click()

I created a run method to handle all actions as one:

    def run(self):
        self.login()
        self.search()
        self.view_latest_tweets()
        time.sleep(2)
        self.like_tweet()
        self.browser.quit()

Notice the time.sleep() line – this is only to wait a few seconds before executing the next lines (because changing between tabs isn't always instantaneous)

See Full Script