3

I am new to python, but need to modify code created by someone else. I am not able to post the full code, but I posted most of it below:

from bs4 import BeautifulSoup
import datetime
import getpass
from gmail import Gmail
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import ElementNotVisibleException
from time import sleep
from selenium.common.exceptions import NoAlertPresentException
from selenium.webdriver.support import expected_conditions as EC


def soupify(session, url):
    """
    Makes parse-able HTML from any given URL.
    :param session: requests.Session()
    :param url: str
    :return: BeautifulSoup object
    """
    while True:
        try:
            r = session.get(url)
            break
        except Exception as e:
            print(e)
    return BeautifulSoup(r.content, 'html.parser')


def create_http_session():
    """
    Quick little function for returning a requests.Session() instance
    with a properly set User-Agent header.
    :return: requests.Session()
    """
    session = requests.Session()
    session.headers.update({'User-Agent':
                            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
                        })
    return session


def retrieve_hidden_input(soup, name):
    return soup.find('input', attrs={
        'type': 'hidden',
        'name': name
    }).get('value')

class AmericanHomesScraper:
    def __init__(self, username, password, testing):
        self.username = username
        self.password = password
        self.testing = testing
        self.chrome_session = webdriver.PhantomJS()
        self.chrome_session.maximize_window()
        self.g = Gmail()
        self.g.login(username, password)
        self.index = 0

    def check_for_new_emails_from_sender(self,
                                     sender='info@mailer.netflix.com'):
        self.index += 1
        if self.index % 1000 == 0:
            print('Checking emails - {0}.'.format(datetime.datetime.now()))
        elif self.index == 1:
            print('Checking emails - {0}.'.format(datetime.datetime.now()))
        else:
            pass
        for s in sender:
            messages = self.g.inbox().mail(sender=s,
                                       unread=True)
            for message in messages:
                message.read()
                print(
                'Email from {0}: {1}.'.format(s, datetime.datetime.now()))
                self.check_for_listings()
            self.g.logout()
            self.g = Gmail()
            self.g.login(self.username, self.password)

 def login(self, username, ahsPassword):
    self.ahsPassword = ahsPassword
    self.chrome_session.get('https://www.aaa.com/Login.aspx')
    self.chrome_session.find_element_by_xpath(
        '//*[@id="txtUsername"]'
    ).send_keys(username)
    self.chrome_session.find_element_by_xpath(
        '//*[@id="txtPassword"]'
    ).send_keys(ahsPassword)
    self.chrome_session.find_element_by_xpath(
        '//*[@id="btnSubmit"]'
    ).click()

 def check_for_listings(self):
    #code block
    links = self.chrome_session.find_elements_by_class_name('link-record')
    links = [(link.text, link.get_attribute('href').decode('utf-8'))
             for link in links]
    if len(links) == 0:
        print("No work orders available at {0}".format(
            datetime.datetime.now())
        )
    else:
        for link_text, link_url in links:
            print("Clicking work order {0} at {1}".format(link_text,datetime.datetime.now()))
            self.chrome_session.get(link_url)
            print("Attempting to accept at {0}".format(datetime.datetime.now()))
            try:
                self.chrome_session.find_element_by_xpath("//input[@value='Accept']").click()
                try:
                    WebDriverWait(self.chrome_session, 1).until(EC.alert_is_present)
                    self.chrome_session.switch_to().alert().accept()
                    print("Accepted work order {0} at {1}.".format(link_text,datetime.datetime.now()))
                except:
                    print "no alert"
            except ElementNotVisibleException:
                print("Accept input not found at {0}".format(datetime.datetime.now()))
            self.chrome_session.back()

def main():
    username = raw_input(
        'Please enter the username of the GMail account you want to monitor.\n>')
    password = getpass.getpass(
        'Please enter the password of the GMail account you want to monitor.\n>')
    ahsPassword = getpass.getpass('Please enter the password for ahs. \n>')
    ahs = AmericanHomesScraper(username, password, testing)
    ahs.login(username,ahsPassword)
    print("Starting script")
    while True:
        ahs.check_for_new_emails_from_sender([
            'crm@aaa.com',
            'ds@aaa.com',
        ])


if __name__ == '__main__':
    main()

This correctly finds and clicks the "Accept" input button. After clicking accept, a javascript alert (Ok/Cancel) opens to confirm the acceptance. However, the script does not find the resulting alert. Or, at least it is not able to accept it because the exception is called.

As you can see, I have attempted to switch_to().alert(), but that isn't working.

What am I doing wrong? Thank you so much for your help, I have been working on this for hours.

UPDATE

