2013-07-12 12 views
12

Próbuję napisać skrypt PowerShell, aby uzyskać tekst ze wszystkich klas o nazwie "newstitle" ze strony internetowej.Użyj GetElementsByClassName w skrypcie

To jest to, co mam:

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

check-krpano 

To oczywiście wymaga dużo bardziej skomplikowany, ale do tej pory, to nie działa.

Udało mi się napisać skrypt za pomocą GetElementById, ale nie znam składni dla GetElementsByClassName, i szczerze mówiąc, nie byłem w stanie znaleźć wiele informacji na ten temat.

UWAGA:

mam zaznaczyć właściwą odpowiedź na moje pytanie, ale nie jest to rozwiązanie, które ja wybraliśmy do wykorzystania w moim skrypcie.

Mimo że udało mi się znaleźć zawartość w tagu zawierającym pewną klasę, używając dwóch metod, były one znacznie wolniejsze niż wyszukiwanie linków.

Oto wynik używając Measure-Command:

  • Szukaj div class 'newstitle zawierających' używając parsedhtml.body -> 29,6 sekund
  • Szukaj deweloperów zawierające klasę 'newstitle' używając Allelements -> 10.4 sekundy
  • wyszukiwania dla łączących jego elementem „href” zawiera #news -> 2,4 sekundy

Więc zostały oznaczone jako użyteczna metoda Linki odpowiedź.

To jest mój ostateczny scenariusz:

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 

Odpowiedz

13

Jeśli dowiedzieć się, jak dostać getElementsByClassName do pracy, chciałbym wiedzieć. Właśnie wczoraj wpadłem na to i zabrakło mi czasu, więc wymyśliłem obejście:

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

Wygląda jak błąd w 'getElementsByTagName()' dla mnie. Jednak właśnie natknąłem się na [tę odpowiedź] (http://stackoverflow.com/a/9059206/1630171), która sugeruje coś takiego: '$ geturl.AllElements | ? {$ _. Klasa -eq "newstitle"} | wybierz innerText'. Może być trochę bardziej elegancko. –

+1

Dobra wiadomość jest taka, że ​​działa z PowerShell v5. Natknąłem się na ten wątek po tym, jak mój kod złamał się w PowerShell v4. – Robin

+0

Czy istnieje sposób na zapisanie jednego z elementów otrzymanych z powrotem @AnsgarWiechers? Tak jak w przypadku, gdy otrzymam 5 elementów z powrotem na mojej liście wyboru, tak jak wspomniałeś, i chcę "schwytać" ją w tablicy, jak mogłem to zrobić? – KangarooRIOT

2

Nie mogę, na całe życie, również zastosować tej metody!

W zależności od tego, co jest potrzebne w wyniku, może to pomóc;

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

$news=($geturl.Links|where href -match '\#news\d+')[0] 

$news 

} 

check-krpano 

Daje mi:

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 

Można użyć tych właściwości bezpośrednio oczywiście, więc jeśli tylko chciał wiedzieć, najbardziej ostatnio wydana wersja krpano, byłoby to zrobić:

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 

powróci 1.16.5 w momencie pisania.

Nadzieję, że osiąga to, co chcesz, choć w inny sposób.

Edycja:

To może nieco szybciej niż w przypadku instalacji poprzez SELECT obiektu

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

Dziękuję bardzo za odpowiedź. Pomógł mi osiągnąć to, czego szukałem! Chociaż twój scenariusz nie jest dokładnie tym, o co prosiłem, jest to najszybszy sposób na zdobycie informacji, a ja zaadaptowałem skrypt zainspirowany twoim. – RafaelGP

+0

Nie ma za co, wiem, że nie używa on metod 'getElements..'' 'ParsedHtml.body', ale jest bardziej wydajny w twoim przypadku użycia. Zmodyfikowałem swój wpis, modyfikując skrypt, który może być trochę szybszy, uzyskując dostęp do pierwszych 5 elementów tablicy bezpośrednio, bez instalacji pipeline do wybranego obiektu. Zapisane 0,5 - 1 sekundy w moich testach. –

+0

Dzięki za pomoc. Uzyskanie dostępu do pierwszych 5 elementów tablicy wydaje się być trochę szybsze niż użycie Select-Item :-) – RafaelGP

14

getElementsByClassName nie zwraca tablicę bezpośrednio lecz proxy wyników przez COM. Jak odkryłeś, konwersja do tablicy nie jest automatyczna z operatorem []. Można użyć składni oceny listy, @(), aby zmusić go do tablicy pierwszy, dzięki czemu można uzyskać dostęp do poszczególnych elementów:

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

Tak na marginesie, konwersja jest wykonywane automatycznie w przypadku korzystania z rurociągu obiektu, np :

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

jest również wykonywane automatycznie z foreach konstrukt:

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

Pracowałem, stwierdziłem, że to dziwne, że gettype zwróciło obiekt com. @ ($ table) [1] .outerHTML. Uratowałeś mi dużo czasu. – Ernesto

1

Zdaję sobie sprawę, że jest to stare pytanie, ale chciałem dodać odpowiedź dla każdego, kto może starać się osiągnąć to samo poprzez kontrolowanie Internet Explorer przy użyciu obiektu COM jak takie:

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

Zwykle wolą użyj Invoke-WebRequest, jak zrobił to oryginalny plakat, ale znalazłem przypadki, w których wydawało mi się, że potrzebuję pełnowartościowej instancji IE, aby zobaczyć wszystkie elementy DOM generowane przez JavaScript, mimo że spodziewałbym się, że parsedhtml.body im.

odkryłem, że mogę zrobić coś takiego, aby uzyskać zbiór elementów przez nazwę klasy:

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

zauważyłem ten sam bardzo niska wydajność oryginalny plakat zanotowany przy użyciu PowerShell, aby przeszukać dom, ale używając PowerShell 3.0 i IE11, Measure-Command pokazuje, że moja kolekcja klas znajduje się w dokumencie HTML o długości 125 KB w 280 ms.

Powiązane problemy