2013-07-12 118 views
12

我試圖編寫一個PowerShell腳本來從網站中獲取名爲「newstitle」的所有類中的文本。在腳本中使用GetElementsByClassName

這是我有:

function check-krpano { 
    $geturl=Invoke-WebRequest http://krpano.com/news/ 
    $news=$geturl.parsedhtml.body.GetElementsByClassName("newstitle")[0] 
    Write-Host "$news" 
} 

check-krpano 

這顯然需要更多的調整,但到目前爲止,這是行不通的。

我設法使用GetElementById編寫腳本,但我不知道GetElementsByClassName的語法,說實話,我一直沒能找到關於它的許多信息。

注:

我已經選中正確回答我的問題,但是這並不是說我已經選擇了我的腳本使用該解決方案。

雖然我能夠找到包含某一類,採用2種方法標籤中的內容,他們很慢,當搜索的鏈接。

這裏是用測量命令的輸出:

  • 搜索使用parsedhtml.body包含類「newstitle」的div - >29.6秒
  • 搜索包含類「newstitle」使用Allelements開發者 - > 10.4秒
  • 搜索鏈接這些元素的「href」屬性包含#news - >2.4秒

所以我已經標記爲有用的鏈接方法的答案。

這是我最後的腳本:

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 

回答

13

如果你弄清楚如何獲得getElementsByClassName方法來工作,我想知道。我只是碰到了這個昨天跑出的時間,所以我想出了一個解決辦法:

$geturl.ParsedHtml.body.getElementsByTagName('div') | 
    Where {$_.getAttributeNode('class').Value -eq 'newstitle'} 
+3

看起來像'getElementsByTagName()'中的一個錯誤。然而,我剛剛遇到[這個答案](http://stackoverflow.com/a/9059206/1630171),這表明這樣的事情:'$ geturl.AllElements | ? {$ _。Class -eq'newstitle'} |選擇innerText'。可能會更優雅一點。 –

+1

好消息是它適用於PowerShell v5。我的代碼在PowerShell v4下運行後遇到此線程。 – Robin

+0

有沒有方法可以存儲您收到的@AnsgarWiechers元素之一?就像在我的選擇列表中,如你所提到的那樣,如果我返回5個元素,並且我想將它「捕獲」到一個數組中,我該怎麼做? – KangarooRIOT

2

不能爲我的生活,讓該方法要麼工作!

取決於你需要什麼結果,但這可能有幫助;

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

。希望達到你想要什麼,儘管以不同的方式。

編輯:

這比通過選擇對象管道快可能有點:

function check-krpano { 
$geturl=Invoke-WebRequest http://krpano.com/news 

($geturl.Links|where href -match '\#news\d+'|where class -notmatch 'moreinfo+')[0..4].outerText 

} 
+0

非常感謝您的回答。它幫助我實現了我期待的目標!儘管你的腳本並不完全符合我的要求,但它是獲取信息的最快方式,而且我修改了受你的啓發的腳本。 – RafaelGP

+0

不客氣,我知道它沒有使用'ParsedHtml.body'的getElements..'方法,但它對於你的用例更有效。我編輯了我的文章,對腳本進行了修改,可以通過直接訪問前5個數組項直接訪問選擇對象而稍微快速一點。在我的測試中保存0.5 - 1秒。 –

+0

感謝您的幫助。訪問前5個數組項目似乎比使用Select-Item快一點:-) – RafaelGP

14

getElementsByClassName不經由COM直接而是返回一個數組的代理的結果。正如您發現的那樣,使用[]運算符不會自動轉換爲數組。您可以使用列表評估語法,@(),首先將它強制到一個數組,這樣就可以訪問各個元素:

@($body.getElementsByClassName("foo"))[0].innerText 

順便說一句,轉換如果你使用對象的管道,如自動執行:

foreach ($element in $body.getElementsByClassName("foo")) 
{ 
    $element.innerText 
} 
+0

工作過,我發現gettype返回com對象很奇怪。 @($表)[1] .outerHTML。你爲我節省了很多時間。 – Ernesto

1

$body.getElementsByClassName("foo") | Select-Object -First 1 

它也自動地與foreach構建體進行

我意識到這是一個老問題,但我想添加爲別人誰可能會嘗試使用COM對象像這樣控制Internet Explorer,以達到相同的答案:

$ie = New-Object -com internetexplorer.application 
$ie.navigate($url) 
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 100; } 

我通常喜歡使用Invoke-WebRequest作爲原始的海報,但我發現了一些情況,好像我需要一個完整的IE實例來查看所有JavaScript生成的DOM元素,即使我期望parsedhtml.body包含他們。

我發現我可以做這樣的事情由一個類名來獲取元素的集合:

$titles = $ie.Document.body.getElementsByClassName('newstitle') 
foreach ($storyTitle in $titles) { 
    Write-Output $storyTitle.innerText 
} 

我觀察到同樣的速度很慢的性能原始的海報使用PowerShell來搜索DOM時指出,但使用PowerShell 3.0和IE11,Measure-Command顯示我的集合類在280毫秒的125 KB HTML文檔中找到。