and How?The task at hand is to build a “bot” of sorts that possesses the ability to share a given post to LinkedIn.

Before tackling any situation, it is wise to:understand where one isunderstand what he or she is trying to accomplishhave some rough estimation of how this will be doneThe intro already blew through Step 1 (hell) and Step 2 (spend as little time here as possible); now, how to get the situation sorted?Well, based on the title, it will involve Selenium WebDriver (Selenium 1.

0 + WebDriver = Selenium 2.

0), a web automation framework; so what are the steps one would take if posting to LinkedIn via the web?Rough EstimationOpen a browserNavigate to https://LinkedIn.

comSign In to an AccountRecognize and click the “Start a post” buttonType up (or otherwise) the content to be displayedFocus and click the ‘Post’ buttonMaking it HappenOkay, seems simple enough.

So how would Selenium go about this?Not all that differently, as it turns out, there is a bit of fine print humans mask with a GUI that Selenium insists on documenting in the Captain’s Log –remind me to remind you of this later on– but this is programming, so what did you expect?LinkedFrom — ImportsAs with all good shenanigans, go ahead and start with;from time import sleepThen it might be a decent idea to call in the package being explored;from selenium import webdriverNext, as this tutorial is to make life easier, opt to code with masks (Keys) like CONTROL, ALT , or DELETE instead of more traditional 'ue009' , ‘ue00a’ , or ‘ue017' .

Here’s the full list of Keys this import grants access to.

from selenium.



keys import KeysThird, make a separate file to store your account information (username and password), mine is called userinfo.

py looks like:with ‘__OPT-OUT__’ later mentioned (in main file) as an option to input email/phone number and password when the script is called.

Finally, back in the main file, bring in that account info;from userinfo import user, pwrd“And they’re off!”Now, to get to work coding away these pesky teens and their awkward attempts to share literally anything that generates attention.

Just kidding, Instagram will be covered next week; we’re here to save you time and frustration when sharing your content, ideas, and abilities to LinkedIn.

As WebDriver is an Object-Oriented API, the coding of this script will follow suit and move forward in the OOP paradigm.

LinkedSelf — __init__(username, password, driver)With the exact outcome of the next few minutes still unclear, but there being the rough estimation to rely on, we can worry about naming and explaining later.

Right now, bringing the computer up to speed with a few base assumptions, so it has the data necessary to complete the task, is priority #1.

Of these info bits, the most vital include:Username for logging inPassword for logging inBrowser being operatedBrowser vs DriverHere arises an important note: Selenium utilizes a WebDriver to interact with a matching Browser; that is to say Selenium does not control a Browser, but, much like yourself when making use of your thumbs or a mouse, Selenium communicates with a Browser through a Driver.

So rather than a Browser, the attribute being passed though should be a Driver (e.


Gecko, Chrome, Edge), which will call upon its paired Browser for collaboration.

Setting Up ShopWith the understanding of a Driver in mind, kick things off with a little;def __init__(self, username, password, driver):and continue mapping out initialization of username,password, and driver until the current product is akin to:Note: the post is not yet in play, and the notes and docstrings are optional.

LinkedKey — login()Any good negotiator knows that once he or she is in, the world of possibilities will present itself.

Other than the target, which is a hard variable (same across situations), we have equipped Selenium with the information necessary to act on our behalf, summon a Browser, and log in to LinkedIn.

get( the Page )To load a webpage with Selenium 2.

0, one simply needs to request the driver to get(that url here) .

For example; driver.


com’) would fetch the Google homepage.

Signing InAll that remains now is convincing LinkedIn that the Bot is you, which is fairly easy given we’ve told Selenium your account information, so make it happen;# regarding logging in def login(self): # retag driver driver = self.

driver # load login page driver.


com/login') # take a breath (hedge load time) sleep(1) # identify and send username to user input box self.



Note: Selenium is unaware of this “Login box” about which you speakError — What??????‍♂️????‍♀️, that’s on you fam, we agreed you would remind me to remind you about the Captain’s Log.

Whatever, basically that box you type your email or phone number into does not exist in Selenium’s universe.

Or, now.

it does, but not in the way you currently know or think of it.

Here the user information input boxes are less like;And more like;‘#username’ ,‘#password’ , and ‘.

btn__primary — large’ if identifying by CSS SelectorBut could also be;‘//*[@id=”username”]’ ,‘//*[@id=”password”]’ , and ‘/html/body/div/main/div/form/div[3]/button’ if identifying by XPathWhile numerous other methods for a Driver to identify these boxes exist, CSS Selector and XPath are by far the most popular, almost always get the job done, and will be all we use today.

XPaths and CSS SelectorsExtraction of these labels is easily done by:right clicking the GUI representationthen click inspect elementfinding the element (now highlighted) in the Inspectorright clicking on it to pull up a new menuselecting Copyand choosing either XPath or CSS SelectorThe head-to-head pros and cons of these two primary identification methods are hotly debated and a Story for another day.

For now, just know we will use whichever is cleaner and more likely to succeed in the future.

In the case of the log in page, neither the CSS Selector nor the XPath of the “Sign in” button are ideal.

Never bet on anything resembling ‘/html/body/div/main/div/form/div[3]/button’ or ‘.

btn__primary — large’ to work longer than today (especially if dealing with Facebook –a Story for tomorrow).

Both could easily be wiped by the addition of another button, the loading of an unexpected format, or just routine site maintenance.

It is for instances like these that we imported Keys , and will just RETURN or ENTER after sending the self.

p attribute (via WebDriver’s send_keys() method) to the password input box.

