0

I am in the process of building a script using python and selenium which clicks on the 'message button' on my network to send a default message.

Linkedin uses dynamic fields (ember), so it's not possible to find elements by id. So far I have tried:

driver.find_elements_by_class_name("...").click()
driver.find_element_by_tag_name("button").click()
driver.find_element_by_css_selector("...").click()
driver.find_element_by_xpath(//...)

This is my code so far:

def TextBot(browser):
time.sleep(3)
browser.get('https://www.linkedin.com/mynetwork/invite-connect/connections/')
time.sleep(3)
xpath = '//button[contains(@aria-label,"Send message to")]'
time.sleep(3)
buttons = driver.find_element_by_xpath(xpath)
for btn in buttons:
    print("Can %s" % btn.get_attribute("aria-label"))  

def Main():
#Parse enail and password to the script
parser = argparse.ArgumentParser()
parser.add_argument('email', help='linkedin email')
parser.add_argument('password', help='linkedin password')
args = parser.parse_args()

#browse to the login page
browser = webdriver.Firefox()
browser.get('https://linkedin.com/uas/login')

#Parse the two argument in the login form
emailElement = browser.find_element_by_id('session_key-login')
emailElement.send_keys(args.email)
passElement = browser.find_element_by_id('session_password-login')
passElement.send_keys(args.password)
passElement.submit()

#Initialise ViewBot function
os.system('clear') #cls rather than clear on windows
print ("[+] Success! Logged In, Bot Starting")
#ViewBot(browser)
TextBot(browser)
browser.close()

And my error:

File "LinkedInBot.py", line 88, in TextBot buttons = driver.find_element_by_xpath(xpath) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 393, in find_element_by_xpath return self.find_element(by=By.XPATH, value=xpath) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 966, in find_element 'value': value})['value'] File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 320, in execute self.error_handler.check_response(response) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response raise exception_class(message, screen, stacktrace) selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: //button[contains(@aria-label,"Send message to")]

Is there anything else I am not thinking about?

By the way thanks for the answers so far. I am close but I still think there is some other form of encryption to bypass

Sers
  • 12,047
  • 2
  • 12
  • 31
matthausen
  • 39
  • 8
  • 1
    Share code you're using with locators, steps and try add waits – Sers Aug 31 '18 at 20:53
  • Cheers! I added waits all over the place. If I type: page.find_all('button') the I get a list, but when I do it via driver.find_element_by I always get ths error above – matthausen Sep 01 '18 at 14:20
  • I gave you code you need below in my answer – Sers Sep 01 '18 at 14:23
  • Thank you mate! Any idea why I get: name 'By' is not defined error? Any imports apart fromm yours i missed? – matthausen Sep 01 '18 at 14:55
  • https://stackoverflow.com/questions/17540971/how-to-use-selenium-with-python – Sers Sep 01 '18 at 15:07
  • Now I get this error: connectionsHeader = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".message-anywhere-button"))).text File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/selenium/webdriver/support/wait.py", l ine 80, in until raise TimeoutException(message, screen, stacktrace) selenium.common.exceptions.TimeoutException: Message: The message is empty but I changed the time from 20 to 3 to 10. I guess the real problem is it cannot find the element by css selector... – matthausen Sep 01 '18 at 17:41

2 Answers2

2

"Message button" in Linkedin looks like this:

<button class="message-anywhere-button mn-connection-card__message-btn button-secondary-medium" aria-label="Send message to John Smith" data-ember-action="" data-ember-action-5128="5128">
  <span aria-hidden="true">Message</span>
  <span class="visually-hidden">
    Send a message to John Smith
  </span>
</button>

So for locator you have several options, lets settle on most primitive one: look for Send message to text inside aria-label:

xpath = '//button[contains(@aria-label,"Send message to")]'

This locator will find all buttons. But depending on which function you call, you may end up choosing only first element, or all elements. Lets say the goal is to collect all the buttons:

xpath = '//button[contains(@aria-label,"Send message to")]'
all_message_buttons = driver.find_elements(By.XPATH, xpath)
for message_button in all_message_buttons:
    print("Can %s" % message_button.get_attribute("aria-label")) 
    # prints Can Send Message to John Smith
    # and any other names available on page

Finally, before selecting buttons you need to make sure that page was loaded and buttons are indeed displayed. There are various ways to do that, but I usually just replace finding elements I need with waiting for them:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# ...
xpath = '//button[contains(@aria-label,"Send message to")]'
wait = WebDriverWait(browser, 10) # wait for up to 10 sec
all_message_buttons = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
for message_button in all_message_buttons:
    print("Can %s" % message_button.get_attribute("aria-label")) 
    # prints Can Send Message to John Smith
    # and any other names available on page
timbre timbre
  • 12,648
  • 10
  • 46
  • 77
2

Code below scroll down until all contacts have been loaded.
Then get all message buttons, click, send and close message window.

from selenium.webdriver.support import expected_conditions as EC
import re

#...

wait = WebDriverWait(driver, 20)
connectionsHeader = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".mn-connections__header h2"))).text
totalConnections = int(re.findall(r"\d+", connectionsHeader))
while len(driver.find_elements_by_css_selector(".mn-connections li")) < totalConnections-1:
    driver.execute_script("window.scrollTo(0, 100);")

messageButtons = driver.find_elements_by_css_selector(".mn-connections li button.mn-connection-card__message-btn")

for button in messageButtons:
    button.click()
    wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".msg-form__contenteditable"))).click()
    driver.find_elements_by_css_selector(".msg-form__contenteditable").send_keys('message')
    driver.find_elements_by_css_selector(".js-msg-close").click()
    wait.until(EC.invisibility_of_element_located((By.CSS_SELECTOR, ".js-msg-close")))

Not: code may contains typos or small errors. Feel free to improve it.

Sers
  • 12,047
  • 2
  • 12
  • 31
  • Yes I did. Got the following error now error: connectionsHeader = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".message-anywhere-button"))).text File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/selenium/webdriver/support/wait.py", l ine 80, in until raise TimeoutException(message, screen, stacktrace) selenium.common.exceptions.TimeoutException: Message: The message is empty but I changed the time from 20 to 3 to 10. I guess the real problem is it cannot find the element by css selector... – matthausen Sep 01 '18 at 18:52
  • Check again, I used `.mn-connections__header h2` not `.message-anywhere-button` – Sers Sep 01 '18 at 18:53
  • Yes, I tried with both your classes and the classes I found on the page I am in, but no success. Always Timeout Error. time.sleep() doesn't throw that error but I still get the same proble later on of no element been found. No idea how to get out of it... – matthausen Sep 01 '18 at 20:25
  • You didn't even try. There were huge mistake you could not run it. – Sers Sep 01 '18 at 20:58