Add friends on facebook (python)

This is the follow up to my first selenium post which gave a good example of how one could scrape facebook with python. If we go more into detail, the first post gave an example of how to invite friends to your facebook page with python using selenium package. As this was a good exercise, I wanted to do a follow up. The following python code mainly uses selenium with a bit of help from BeautifulSoup, which by the way is another great web scraping tool which I will start to use more often.

The code might be pretty straight forward for someone who’s familiar with python and selenium, but if you are not sure about something you can look for explanation below. Also, if you are new to python world, I wrote a quick python setup on windows tutorial in which you can look into if you want to quickly setup python environment.

By the way, when I was writing this I encountered a number of new ideas where I could go with selenium and beautifulsoup combined together. Here’s a video example of how the script actually works:

Requirements:
* Install BeautifulSoup4 pip install beautifulsoup4
* Install win32api, win32con pip install pypiwin32
* Install selenium pip install selenium
* Include type_text.py in project’s folder
* Download and install Mozilla Firefox (some versions doesn’t work with selenium, I used 41.0.1)
* Use MousePos.exe to adjust coordinates if necessary
* Set your facebook user’s language to English (US)

Whole project can also be downloaded here

You might wonder why there’s a click function for mouse click if example is not using any. Well, I left this in here for those who want to adjust the code. It’s not always possible to get the element that you want to click with selenium. However, beautifulsoup, if used correctly may open a lot more possibilities.

from selenium import webdriver
import win32api, win32con
import time
import unittest
import datetime
import type_text as keyboard
from bs4 import BeautifulSoup

def click(x,y):
    win32api.SetCursorPos((x,y))
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,x,y,0,0)
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,x,y,0,0)

facebook = "https://www.facebook.com/"
emails = ['youremail@gmail.com']
passwords = ['yourpassword']
friends_to_add = 5

class GetAttributeTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.maximize_window()
        self.driver.get(facebook)
        time.sleep(3)

    def test_GetAttribute(self):
        driver = self.driver
        for index in  xrange(0, len(emails)):
            print 'User enters email "%s", password "%s" and clicks on "Login" button' % (emails[index], passwords[index])
            email = driver.find_element_by_id('email')
            email.send_keys('%s' % emails[index])
            password = driver.find_element_by_id('pass')
            password.send_keys('%s' % passwords[index])
            driver.find_element_by_id('loginbutton').click()
            time.sleep(3)
            print "Redirecting to profile page"
            driver.get("https://www.facebook.com/profile.php")
            time.sleep(3)
            friends_amount = int(driver.find_element_by_xpath("//span[@class='_gs6']").text)
            print "Current user has %s friends" %friends_amount
            driver.find_element_by_link_text("Friends").click()
            time.sleep(3)

            print "Choosing scroll down amount depending on current user's friends amount"
            press_down_amount = friends_amount
            for press in xrange(0, press_down_amount):
                keyboard.Press("DOWN")

            html = driver.page_source
            soup = BeautifulSoup(html, 'html.parser')

            new_user_profiles = []
            for link in soup.findAll("a", { "class" : "_39g5" }):
                new_profile = link.get('href')
                if "facebook" in new_profile:
                    new_user_profiles.append(new_profile)

            print "Amount of user profiles displayed: %s" %len(new_user_profiles)

            if len(new_user_profiles) > 120:
                random_number = int(datetime.datetime.now().minute) * 2
            elif len(new_user_profiles) > 180:
                random_number = int(datetime.datetime.now().minute) * 3
            elif len(new_user_profiles) > 240:
                random_number = int(datetime.datetime.now().minute) * 4
            elif len(new_user_profiles) > 300:
                random_number = int(datetime.datetime.now().minute) * 5
            elif len(new_user_profiles) > 360:
                random_number = int(datetime.datetime.now().minute) * 6
            elif len(new_user_profiles) > 420:
                random_number = int(datetime.datetime.now().minute) * 7
            elif len(new_user_profiles) > 480:
                random_number = int(datetime.datetime.now().minute) * 8
            elif len(new_user_profiles) > 540:
                random_number = int(datetime.datetime.now().minute) * 9
            elif len(new_user_profiles) > 600:
                random_number = int(datetime.datetime.now().minute) * 10
            elif len(new_user_profiles) > 660:
                random_number = int(datetime.datetime.now().minute) * 11
            else:
                random_number = int(datetime.datetime.now().minute)

            random_profile = new_user_profiles[random_number]
            print "Redirecting to random profile: %s" %random_profile
            driver.get(random_profile + "_suggested")
            time.sleep(3)

            html = driver.page_source
            soup = BeautifulSoup(html, 'html.parser')
            mutual_friends = []
            for link in soup.findAll("a", { "class" : "_39g5" }):
                mutual_link = link.get('href')
                if "mutual" in mutual_link:
                    mutual_friends.append(mutual_link)
            print "Found %s mutual friend links" %len(mutual_friends)

            if len(mutual_friends) > friends_to_add:
                try:
                    for friend_id in range(0, friends_to_add):
                        try:
                            driver.find_elements_by_css_selector("button._42ft._4jy0.FriendRequestAdd.addButton._4jy3._517h._51sy")[friend_id].click()
                            time.sleep(.7)
                        except:
                            print "Couldn't find 'Add friend' button or error was displayed"
                            keyboard.Press("ENTER")
                except:
                    pass
            else:
                print "Amount of mutual friends was less than you wanted to add"

            driver.get(facebook)
            time.sleep(3)
            driver.find_element_by_id('pageLoginAnchor').click()
            time.sleep(3)
            driver.find_element_by_link_text('Log Out').click()
            time.sleep(3)