Obviously, this could also apply to ‘//*[@id=”password”]’, but how often do you think LinkedIn decides it’s a good idea to adjust the id of a password element?Hey Driver, Find it pleaseLast thing to cover before we fire it back up; how does Selenium go about finding these variables (elements)?WebDriver can tag these elements for Selenium in a few ways that all basically do the same thing, so go for the easiest to remember; find_element_by_xpath() and find_element_by_css_selector() are those.

Now, with this mess cleaned up, let’s knock logging in out;Reminder: noting and docstrings are optionalNice work!.Selenium is now through the gates and straight chillin at the LinkedIn homepage.

That wasn’t all that difficult.

Was it?LinkedOn —share_the_(post)Whew, the uphill battle has been won, and it only gets easier from here.

As a major milestone has just been passed, it is a good time to review where we are.

Think back to the initial roadmap:Rough Estimation̵O̵p̵e̵n̵ ̵a̵ ̵b̵r̵o̵w̵s̵e̵r̵̵N̵a̵v̵i̵g̵a̵t̵e̵ ̵t̵o̵ ̵h̵t̵t̵p̵s̵:̵/̵/̵L̵i̵n̵k̵e̵d̵I̵n̵.

̵c̵o̵m̵ ̵̵S̵i̵g̵n̵ ̵I̵n̵ ̵t̵o̵ ̵a̵n̵ ̵A̵c̵c̵o̵u̵n̵t̵Recognize and click the “Start a post” buttonType up (or otherwise) the content to be displayedFocus and click the ‘Post’ buttonOk, we are doing pretty well!.For the purposes of readability and functionality, items 4–6 will be group together in the next method.

PostingThe paths needed to check these off are:# css selector of 'start a post' buttonstart_share_button = '.

share-box__open'# css selector of post input boxto_talk_about = '.

mentions-texteditor__contenteditable' # xpath of 'post' buttonpost_button = '//*[contains(@class,"share-actions__primary-action")]'And it is now time to get to posting.

First up, opening the content box;# find button to start a post using css selectorstart_post = driver.

find_element_by_css_selector(start_share_button)# and click to start the poststart_post.

click()Second, type up the post being shared.

The post will be input from the method, so just call it post for now;# find post input box (now open) via css selectorpost_box = driver.

find_element_by_css_selector(to_talk_about)# type out the content (input from method)post_box.

send_keys(post)Pause, quick note; deriving non-obvious XPathsBefore completing the trio, there is a little trick that was used to find this XPath that should be mentioned.

You’re likely familiar with the layout of LinkedIn’s posting box, but just in case, this is what the bottom looks like:Directly pulling the XPath from that little blue “Post” button results in something like //*[@id=”ember683"] , an immediate red flag as any path with numbers at the end is subject to quickly change.

For example, refreshing the page and pulling again returns //*[@id=”ember365"] (same with the CSS Selectors #ember683 and #ember365).

How can we solve this?.One way is to examine the HTML (highlighted in the inspector) and find a recognizable label.

The HTML in question looks like:<button data-control-name=”share.

post” id=”ember383" class=”share-actions__primary-action artdeco-button artdeco-button — 2 artdeco-button — primary ember-view”><!.— →data-control-name seems decent and short, but something more common, such as class, would better serve the purposes of readable code that can easily be edited in the event of breaking some time in the future.

This decision is based on my semi-familiar understanding of HTML, where I know class is commonplace, but have not encountered data-control-name very often.

Using a sweet XPath trick, contains, that class can be summed up to “share-actions__primary-action” and turned into the viable XPath ‘//*[contains(@class,”share-actions__primary-action”)]’ .

While other applications exist, and are stupid cool & handy, they are not this purpose of this Story.

Onward;Posting (continued)Third and finally, find and click the post_button;# find post button via xpathpost_now = driver.

find_element_by_xpath(post_button)# and click to postpost_now.

click()Now just,Combine each into a their own one-line version (because you’re too good to be wasting space and it’s a simple challenge)Select and them all into a method ; def share_the_(self, post):Tie in the driver, add cool-down sleeps, and self the postto finalize our posting method to something along the lines (ha!) of:No.

Noting and docstrings are not optional.

Anything less than perfection is sin.

(jk, lol — or am I?)LinkedOff — close_out()Wrapping up, the purpose of this Bot is to help keep you off LinkedIn, and Selenium does not automatically shut down, so we need to add a final method to power this down and continue progressing with your day.

Done simply;LinkedOut— classYou did it!.Yep, that’s all.

The work has been done and a clear understanding of what is going on has been earned.

It is finally time to name the class and convert the Python file in to code that can run.

Select everything except the importsHit tabMake a new line below the imports and before __init__Name the class whatever you want, I use LinkedOutThe following should be added to make the script to be able to run on call and request post to share:# proper runif __name__ == "__main__": # username user_id = user # password pword = pwrd # option to input email/phone live if user_id == '__OPT-OUT__': # ask for user user_id = input('username: ')# option to input password live if pword == '__OPT-OUT__': # ask for pwrd pword = input('password: ') # Firefox/Gecko options setup options = webdriver.

FirefoxOptions() # deactivate push notifications options.



enabled', False) # request post for sharing status = input('What would you like to share? ') # tag bot incoming = LinkedOut(username=user_id, password=pword, driver=webdriver.

Firefox(options=options)) # get the party started incoming.

login() # share the post incoming.

share_the_post(post=status) # get the heck outta dodge incoming.

close_out() # and give reassurance so we don't engage the void print(f'sent {status}!.to account associated with {user_id}')Finally, here’s the full Python file and a link to the GitHub repo:Thank you for stopping by and following along.Please do not hesitate to drop a comment or reach out with any question or feedback you may have.

.. More details

Leave a Reply