Я использую Selenium для поиска элементов в HTML-документе, отображаемом в браузере Chrome. HTML создан не мной, и, похоже, у него много проблем. У меня нет возможности изменить его поколение, мне просто приходится работать с тем, что у меня есть.
Вот фрагмент HTML-кода элемента, который я пытаюсь найти по его тексту «>Общая юридическая информация по аренде».
<div class = "TableTitlebar HeaderedTableTitlebar">
<div class = "FastTitlebar">
<div class = "FastTitlebarCaptionWrapper">
<h3 id = "caption2_Dd-53" class = " FastTitlebarCaption">General Lease Legal Information</h3>
</div>
Это много уровней в очень запутанной конструкции. Я пробовал разные комбинации критериев поиска для FindElement. Я всегда получаю сообщение об ошибке «Элемент не найден».
Большая проблема заключается в том, что веб-сайт, генерирующий этот HTML-код, будет время от времени заново генерировать идентификаторы элементов при обновлении страницы. Таким образом, использование поиска по идентификатору в конечном итоге сломается. Кроме того, атрибуты, такие как имена классов, являются просто повторениями идентификаторов и не дают стабильного представления того, что представляет собой элемент.
Вот как попасть на веб-страницу. Откройте браузер и перейдите по адресу: https://oktap.tax.ok.gov/OkTAP/web?link=PUBLICPUNLKP
Установите переключатель «Поиск по PUN:». Введите значение «029-123551-0-0000» Прокрутите страницу вниз и выберите кнопку «Поиск». Чуть выше кнопки «Поиск» будет строка текста с выделенным в крайнем левом углу «029-123551-0-0000». Выберите эту ссылку. На новой странице выберите вкладку «Доступ для печати» в правой части строки меню. На новой странице прокрутите вниз, чтобы найти таблицу с общей юридической информацией по аренде.
Поскольку аннотации в HTML очень плохие, единственный способ найти следующий элемент таблицы, содержащий искомые данные, без жесткого кодирования идентификатора для поиска текста заголовка в опубликованном мной фрагменте HTML, а затем выполнить поиск первой таблицы. элемент после этого.
Это действительно беспорядок. Я не уверен, что сайт можно очистить из-за плохой конструкции HTML. Я использую C#, поскольку это уровень владения мной и другими программистами в моей компании. Я открыт для предложений о том, как найти нужный элемент, а также провести реинжиниринг всего приложения.