if __name__=='__main__':
    unittest.main()

Important!

I would suggest to use small amount of friends you add. The more often you’ll add random friends, the more often facebook will get suspicious. It might start with facebook displaying notification messages that you are adding random friends and it might end with them removing your account, so be careful. You shouldn’t use your real profile with this.

Most of the code is simple and has been already explained by print messages, but I will try my best to explain every part below for those who want to completely understand what’s going on.


Add facebook accounts

emails = ['youremail@gmail.com']
passwords = ['yourpassword']
friends_to_add = 5

Here you can define facebook accounts that you want to use in the script as well as passwords and amount of friends you want to add to each account. Email index must match with password index. For example youremail@gmail.com will use yourpassword to login.


Choose webdriver

self.driver = webdriver.Firefox()
self.driver.maximize_window()
self.driver.get(facebook)

I used to use chromedriver for selenium, but recently found out that firefox actually works better. maximize_window() makes sure that firefox is maximized as soon as started. With driver.get(facebook) user is redirected to facebook’s landing page. facebook variable is defined at the beginning of the script.


Use each email

for index in  xrange(0, len(emails)):
    email = driver.find_element_by_id('email')
    email.send_keys('%s' % emails[index])
    password = driver.find_element_by_id('pass')
    password.send_keys('%s' % passwords[index])

For loop is simply necessary if you have more than one facebook account defined in the emails and passwords lists. For loop is defined in range from 0 to length of emails list.

Finding elements and input fields for selenium is not always as easy as with facebook’s email and password fields. It gets complicated when we want to do more advanced scraping. Facebook’s field element id can be found if you right click on the field and select “Inspect” or “Inspect element” in your web browser.


Click on login button

    driver.find_element_by_id('loginbutton').click()
    time.sleep(3)

If you want to click on element with selenium, at first you have to find that element and then apply click() action to it. I would suggest to use time.sleep(3) after each time website is refreshed or user is redirected to new page, just to make sure everything is fully loaded before you try to click on another page element.


Find current user’s friends amount

            friends_amount = int(driver.find_element_by_xpath("//span[@class='_gs6']").text)
            print "Current user has %s friends" %friends_amount

