Обзор
Я пишу скрипт на Python 3 для создания новых общих альбомов Google Фото.
Я выбрал 1 для использования на основе Conda 3 Селен
работает на Ubuntu 18.10 Linux. Чтобы создать альбом после входа в систему,
мой скрипт использует объект ActionChains , чтобы нажать «Создать»
кнопка. На странице появляется меню выбора, один из которых
мне нужно щелкнуть скрипт, который называется «Создать общий альбом».
«Чистый» код для нажатия на этот пункт меню не работает: он просто
закрывает меню Альтернатива взломать (это работает): после
меню открывается, отправьте две клавиши со стрелками вниз и клавишу возврата. Но это
хак хрупок, поэтому я хотел бы получить некоторые рекомендации о том, как сделать чище
подход осуществим.
скрипт MCVE mcve_google_photos.py
The
Скрипт MCVE ниже имеет две «особенности», обе из которых вы можете
выполнить из командной строки Linux, чтобы увидеть проблему, с которой я борюсь
с:
# A MCVE (https://stackoverflow.com/help/minimal-reproducible-example)
# of attempts to click on the Google Photo "Create shared album" item:
import sys
import os
import subprocess
from pyvirtualdisplay import Display
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
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from bs4 import BeautifulSoup
def prepend_dir_into_PATH(directory):
elems = os.environ['PATH'].split(':')
if not [x for x in elems if x == directory]:
elems.insert(0, directory)
os.environ['PATH'] = ":".join(elems)
def prepend_chromedriver_into_PATH():
chromedriver_binary_dir = subprocess.check_output(['chromedriver-path'], shell=True).decode('UTF-8').strip()
prepend_dir_into_PATH(chromedriver_binary_dir)
def find_by_id_and_click(driver, id):
elem = driver.find_element_by_id(id)
elem.click()
def automate_google_photos(google_email_address, google_password, features):
prepend_chromedriver_into_PATH()
headless = True # Display the driver under Xvfb
if 'interactive' in features:
headless = False # Display the driver under the main $DISPLAY
if headless:
display = Display(visible=0, size=(3000, 3000))
display.start()
driver = webdriver.Chrome()
url = 'https://photos.google.com/'
driver.get(url)
timeout = 10
login_elem = WebDriverWait(driver, timeout).until(EC.presence_of_element_located((By.XPATH, "//*[contains(text(), 'Go to Google Photos')]")))
login_elem.location_once_scrolled_into_view
login_elem.click()
email_elem = WebDriverWait(driver, timeout).until(EC.visibility_of_element_located((By.XPATH, "//input[contains(@type, 'email')]")))
email_elem.send_keys(google_email_address)
find_by_id_and_click(driver, 'identifierNext')
password_elem = WebDriverWait(driver, timeout).until(EC.visibility_of_element_located((By.XPATH, "//input[contains(@type, 'password')]")))
password_elem.send_keys(google_password)
find_by_id_and_click(driver, 'passwordNext')
create_elem_xpath = "//div[@aria-label='Create']"
create_elem = WebDriverWait(driver, timeout).until(EC.presence_of_element_located((By.XPATH, create_elem_xpath)))
# # Force the element into view (but no this does not work, given the 0 values of x and y in location_dict):
# location_dict = create_elem.location_once_scrolled_into_view
# print("location_dict", location_dict)
test_feature = 'use_action_class_click_on_shared_album_menu_element'
if test_feature in features:
print(f'Attempting feature: {test_feature}')
# This just opened up the menu and closed it right away:
action = ActionChains(driver)
action.click(create_elem)
action.perform()
create_shared_album_elem = create_elem.find_element_by_xpath("//div[@aria-label='Create shared album']")
action.move_to_element(create_shared_album_elem)
action.click(create_shared_album_elem)
action.perform()
test_feature = 'use_action_class_use_keys_on_shared_album_menu_element'
if test_feature in features:
print(f'Attempting feature: {test_feature}')
# This works, but has a major flaw, in that it doesn't
# reference the element by xpath and thus is fragile
# (e.g. when Google adds more elements above the "Create
# shared album" element in the menu):
action = ActionChains(driver)
action.click(create_elem)
action.send_keys(Keys.ARROW_DOWN)
action.send_keys(Keys.ARROW_DOWN)
action.send_keys(Keys.RETURN)
action.perform()
# Wait for the "Add a title" input field to show up, to know
# we are truly in the "Shared Album" page, so that we can then
# take the "success" screenshot:
WebDriverWait(driver, timeout).until(EC.presence_of_element_located((By.XPATH, "//*[contains(text(), 'Add a title')]")))
# import pdb; pdb.set_trace() # <-- uncomment this line to stop it before the browser shuts down
# If we get this far without exceptions, we have succeeded.
google_email_address, google_password = sys.argv[1:3]
automate_google_photos(
google_email_address,
google_password,
# The rest of sys.argv will be keywords to enable specific functionality for this MCVE:
sys.argv[3:]
)
Использование этого сценария в командной строке:
python ./mcve_google_photos.py YOUR_USERNAME_HERE@gmail.com YOUR_GOOGLE_PASSWORD_HERE interactive FEATURE
, где FEATURE является одним из:
use_action_class_click_on_shared_album_menu_element
: кажется
чтобы быть очевидным способом выполнить клик, используя
ActionsChain
объект, но вместо этого он просто вызывает меню Создать
исчезнуть. Это тот, который я хочу быть работоспособным.
use_action_class_use_keys_on_shared_album_menu_element
: Это
работает по крайней мере сейчас, но зависит от относительного порядка
пунктов меню, которые являются хрупкими 2 .
Вопрос
Какие изменения мне нужно сделать, чтобы получить код в
use_action_class_click_on_shared_album_menu_element
раздел будет
работоспособный?
Сноска * +1051 *
Мне специально нужно использовать Selenium на основе conda под Python
3, а не предоставляемые Google API. Мне нужно общее решение
аналогичные типы проблем при автоматизации сайтов, не принадлежащих Google
(без веб-API).
Если Google решит добавить новый пункт меню до , то "Создать"
«Общий альбом», мой код будет выполнен неправильно.
Я использую Google Chrome версии 76.0.3800.0-1, установленный из .deb
файл скачан с гугла. Я использую chromedriver
WebDriver, установленный с помощью conda:
conda install -n envpython3 -c conda-forge python-chromedriver-binary