Я не уверен, в чем проблема... на этой странице МНОГО идентификаторов, и они согласованы (не случайны при каждой загрузке страницы и т. д.).
Приведенный ниже код работает в соответствии с предоставленными вами шагами и печатает все ячейки в таблице «Общая юридическая информация по аренде». Он не использует никаких идентификаторов, поскольку вы сказали, что это проблема.
IWebDriver driver = new ChromeDriver();
driver.Manage().Window.Maximize();
string url = "https://oktap.tax.ok.gov/OkTAP/web?link=PUBLICPUNLKP";
driver.Url = url;
string pun = "029-123551-0-0000";
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(ExpectedConditions.ElementIsVisible(By.Id("caption2_Dd-5"))); // wait for element on page to make sure page is loaded
ReadOnlyCollection<IWebElement> inputs = driver.FindElements(By.XPath("//tr[.//span[text()='Search By PUN:']]//input"));
inputs.ElementAt(0).Click(); // Search By PUN radio button
inputs.ElementAt(1).SendKeys(pun); // Search By PUN field
driver.FindElement(By.XPath("//span[text()='Search']")).Click(); // Search button
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath("//table[@data-delmsg][.//a[text()='PUN']]//td//a"))).Click(); // Click first result link
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath("//span[text()='Printable']"))).Click(); // Click "Printable" tab
IWebElement table = wait.Until(ExpectedConditions.ElementIsVisible(By.XPath("//div[contains(@class,'TableContainer')][.//h3[text()='General Lease Legal Information']]//table[contains(@class,'DocTableNormal')]")));
string headers = string.Join(",", table.FindElements(By.CssSelector("th")).Select(e => e.Text).ToArray());
Console.WriteLine(headers); // write headers
ReadOnlyCollection<IWebElement> cells = table.FindElements(By.CssSelector("td"));
string cellText = string.Join(",", cells.Select(e => e.Text).ToArray());
Console.WriteLine(cellText); // write cells
Выход
Q4,Q3,Q2,Q1,Section,Township,Range
,,,AL,36,01N,09E
Вы можете записать это в CSV-файл и открыть его как лист Excel или что-то еще, что вам нужно.
«Большая проблема заключается в том, что веб-сайт, генерирующий этот HTML-код, будет время от времени заново генерировать идентификаторы элементов при обновлении страницы». Я предполагаю, что это кэшировано
@mattsharp Я запускал свой код более 10 раз и ни разу не заметил, чтобы идентификаторы изменились... Я не знаю.
@mattsharp Я добавил версию, которая не использует никаких идентификаторов, если вам интересно.
Вы могли бы предположить, что он кэширован до <1 часа, так что, возможно, вы правы, или, может быть, они обновляют его при развертывании (идентификаторы восстанавливаются) или что-то в этом роде. Независимо от того, выглядит ли ваш код, он должен работать
Идентификаторы не меняются каждый раз при перезагрузке страницы. Но в генерации HTML на стороне сервера есть что-то такое, что, как я предполагаю, в логике генерации HTML вносится корректировка, тогда в идентификаторах может произойти одно или несколько изменений. Это делает очистку движущейся целью.
@BartAtRanch Понятно. Я обновил код, чтобы не использовать какие-либо идентификаторы, и он работает нормально. Вы пробовали?
Спасибо за пример кода. Использование «подождать» может помочь. Но даже в вашем «неидентификационном» коде ваши идентификаторы могут быть изменены. Значения типа «caption2_Dd-5» будут меняться при изменении идентификаторов подключенных элементов. Кроме того, различные разделы HTML настолько идентичны, без какого-либо уникального объяснения того, для чего используется элемент, что типы или классы стилей повторяются, так что при поиске можно получить любой из связанных элементов, а не тот, который вам нужен. .
Затем дополнительный вопрос о том, как Selenium анализирует HTML. Судя по поведению и логике рабочего процесса в коде, который мне изначально дал первоначальный автор, похоже, что когда Selenium выполняет FindElement, он перемещает начальный указатель для следующего поиска в позицию последнего успешного поиска. Я могу с этим работать, если это так. Мне просто нужно будет строго подходить к структуре HTML. Но что произойдет с указателем начала поиска, если запрошенный элемент не будет найден?
Являются ли вызовы Selenium, такие как FindElement, синхронными или асинхронными в зависимости от завершения возврата управления вызывающей программе и действий на целевой веб-странице? Я предполагаю, что асинхронно, если есть необходимость подождать. Просто хочу убедиться, что понимаю, как работает драйвер.
Код не подлежит изменению идентификаторов, если я не использую какие-либо идентификаторы для поиска элементов. Вы пробовали его запустить? Это сработало?
Я не понимаю ваш второй комментарий. Весь код Selenium синхронен... выполнение кода блокируется на каждом этапе. Ожидание необходимо из-за различий во времени загрузки страниц и т. д.
Я пробую ваш код. Прекрасно работает. Ты делаешь это; wait.Until(ExpectedConditions.ElementIsVisible(By.Id("caption2_Dd-5"))); // ждем элемента на странице, чтобы убедиться, что страница загружена. Из-за изменения идентификаторов при регенерации HTML то, что изначально было «Caption2_Dd-5», когда был написан код, могло быть изменено на «Caption2_Dd-8». Такое уже случалось раньше, и я уверен, что это произойдет снова. Я обошел эту проблему, вместо того чтобы использовать элементы записи PUN в качестве цели поиска «подождать, пока не станет видимым».
Я удалил первый блок кода, содержащий идентификаторы. Используйте оставшийся блок кода... он должен работать без проблем и не требует постоянного обслуживания.
Конечно... Я хотел вернуться и найти новую цель для этой проверки, в которой не использовался идентификатор. По какой-то причине мне было трудно использовать ожидания со многими элементами с идентификаторами, поэтому я просто выбрал элемент на странице, чтобы убедиться, что страница загружена и запущена с ним.
Джефф, если вы рассмотрите возможность небольшой консультационной работы, напишите по адресу [email protected].
@BartAtRanch Только что отправил вам письмо.
Вы можете удалить цикл
row in rows, если в этой таблице есть только одна строка. Я не был уверен, поэтому на всякий случай добавил цикл.