This code is running on a virtual server and does not use a UI. I just realized that the driver is PhantomJS, not Chrome. So, this is probably why it is failing. I have updated the question to reflect this.

I have tried the suggestions from similar questions, but it isn't working. The 3rd party site requires the alert confirmation to finish the process, but my script cannot see it because it is headless.

davids
  • 5,397
  • 12
  • 57
  • 94
  • atleast give the url – Argus Malware Nov 17 '17 at 15:34
  • http://selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webdriver.WebDriver.switch_to_alert – Bahrom Nov 17 '17 at 15:38
  • Possible duplicate of [Click the javascript popup through webdriver](https://stackoverflow.com/questions/8631500/click-the-javascript-popup-through-webdriver) – Bahrom Nov 17 '17 at 15:39
  • @Bahrom, as you can see from the code, I have tried to click the alert as mentioned in your article. It isn't working. – davids Nov 17 '17 at 15:44
  • (This isn't rhetorical, asking for my own learning) Is the alert guaranteed to exist instantaneously from when when .click() returns on the accept button? –  Nov 17 '17 at 16:01
  • I see it immediately. But, I am not sure if the code considers it instant. – davids Nov 17 '17 at 16:03
  • @davids Easy and horrendous way to check is to sleep for ~2-3 seconds after you click, if the code suddenly starts working - you're now going to have to implement a proper way of checking for the alert box before trying to click it. If not, it's "probably" something else. –  Nov 17 '17 at 16:16
  • @davids If this is the case, this answer will likely be your problem: https://stackoverflow.com/questions/35728689/firefox-alert-box-not-detected-with-selenium-webdriver - apparently Selenium will not wait for javascript to finish executing, and so the alert could not be visible at the point you try to accept it. –  Nov 17 '17 at 16:20
  • driver.switch_to.alert – Corey Goldberg Nov 17 '17 at 16:42

3 Answers3

1

A couple of points here :

  • switch_to_alert had been deprecated so we have to mandatory use switch_to().alert()
  • Seems to me a purely timing issue. Hence we need to induce ExplicitWait i.e. WebDriverWait with expected_conditions clause set to alert_is_present as follows :

    from selenium.webdriver.support import expected_conditions as EC
    #code block
    self.chrome_session.find_element_by_xpath("//input[@value='Accept']").click()
    WebDriverWait(self.chrome_session, 10).until(EC.alert_is_present)
    self.chrome_session.switch_to().alert().accept()
    print("Accepted work order {0} at {1}.".format(link_text, datetime.datetime.now()))
    
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • I have tried this, but it just hangs. No messages, no errors. – davids Nov 19 '17 at 04:23
  • I have posted most of the code now to see if it helps – davids Nov 19 '17 at 04:44
  • I just realized that it is using PhantomJS, not Chrome. So, the alert never actually appears when the script runs. However, I cannot accomplish my goal without clicking the accept. How do I handle this? – davids Nov 21 '17 at 16:14
0

I dont know if the below code will help you, at one point I faced the same problem and ended up waiting for an alert and then accepting the alert, worked for me.

from selenium.common.exceptions import NoAlertPresentException,

   def wait_for_alert(self):

        for i in range(50):
            try:
                alert = chrome_session.switch_to.alert
                if alert.text:
                   break
            except NoAlertPresentException:
              pass
            time.sleep(.25)
        else:
            raise NoAlertPresentException("Alert visibility timed out")
    return alert

wait_for_alert().accept()
Satish
  • 1,976
  • 1
  • 15
  • 19
  • this just reimplements part of WebDriver's explicit wait. – Corey Goldberg Nov 17 '17 at 16:40
  • you are right and this is the path I took and not the explicit wait, having said that, in my case, there was a delay between the action and the alert display, and my code worked. – Satish Nov 17 '17 at 16:43
  • Awesome code, I would, however, suggest the selenium WebDriver wait whether implicit or explicit. Here's the references : http://selenium-python.readthedocs.io/waits.html# – innicoder Nov 17 '17 at 16:48
  • Thank you, I will look into it. In my case, i need the driver to tell whether the element is present and visible in the dom and not to return the element itself. Its a good read, thank you – Satish Nov 17 '17 at 16:55
  • Satish you just use a method WebdriverWait(arguments).until(presence_of_element_located and then you locate it by XPath or something, It's not this exactly but this will help you find it (hopefully) – innicoder Nov 17 '17 at 17:29
0

Try The Code below! Working Fine for me!

alert = driver.switch_to.alert #This .alert will work For Python
try:
   alert.accept() #If you want to Accept the Alert
except:
   alert.dismiss()  #If  You want to Dismiss the Alert.
Amar Kumar
  • 2,392
  • 2
  • 25
  • 33