Используйте 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 мс.