Avoid LinkedIn’s Notorious “Incoming” Season by Posting Your Updates with Selenium 2.0

Winston Robson
May 2 · 10 min read
Via @finance_god on Instagram (profile link)

As many have noticed, any given user’s LinkedIn feed is currently somewhere between dotted and drowning in content related to the nearly 2.3 million (hopefully young) people updating their Headlines with aims of broadcasting to the world that they are “Incoming” to an 8–10 week job (to date, the universe waits for the first “Current” or “Outgoing” to be spotted).

For readers less familiar with this phenomenon, enjoy this sample taken from the results of a LinkedIn search for people with “Incoming” in their Headline:

  • Incoming Product Design Intern at …
  • Incoming Sales Operations Intern @ …
  • Incoming Investment Banking Summer Analyst at … (2019)
  • Incoming Hot Dog Assembly Intern / CEO at Big Buns Truck Stop

Ok, so maybe the last one wasn’t real, but that search generated over 2,250,000 results, so it is not all that unlikely you have seen worse…

This really captures the general mood pretty well. (Top is Comment, Bottom is Original Post)

Let it be clear, I bear no ill will towards these individuals; I just really do not care to spend time scrolling through their nonsense attempts at relevancy, and have instead spent that time building a script to spew my own nonsense attempts at relevancy through the click of a button.

In a lighthearted and easy to comprehend manner, the remainder of this article will outline how you too can get LinkedOut.

Defining — What is being Done? 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:

  1. understand where one is
  2. understand what he or she is trying to accomplish
  3. have some rough estimation of how this will be done

The 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?

  1. Open a browser
  2. Navigate to
  3. Sign In to an Account
  4. Recognize and click the “Start a post” button
  5. Type up (or otherwise) the content to be displayed
  6. Focus and click the ‘Post’ button

Okay, 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 — Imports

As with all good shenanigans, go ahead and start with;

Then it might be a decent idea to call in the package being explored;

Next, 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.

Third, make a separate file to store your account information (username and password), mine is called 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;

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, coding of this script will follow suit and move forward in the OOP paradigm. See “Intro to OOP in Python” if you’d like a 4 minute refresher on OOP.

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 in
  • Password for logging in
  • Browser being operated

Here 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.g. Gecko, Chrome, Edge), which will call upon its paired Browser for collaboration.

With the understanding of a Driver in mind, kick things off with a little;

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.

To load a webpage with Selenium 2.0, one simply needs to request the driver to get(that url here) . For example; driver.get(‘’) would fetch the Google homepage.

All 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;

Note: Selenium is unaware of this “Login box” about which you speak

🤦‍♂️🤦‍♀️, 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 Selector

But could also be;

‘//*[@id=”username”]’ ,‘//*[@id=”password”]’ , and ‘/html/body/div/main/div/form/div[3]/button’ if identifying by XPath

While 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.

Extraction of these labels is easily done by:

  1. right clicking the GUI representation
  2. then click inspect element
  3. finding the element (now highlighted) in the Inspector
  4. right clicking on it to pull up a new menu
  5. selecting Copy
  6. and choosing either XPath or CSS Selector

The 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?

Last 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 optional

Nice 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:

  1. ̵O̵p̵e̵n̵ ̵a̵ ̵b̵r̵o̵w̵s̵e̵r̵
  2. ̵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̵ ̵
  3. ̵S̵i̵g̵n̵ ̵I̵n̵ ̵t̵o̵ ̵a̵n̵ ̵A̵c̵c̵o̵u̵n̵t̵
  4. Recognize and click the “Start a post” button
  5. Type up (or otherwise) the content to be displayed
  6. Focus and click the ‘Post’ button

Ok, we are doing pretty well! For the purposes of readability and functionality, items 4–6 will be group together in the next method.

The paths needed to check these off are:

And it is now time to get to posting. First up, opening the content box;

Second, type up the post being shared. The post will be input from the method, so just call it post for now;

Before 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:

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;

Third and finally, find and click the post_button;

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 post

to 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— class

You 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.

  1. Select everything except the imports
  2. Hit tab
  3. Make a new line below the imports and before __init__
  4. Name the class whatever you want, I use LinkedOut

The following should be added to make the script to be able to run on call and request post to share:

Finally, here’s the full .py file and 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.

Continued Reading

Future Vision

A publication centered around high quality storytelling

Winston Robson

Written by

Terrible in onset. Swift in execution.

Future Vision

A publication centered around high quality storytelling

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade