2013-07-12 5 views
12

J'essaie d'écrire un script PowerShell pour obtenir le texte dans toutes les classes nommées "newstitle" à partir d'un site Web.Utiliser GetElementsByClassName dans un script

C'est ce que j'ai:

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

check-krpano 

Il faut évidemment beaucoup plus de peaufinage, mais jusqu'à présent, il ne fonctionne pas.

J'ai réussi à écrire un script en utilisant GetElementById, mais je ne connais pas la syntaxe de GetElementsByClassName, et pour être honnête, je n'ai pas pu trouver beaucoup d'informations à ce sujet.

REMARQUE:

J'ai fait tic tac la bonne réponse à ma question, mais ce n'est pas la solution que je l'avais choisi d'utiliser dans mon script. Bien que j'ai été en mesure de trouver le contenu dans une balise contenant une certaine classe, en utilisant 2 méthodes, ils étaient beaucoup plus lent que la recherche de liens.

est ici la sortie en utilisant Mesure-Commande:

  • Rechercher divs contenant classe 'newstitle' en utilisant parsedhtml.body -> 29,6 secondes
  • Rechercher devs contenant classe 'newstitle' en utilisant allElements -> 10.4 secondes
  • recherche pour les liens que son élément « href » contient #news -> 2.4 secondes

J'ai marqué comme utile la méthode liens réponse.

Ceci est mon script final:

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 

Répondre

13

Si vous figurez comment obtenir getElementsByClassName travailler, je voudrais savoir. Je viens de croiser hier et a couru hors du temps, donc je suis venu avec une solution de contournement:

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

On dirait un bug dans 'getElementsByTagName()' pour moi. Cependant, je suis juste tombé sur [cette réponse] (http://stackoverflow.com/a/9059206/1630171), ce qui suggère quelque chose comme ceci: '$ geturl.AllElements | ? {$ _. Classe -eq 'newstitle'} | sélectionnez innerText'. Peut-être un peu plus élégant. –

+1

Les bonnes nouvelles sont que cela fonctionne avec PowerShell v5. Je suis tombé sur ce fil après que mon code ait fonctionné sous PowerShell v4. – Robin

+0

Y at-il un moyen de stocker un des éléments que vous recevez en retour @AnsgarWiechers? Comme dans, si je reçois 5 éléments dans ma liste de sélection comme vous l'avez mentionné, et que je veux "capturer" dans un tableau comment pourrais-je faire cela? – KangarooRIOT

2

ne peut pas, pour la vie de moi, obtenir cette méthode pour travailler non plus! En fonction de ce dont vous avez besoin dans le résultat, cela peut aider;

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

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

$news 

} 

check-krpano 

Donne-moi:

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 

Vous pouvez utiliser ces propriétés directement bien sûr, donc si vous ne vouliez connaître la version la plus récente publiée de krpano, ce ferait:

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 

retournera 1.16.5 au moment de l'écriture.

Espérons que cela réalise ce que vous vouliez, quoique d'une manière différente.

EDIT:

C'est un peut-être un peu plus rapide que la tuyauterie Choisit objet:

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

Merci beaucoup pour votre réponse. Cela m'a aidé à réaliser ce que je cherchais! Bien que votre script ne soit pas exactement ce que j'ai demandé, c'est le moyen le plus rapide d'obtenir l'information, et j'ai adapté mon script inspiré par le vôtre. – RafaelGP

+0

De rien, je sais qu'il n'utilise pas les méthodes 'getElements..' de' ParsedHtml.body' mais il est plus efficace pour votre cas d'utilisation. J'ai modifié mon post avec une modification de votre script qui peut être un peu plus rapide en accédant directement aux 5 premiers éléments du tableau sans passer par select-object. Enregistré 0,5 - 1 seconde dans mes tests. –

+0

Merci pour votre aide. Accéder aux 5 premiers éléments du tableau semble être un peu plus rapide que l'utilisation de Select-Item :-) – RafaelGP

14

getElementsByClassName ne retourne pas un tableau directement, mais à la place un proxy aux résultats via COM. Comme vous l'avez découvert, la conversion en tableau n'est pas automatique avec l'opérateur []. Vous pouvez utiliser la syntaxe d'évaluation de la liste, @(), pour le forcer à un tableau premier afin que vous puissiez accéder à des éléments individuels:

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

En aparté, la conversion est effectuée automatiquement si vous utilisez le pipeline d'objets, par exemple :

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

Il est également effectué automatiquement avec la construction foreach:

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

J'ai travaillé, j'ai trouvé bizarre que gettype retourne un objet com. @ ($ table) [1] .outerHTML. Tu m'as sauvé beaucoup de temps. – Ernesto

1

Je sais que c'est une vieille question, mais je voulais ajouter une réponse à toute autre personne qui pourrait essayer de réaliser la même chose en contrôlant Internet Explorer en utilisant l'objet COM comme tel:

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

Je préfère normalement utiliser Invoke-WebRequest comme l'affiche originale, mais j'ai trouvé des cas où il me semblait avoir besoin d'une instance IE à part entière pour voir tous les éléments DOM générés par JavaScript même si je m'attendais à ce que parsedhtml.body inclue leur.

Je trouve que je pouvais faire quelque chose comme ça pour obtenir une collection d'éléments par un nom de classe:

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

J'ai observé les mêmes performances vraiment lent l'affiche originale a noté lors de l'utilisation PowerShell pour rechercher les DOM, mais en utilisant PowerShell 3.0 et IE11, Measure-Command montre que ma collection de classes se trouve dans un document HTML de 125 Ko en 280 ms.

Questions connexes