#javascript #xpath #applescript
#javascript #xpath #applescript
Вопрос:
Я пишу скрипт AppleScript для очистки веб-страницы в Safari, и есть часть, с которой я уже давно борюсь.
Это возвращает требуемый текст: log (do JavaScript "document.querySelector('h1 > span').innerHTML;" in front document)
И это не: log (do JavaScript "document.evaluate('//h1/span/text()[normalize-space()]', document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" in front document)
В инспекторе браузера работают оба подхода, но в AppleScript ни один вариант xpaths не сработал для меня.
И мне действительно нужно использовать document.evaluate
функцию, чтобы делать что-то вроде document.evaluate("//p[contains(., 'Metrics')]/following-sibling::p[1]/text()[normalize-space()]", document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
того же скрипта.
Как это нужно переписать, чтобы начать выдавать результаты в AppleScript?
Пример HTML-кода:
<html>
<body>
<h1>
<span>Test Entry</span>
</h1>
</body>
</html>
Вывод веб-инспектора Safari (демонстрирует, что оба querySelector
и evaluate
работают без проблем):
> document.querySelector('h1 > span').innerHTML;
< "Test Entry"
> document.evaluate('//h1/span/text()[normalize-space()]', document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
< "Test Entry"
Код редактора AppleScript:
tell application "Safari"
log (do JavaScript "document.querySelector('h1 > span').innerHTML;" in front document)
log (do JavaScript "document.evaluate('//h1/span/text()[normalize-space()]', document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" in front document)
end tell
Вывод редактора AppleScript (демонстрирует, что querySelector
работает, а evaluate
не работает):
(*Test Entry*)
(**)
Комментарии:
1.
And this doesn’t
— что он возвращает?2. Ничего. Он также не вызывает никаких ошибок, и я не уверен, как проверить, куда он попадает. Просто ни один вариант xpath, который я пробовал, не сработал. 🙁
3. Можете ли вы воспроизвести это поведение с минимальным источником ввода, которым вы могли бы поделиться?
4. Да, теперь я обновил сообщение минимальным образцом HTML и вывел код, созданный в Safari Web Inspector (где код работает) и Script Editor (где
document.evaluate
ничего не возвращает). Спасибо, что спросили.5. @Vladimir, я написал ответ с высказанными предложениями. Что касается вашего другого примера, я бы предложил проверить, работает ли
log (do JavaScript "document.querySelector('h1 > span');" in front document)
log (do JavaScript "document.querySelector('h1 > span').firstChild;" in front document)
он так же хорошо или нет(**)
.
Ответ №1:
Выражение XPath, которое вы используете для своей минимальной выборки ( //h1/span/text()[normalize-space()]
), выбирает текстовый узел в DOM браузера и с помощью document.evaluate('//h1/span/text()[normalize-space()]', document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
вашего кода Javascript возвращает этот текстовый узел (https://dom.spec.whatwg.org/#text ). Хотя специализированный инспектор браузера может затем напрямую отображать содержимое текстового узла, похоже, что ваша консоль AppleScript этого не делает.
Если вы хотите, чтобы ваш код Javascript возвращал простую строку со значением текстового узла, вы можете использовать data
свойство (https://dom.spec.whatwg.org/#dom-characterdata-data ) текстового узла, подобно тому, как вы использовали innerHTML
свойство узла элемента, которое вы получили от querySelector
вызова.
Итак
document.evaluate('//h1/span/text()[normalize-space()]', document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
в Javascript возвращает текстовый узел DOM, и чтобы получить строку с содержимым текстового узла, используйте data
свойство с, например
document.evaluate('//h1/span/text()[normalize-space()]', document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.data
Другой вариант — использовать
document.evaluate('//h1/span/text()[normalize-space()]', document.body, null, XPathResult.STRING_TYPE, null).stringValue
Поскольку вам нужен минимальный пример, обратите также внимание, что, пока вы используете абсолютный XPath, начинающийся с /
или //
, вы ничего не получаете, используя document.body
в качестве второго аргумента document.evaluate
, document
достаточно также передать один и дать тот же результат.