Используйте GetElementsByClassName в script
Я пытаюсь написать PowerShell script, чтобы получить текст во всех классах с именем "newstitle" с веб-сайта.
Это то, что у меня есть:
function check-krpano {
$geturl=Invoke-WebRequest http://krpano.com/news/
$news=$geturl.parsedhtml.body.GetElementsByClassName("newstitle")[0]
Write-Host "$news"
}
check-krpano
Очевидно, что требуется гораздо больше настроек, но пока это не работает.
Мне удалось написать script с помощью GetElementById, но я не знаю синтаксиса для GetElementsByClassName, и, честно говоря, я не смог найти много информации об этом.
Примечание:
Я поставил правильный ответ на мой вопрос, но это не то решение, которое я выбрал для использования в script.
Хотя мне удалось найти содержимое в теге, содержащем определенный класс, используя 2 метода, они были намного медленнее, чем поиск ссылок.
Вот результат с использованием Measure-Command:
- Искать divs, содержащие класс 'newstitle', используя parsedhtml.body → 29.6 секунд
- Поиск разработчиков, содержащих класс 'newstitle', используя Allelements → 10.4 секунд
- Поиск ссылок, которые содержит его элемент 'href' #news → 2.4 секунды
Итак, я пометил как полезный ответ метода Links.
Это мой последний script:
function check-krpano {
Clear-Host
$geturl=Invoke-WebRequest http://krpano.com/news
$news = ($geturl.Links |Where href -match '\#news\d+' | where class -NotMatch 'moreinfo+' )
$news.outertext | Select-Object -First 5
}
check-krpano
Ответы
Ответ 1
Если вы выясните, как заставить GetElementsByClassName работать, я хотел бы знать. Я просто столкнулся с этим вчера и закончил время, поэтому придумал обходное решение:
$geturl.ParsedHtml.body.getElementsByTagName('div') |
Where {$_.getAttributeNode('class').Value -eq 'newstitle'}
Ответ 2
getElementsByClassName
не возвращает массив напрямую, а вместо этого прокси-сервер для результатов через COM. Как вы обнаружили, преобразование в массив не является автоматическим с помощью оператора []
. Вы можете использовать синтаксис оценки списка, @()
, чтобы сначала перенести его в массив, чтобы вы могли получить доступ к отдельным элементам:
@($body.getElementsByClassName("foo"))[0].innerText
В качестве альтернативы преобразование выполняется автоматически, если вы используете конвейер объекта, например:
$body.getElementsByClassName("foo") | Select-Object -First 1
Он также выполняется автоматически с помощью конструкции foreach
:
foreach ($element in $body.getElementsByClassName("foo"))
{
$element.innerText
}
Ответ 3
Нельзя, для моей жизни, заставить этот метод работать!
В зависимости от того, что вам нужно в результате, это может помочь;
function check-krpano {
$geturl=Invoke-WebRequest http://krpano.com/news
$news=($geturl.Links|where href -match '\#news\d+')[0]
$news
}
check-krpano
Дает мне ответ:
innerHTML : krpano 1.16.5 released
innerText : krpano 1.16.5 released
outerHTML : <A href="#news1165">krpano 1.16.5 released</A>
outerText : krpano 1.16.5 released
tagName : A
href : #news1165
Вы можете использовать эти свойства напрямую, поэтому, если бы вы только хотели узнать самую недавно выпущенную версию krpano, это сделало бы это:
function check-krpano {
$geturl=Invoke-WebRequest http://krpano.com/news
$news=($geturl.Links|where href -match '\#news\d+')[0]
$krpano_version = $news.outerText.Split(" ")[1]
Write-Host $krpano_version
}
check-krpano
вернет 1.16.5
во время записи.
Надеюсь, что вы достигнете того, чего хотите, хотя и по-другому.
EDIT:
Это возможно немного быстрее, чем прохождение через select-object:
function check-krpano {
$geturl=Invoke-WebRequest http://krpano.com/news
($geturl.Links|where href -match '\#news\d+'|where class -notmatch 'moreinfo+')[0..4].outerText
}
Ответ 4
Я понимаю, что это старый вопрос, но я хотел добавить ответ для всех, кто может попытаться добиться того же самого, контролируя Internet Explorer с помощью COM-объекта, такого как:
$ie = New-Object -com internetexplorer.application
$ie.navigate($url)
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 100; }
Обычно я предпочитаю использовать Invoke-WebRequest в качестве исходного плаката, но я нашел случаи, когда мне казалось, что мне нужен полноценный IE-экземпляр, чтобы увидеть все элементы DOM, созданные JavaScript, даже если я ожидал бы, что parsedhtml.body включит их.
Я обнаружил, что могу сделать что-то подобное, чтобы получить набор элементов по имени класса:
$titles = $ie.Document.body.getElementsByClassName('newstitle')
foreach ($storyTitle in $titles) {
Write-Output $storyTitle.innerText
}
Я наблюдал ту же самую очень медленную производительность, которую оригинальный плакат отмечал при использовании PowerShell для поиска DOM, но с использованием PowerShell 3.0 и IE11 Measure-Command показывает, что моя коллекция классов найдена в HTML-документе 125 KB в 280 мс.