I wanted to calculate scroll down amount which I was using on user’s friends page, depending on how many friends current user has. Because of that I had to figure out a way to scrape displayed number of friends each time user has logged in. With above code we’re looking for span element _gs6 and saving it’s text to friends_amount variable.

add-friends-on-facebook-python


Find element by text

driver.find_element_by_link_text("Friends").click()
time.sleep(3)

Sometimes you can simply find link by it’s text and click on on it, but it happens rarely if you are working with dynamic websites like facebook.


Completely load friends page

press_down_amount = friends_amount
for press in xrange(0, press_down_amount):
    keyboard.Press("DOWN")

In order to randomly choose one friend out of all you have to make sure that all of the friends are loaded in page. Quick solution was to simply scroll down to the bottom of the page. Like I mentioned before, I wanted to set amount of times to press down depending on how many friends the current user has. Above code works great with variety of friend quantities.


Selenium to BeautifulSoup

html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')

Once all current user’s friends were loaded the next challenge was to scrape links from their profiles. Selenium stores the source HTML in the driver’s page_source attribute. Once we have that we can load html into soup using BeautifulSoup(html, ‘html.parser’)


Find links

new_user_profiles = []
for link in soup.findAll("a", { "class" : "_39g5" }):
    new_profile = link.get('href')
        if "facebook" in new_profile:
            new_user_profiles.append(new_profile)

new_user_profiles is a list where all the links will be stored. soup.findAll(“a”, { “class” : “_39g5” }) returns a list of all a tag elements which include class _39g5. You can see in the screen shot below that “527 friends” link holds href with the actual link to the facebook profile. Basically, for each link in the soup.findAll list we’re using beautifulsoup’s .get(‘href’) method to apply only href to the new_profile variable.

We use if statement just to make sure that link that’s applied to new_profile variable actually holds keyword facebook and only then if this statement is True we append profile link to the list.

add-friends-on-facebook-python-1


Calculate randomness

if len(new_user_profiles) > 120:
    random_number = int(datetime.datetime.now().minute) * 2
else:
    random_number = int(datetime.datetime.now().minute)
 
random_profile = new_user_profiles[random_number]
print "Redirecting to random profile: %s" %random_profile
driver.get(random_profile + "_suggested")
time.sleep(3)

You could simply import random and use random.choice(new_user_profiles) to randomly choose one link from the list, but in my experience this usually is not the best practice. In my opinion, the best way to select something randomly is to depend on real time. In this example, to select random_number current time in minutes is used and increased if the current user’s number of friends is larger than 120. Once random_profile is selected, we apply “_suggested” string at the end of the link in order for user to be redirected to random profile’s suggested friends page.


Add facebook friends

if len(mutual_friends) > friends_to_add:
    try:
        for friend_id in range(0, friends_to_add):
            try:
                driver.find_elements_by_css_selector("button._42ft._4jy0.FriendRequestAdd.addButton._4jy3._517h._51sy")[friend_id].click()
                time.sleep(.7)
            except:
                print "Couldn't find 'Add friend' button or error was displayed"
                keyboard.Press("ENTER")
    except:
        pass
else:
    print "Amount of mutual friends was less than you wanted to add"

Once user has arrived on mutual friends page we have to look for “Add friend” buttons. It took a while and in the end selenium’s find_elements_by_css_selector method was one that worked in this case. Basically, each button has its own index (friend_id) and we’re using for loop to click “Add friend” buttons in range from 0 to friends_to_add. If and exception statements are there just make sure there is something to click on and in case facebook display error message, script can skip that too.


Log out from facebook

driver.get(facebook)
time.sleep(3)
driver.find_element_by_id('pageLoginAnchor').click()
time.sleep(3)
driver.find_element_by_link_text('Log Out').click()
time.sleep(3)

Finally, in order for next profile to be able to login you have to log out first. The code is pretty straight forward, user is redirected to facebook’s main page just to be able to always locate the little triangle which triggers the drop down menu. Once drop down menu is visible user clicks on “Log out” text.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s