Я пытаюсь создать скрипт для анализа различных номеров деталей с веб-страницы с использованием запросов. Если вы проверите эту ссылку и нажмете на вкладку Product list
, вы увидите номера деталей.
представляет, где находятся номера деталей.
Я пробовал с:
import requests
link = 'https://www.festo.com/cat/en-id_id/products_ADNH'
post_url = 'https://www.festo.com/cfp/camosHTML5Client/cH5C/HRQ'
payload = {"q":4,"ReqID":21,"focus":"f24~v472_0","scroll":[],"events":["e468~12~0~472~0~4","e468_0~6~472"],"ito":22,"kms":4}
with requests.Session() as s:
s.headers['user-agent'] = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
s.headers['referer'] = 'https://www.festo.com/cfp/camosHTML5Client/cH5C/go?q=2'
s.headers['content-type'] = 'application/json; charset=UTF-8'
r = s.post(post_url,data=payload)
print(r.json())
Когда я выполняю приведенный выше скрипт, я получаю следующий результат:
{'isRedirect': True, 'url': '../../camosStatic/Exception.html'}
Как я могу получить номера деталей с этого сайта с помощью запросов?
В случае с селеном я попытался, как показано ниже, получить номера деталей, но, похоже, скрипт не может щелкнуть вкладку списка продуктов, если я отключу от нее жестко запрограммированную задержку. Учитывая, что я не хочу использовать какую-либо жестко запрограммированную задержку в сценарии.
import time
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
link = 'https://www.festo.com/cat/en-id_id/products_ADNH'
with webdriver.Chrome() as driver:
driver.get(link)
wait = WebDriverWait(driver,15)
wait.until(EC.frame_to_be_available_and_switch_to_it(wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "object")))))
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#btn-group-cookie > input[value='Accept all cookies']"))).click()
driver.switch_to.default_content()
wait.until(EC.frame_to_be_available_and_switch_to_it(wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "iframe#CamosIFId")))))
time.sleep(10) #I would like to get rid of this hardcoded delay
item = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "[id='r17'] > [id='f24']")))
driver.execute_script("arguments[0].click();",item)
for elem in wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "[data-ctcwgtname='tabTable'] [id^='v471_']")))[1:]:
print(elem.text)
Ключи и значения полезной нагрузки, которые я использовал, я взял из инструментов разработки.
Вы не можете сделать это! Каждый запрос будет иметь разные значения, если вы не знаете, как их использовать. Это безнадежное дело с использованием запроса. Это должно быть простой задачей с использованием селена, если вы знакомы с ним.
Это определенно не безнадежное дело с использованием запросов, как вы упомянули. Всегда есть способы, которые я пытаюсь выяснить. К вашему сведению, я очень хорошо знаком с селеном, но я не хочу идти по этому пути. Спасибо.
Не сказал невозможно.. удачи! Удалось ли вам понять данные ответа?
Обычно предпочитают имитировать запросы, но в данном случае я признаю, что Selenium — лучшая идея.
Я не могу ответить на вопрос, но могу высказать свое мнение. Сначала я подумал, что это проблема с файлами cookie, но я повторил запрос несколько раз, и все это привело к ответу "{"RID":0}". Хотя параметры запроса просты, я заметил небольшие различия между запросами, и я думаю, что ответом является правильная комбинация параметров запроса (и, возможно, файлов cookie). Если вы настаиваете на использовании запросов, вы можете посетить вкладку «Инициатор» запроса и изучить, как Js создает эти параметры. Скрипт не запутан, и в Chrome есть отличные инструменты для отладки, но Js-R.E. может занять некоторое время.
Чтобы помочь вам начать работу, в SubmitPostData()
(функции, которая отправляет запрос) мы видим, что данные публикации хранятся в gPOSTData
, который создается в DoSubmit()
и содержит n.ReqID = ++gRequestID_Posted, r && (n.focus = r), n.scroll = SaveScrollEvents(), n.events = gArrEvents, n.externaldata = gExternalEventDataArray.arrData,n.ito = gEventController.GetIdleTime(), n.kms = EncodeMouseState());
. Названия создают у меня впечатление, что большинство параметров выполняют некоторые базовые действия по снятию отпечатков пальцев клиента, возможно, для обнаружения автоматизации.
Спасибо за ваши большие усилия и предложение t.m.adam. Я потратил значительное количество времени, чтобы решить эту проблему, используя запросы, но безуспешно. Итак, на данный момент мне кажется, что я должен придерживаться селена, чтобы избежать чрезмерной сложности, как в первую очередь предложил @Nic Laforge. Тем не менее, я прикрепил свой подход к селену выше, который также застревает, когда нужно щелкнуть вкладку списка продуктов. Спасибо.
Вы можете использовать селен. Обратите внимание, что ваша таблица FRAME. Просто переключитесь на фрейм и найдите числа с помощью xpath или другого локатора.
Это то, что я сделал в моем вышеприведенном сценарии @Gaj Julije.
Я не уверен, что вы можете обойти кнопку «принять все файлы cookie» с запросами python. Когда вы физически нажимаете кнопку, для сеанса устанавливается файл cookie «ckns_policy» и 4 других файла cookie. Я попытался обойти эту кнопку, вручную добавив файл cookie «ckns_policy» и другие, но пока ничего не работает.
Чтобы получить номера деталей с веб-страницы с помощью Selenium, вам необходимо:
Вызовите WebDriverWait, чтобы кадр object
стал доступным, и переключитесь на него.
Вызовите WebDriverWait, чтобы нужный элемент стал кликабельным, и нажмите на Accept all cookies.
Вернитесь к default_content()
Вызовите WebDriverWait, чтобы нужный кадр стал доступен, и переключитесь на него.
Вызовите WebDriverWait для staleness_of() устаревшего элемента.
Нажмите на вкладку с текстом в качестве списка продуктов, используя execute_script().
Вы можете использовать следующие стратегии поиска:
driver.get('https://www.festo.com/cat/en-id_id/products_ADNH')
WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.TAG_NAME,"object")))
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input.btn.btn-primary#accept-all-cookies"))).click()
driver.switch_to.default_content()
WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"iframe#CamosIFId")))
WebDriverWait(driver, 20).until(EC.staleness_of(WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//div[text()='Product list']")))))
driver.execute_script("arguments[0].click();", WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[text()='Product list']"))))
print([my_elem.get_attribute("innerHTML") for my_elem in WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.XPATH, "//div[@class='ah']/img//following::div[2]")))])
driver.quit()
Примечание. Вы должны добавить следующие импорты:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
Выход консоли:
['539691', '539692', '539693', '539694']
Вы можете найти пару соответствующих обсуждений в:
Я попробовал ваш код, но он не сработал. Он зависает, когда он должен щелкнуть вкладку со списком продуктов, и в конечном итоге выдает исключение тайм-аута, указывающее на эту строку print()
. Код, который я вставил выше, является рабочим, но мне пришлось использовать жестко запрограммированную задержку, чтобы заставить его работать.
Трудность для водителя состоит в том, чтобы нажать кнопку «Список продуктов», поэтому я нашел решение:
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.common.exceptions import TimeoutException, StaleElementReferenceException
from selenium import webdriver
import time
class NoPartsNumberException(Exception):
pass
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 10)
driver.get("https://www.festo.com/cat/en-id_id/products_ADNH")
wait.until(ec.frame_to_be_available_and_switch_to_it(wait.until(ec.visibility_of_element_located((By.CSS_SELECTOR, "object")))))
wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "#btn-group-cookie > input[value='Accept all cookies']"))).click()
driver.switch_to.default_content()
wait.until(ec.frame_to_be_available_and_switch_to_it((By.XPATH, "//iframe[@name='CamosIF']")))
endtime = time.time() + 30
while True:
try:
if time.time() > endtime:
raise NoPartsNumberException('No parts number found')
product_list = wait.until(ec.element_to_be_clickable((By.XPATH, "//div[@id='f24']")))
product_list.click()
part_numbers_elements = wait.until(ec.visibility_of_all_elements_located((By.XPATH, "//div[contains(@id, 'v471')]")))
break
except (TimeoutException, StaleElementReferenceException):
pass
part_numbers = [p.text for p in part_numbers_elements[1:]]
print(part_numbers)
driver.close()
Таким образом, драйвер нажимает кнопку «Список продуктов», пока не откроется окно, содержащее номера деталей, и вам придется ждать гораздо меньше 10 секунд, как в вашем коде с жестко запрограммированным временем сна.
Если вы не дойдете до оператора break, этот цикл будет бесконечным. Вы повышаете/ререйзите TimeoutException
и игнорируете это в своем заявлении о ловле. У вас должна быть проверка истечения времени в качестве условия для входа в цикл. Вам также нужно обработать, если он никогда не достигнет оператора break. ОП уже использует until()
не вижу смысла создавать такой цикл while.
Разрыв достижим, когда код достигает тайм-аута, в то время как он игнорирует исключение TimeoutException, заданное, когда он не находит элементы номеров деталей. Если он не находит номера деталей, он прерывает цикл while и вызывает исключение TimeoutException. По сути, то, что я написал, — это пользовательская функция, которая ждет, пока элемент действительно станет кликабельным. Вы пробовали мой код? Потому что я проверил это, и это работает очень хорошо
Я должен понизить это. Просто сказать, что это работает, не делает вышесказанное правильным. Вы точно не проверили время истекло. Опять же, это создаст бесконечный цикл. Я согласен, что вы создали еще один wait.until() с вашим кодом, но почему. Вы можете использовать until
из селена и создать свой собственный класс (см. мой ответ). Устранение необходимости обрабатывать тайм-аут, исключение, повторяющийся код, подверженность ошибкам
извините, вы правы, я написал свое собственное исключение TimeoutException, но это была плохая идея. Я отредактирую ответ с другим исключением
Я считаю, что вы хорошо рассмотрели концепцию iframe
и WebDriverWait
.
Сайт, кажется, повторно отображает контент несколько раз, прежде чем сможет получить нужный элемент и щелкнуть по нему. Поэтому вам пришлось добавить сон на 10 секунд.
Существует мнение, что при использовании EC
необходимо использовать WebDriverWait
. EC
— это всего лишь набор хелперов класса для извлечения элемента с некоторыми определенными свойствами (например, видимый, скрытый, кликабельный...)
В вашем случае ec.visibility_of_all_elements_located
был хорошим выбором. Но как только элемент получен, DOM повторно арендуется, и вы сгенерируете StaleElementReferenceException
, если вы используете метод клика WebElement
. Также полагайте, что щелчок с использованием JS
будет просто проигнорирован, так как переданного элемента больше нет.
Поскольку until()
можно использовать для определения момента возврата элемента, почему бы не использовать его и не создать собственный класс EC:
class SelectProductTab(object):
def __init__(self, locator):
self.locator = locator
self._selected_background_image = 'url("IMG?i=ec2a883936d53541a030c2ddb511e7e8&s=p")'
def __call__(self, driver):
els = driver.find_elements(*self.locator)
if len(els) > 0:
els[0].click()
else:
return False
return els[0] if self.__is_selected(els[0]) else False
def __is_selected(self, el):
return self._selected_background_image in el.get_attribute('style')
Этот класс будет делать следующее:
Одна часть не обрабатывается, так как WebDriverWait
уже поддерживает ее, это обработка исключения. В вашем случае вы столкнетесь с StaleElementReferenceException
.
wait = WebDriverWait(driver, 30, ignored_exceptions=(StaleElementReferenceException, ))
Затем вызовите until()
с собственной реализацией класса EC:
wait.until(SelectProductTab((By.CSS_SELECTOR, "[id='r17'] > [id='f24']")))
Полный код:
with webdriver.Chrome(ChromeDriverManager().install(), options=options) as driver:
driver.get(link)
wait = WebDriverWait(driver, 15)
wait.until(EC.frame_to_be_available_and_switch_to_it(
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "object")))))
wait.until(EC.presence_of_element_located(
(By.CSS_SELECTOR, "#btn-group-cookie > input[value='Accept all cookies']"))).click()
driver.switch_to.default_content()
wait.until(EC.frame_to_be_available_and_switch_to_it(
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "iframe#CamosIFId")))))
# Sleep was removed, click is now handled inside our own EC class + will ensure the tab is selected
wait = WebDriverWait(driver, 30, ignored_exceptions=(StaleElementReferenceException, ))
wait.until(SelectProductTab((By.CSS_SELECTOR, "[id='r17'] > [id='f24']")))
for elem in wait.until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, "[data-ctcwgtname='tabTable'] [id^='v471_']")))[1:]:
print(elem.text)
Выход:
539691
539692
539693
539694
Примечание для импорта следующего импорта:
from selenium.common.exceptions import StaleElementReferenceException
Если вы точно не знаете, какую полезную нагрузку вы должны предоставить, мы мало чем можем помочь. Их API кажется очень громоздким, поскольку в качестве параметров используются отдельные буквы. Возврат, который вы получаете, кажется, связан с недопустимым запросом. Я бы предложил посмотреть на селен в таком случае.