дождитесь отображения элемента, а не его отображения, используя selenium, python и whatsapp-web - PullRequest
0 голосов
/ 18 января 2019

Я пытаюсь использовать "whatsapp-web", "selenium" и "python 3", чтобы знать, когда пользователь WhatsApp подключается к сети или отключается.

Чтобы объяснить больше, я хочу, чтобы скрипт работал:

скрипт будет прослушивать диапазон (с заголовком = онлайн), который будет отображаться, когда диапазон отображается (это означает, что пользователь выходит в сеть), я хочу, чтобы время в этот момент было напечатано, тогда сценарий сохранит прослушивание еще раз для исчезновения промежутка, когда исчезает сценарий, напечатайте время исчезновения и так далее.

это мой код:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import datetime

driver = webdriver.Chrome('C:/webdrivers/chromedriver.exe')
driver.get('https://web.whatsapp.com/')

# do nothing until QR code scanned and whatsapp-web is accessed
input('Enter anything after scanning QR code')

# Input the name of the user to track
name = input('Enter the name of the user : ')

# find the whatsapp user to be tracked then a click to enter the conversation
user = driver.find_element_by_xpath("//span[@title = '{}']".format(name))
user.click()

while True:
   # in the conversation page, a span with title online is diplayed when user is online.
   #the web driver will wait 8hrs=28800s, if user not online all this time script will be killed by webdriverWait
   element = WebDriverWait(driver, 28800).until(
      EC.visibility_of_element_located(
         (By.XPATH, "//span[@title = 'online']")))

   #Moment the user came online
   now = datetime.datetime.now()
   print("online at : ")
   print(now.strftime("%H:%M:%S"))

   element = WebDriverWait(driver, 28800).until(
      EC.invisibility_of_element_located(
         (By.XPATH, "//span[@title = 'online']")))

   #Moment the user went offline
   now = datetime.datetime.now()
   print("offline at : ")
   print(now.strftime("%H:%M:%S"))
   print("************")

Мой сценарий работает, но, Я хочу, чтобы он работал часами, например, 8 часов или больше, но я читал, что это плохая практика использовать WebDriverWait с большим количеством секунд (28800 в моем случае).

Так есть ли другой лучший способ добиться этого?

как мне записать вывод в текстовый или текстовый файл?

Есть предложения по улучшению кода?

Как предотвратить сбой процессора? или любая возможная проблема, которая может произойти

1 Ответ

0 голосов
/ 18 января 2019

WebDriverWait - не что иное, как (вполне) причудливый цикл while / catch / sleep ; в вашем конкретном случае вы можете захотеть скопировать его самостоятельно по одной простой причине - он опрашивает каждые 500 мс, что, вероятно, является слишком подробным разрешением для этой задачи. Это также защищает вас от более детального контроля.

Вот как сделать логику самостоятельно - есть логическая переменная - пользователь онлайн или нет; в зависимости от значения, check - видимый элемент (.is_displayed()), время ожидания X и повтор. Исключение NoSuchElementException, StaleElementReferenceException будет учитываться как пользователь в автономном режиме / логическое значение false.

В конце ваш код будет очень близок к логике в WebDriverWait - все же ваш и более гибкий, если это необходимо.


Либо просто передайте больший внутренний опрос в WebDriverWait в текущем коде - это аргумент poll_frequency вызова:)

WebDriverWait(driver, 28800, 5)  # the value is in seconds

Я не могу знать, где и что вы прочитали, это плохая практика использовать WebDriverWait с большим количеством секунд; как видно из кода, сколько времени дается запуску метода.
Я полагаю, что совет был в тонусе: «Это плохая практика - использовать WebDriverWait с большим количеством секунд, , потому что если условие не будет выполнено в течение X секунд, оно никогда не будет выполнено, и ваш код будет просто вращаться и вращаться.". Что на самом деле для вас желаемое поведение:)

Я бы тоже не стал беспокоиться о налогах на процессор - эти проверки очень легкие, ничего страшного. Для этой большой среды выполнения меня беспокоит утечка памяти в самом браузере;)


Что касается оптимизации кода - то, что я хотел бы сделать, это сократить повторения операторов; с обратной стороной снижения его читабельности немного. Мое мнение о петле:

user_online = False

while True:
    # we'll be checking for the reverse of the last status of the user
    check_method = EC.visibility_of_element_located if not user_online else EC.invisibility_of_element_located

    # in the conversation page, a span with title online is diplayed when user is online.
    # the web driver will wait 8hrs=28800s for the user status to change all
    # the script will be killed by webdriverWait if that doesn't happen
    element = WebDriverWait(driver, 28800, 5).until(
            check_method((By.XPATH, "//span[@title = 'online']")))

    # The moment the user changed status
    now = datetime.datetime.now().strftime("%H:%M:%S")
    print("{} at : {}".format('online' if not user_online else 'offline', now))   # if you're using python v3.6 or more, the fstrings are much more convenient for this
    print("************")

    user_online = not user_online   # switch, to wait for the other status in the next cycle

И наконец, по коду - скрипт нельзя оставлять «бесконечным». Зачем? Потому что, если пользователь не изменит статус в течение 8 часов, WebDriverWait остановится. Чтобы спасти это, оберните тело цикла в try / кроме:

from selenium.common.exceptions import TimeoutException  # put this in the beginning of the file

while True:
    try:
        # the code from above
    except TimeoutException:
        # the status did not change, repeat the cycle
        pass

Запись в файл

Возможно, вы захотите прочитать a бит как сделать это - это довольно простая операция.

Вот пример - откройте файл для добавления (чтобы там сохранялись предыдущие журналы), обернув цикл while:

with open("usermonitor.log", "a") as myfile:
    while True:
        # the other code is not repaeted for brevity
        # ...
        output = "{} at : {}".format('online' if not user_online else 'offline', now)
        print(output)
        myfile.write(output + "\n")  # this will write (append as the last line) the same text in the file
        # write() does not append newlines by itself - you have to do it yourself
...