Seriál Windows PowerShell v3 – jak na webové stránky (část 34.)

Velmi často se stane, že potřebujete pracovat s webovými stránkami. Aťse jedná o stažení celé stránky nebo zpracování její části (třeba na základě daného pravidla – pouze obrázky).

V PowerShellu v2 bylo potřeba použít .NET třídu Net.WebClient. Například:

PS C:\> $w = New-Object Net.WebClient 
PS C:\> $page = $w.DownloadString('http://www.powershell.cz') 
PS C:\> $page -match '\<title\>(?<Title>.*?)\</title\>' 
PS C:\> $matches.Title 
PowerShell.cz &laquo; Get-World | ConvertTo-PowerShell

Titulek stránky můžete potvrdit pohledem na vlastní stránku.

image

Nejprve vytvoříme proměnnou pro náš „webový“ objekt. Poté použijeme jeho metodu DownloadString a text stránky uložíme do proměnné page. V dalším kroku si pomocí regulárního výrazu zjistíme text titulku stránky, který si v další řádce vypíšeme.

Stažení stránky je tedy velmi jednoduché. Pro složitější operace je potřeba použít např. regulární výrazy. Což už pro některé administrátory může být nelehkou překážkou. Jak je vidět ze zápisu '\<title\>(?<Title>.*?)\</title\>' nejedná se o žádnou oddechovku. Regulárními výrazy se zabývat nebudeme a zkusíme si ještě zjednodušit stažení stránky.

Pojďme tedy tedy přejít na PowerShell v3.

Invoke-WebRequest

Pojďme si tedy předchozí příklad přepsat:

PC C:\> $www = Invoke-WebRequest -Uri 'http://www.powershell.cz' 
PC C:\> $www.ParsedHtml.title 
PowerShell.cz « Get-World | ConvertTo-PowerShell

Velice jednoduché a efektivní řešení. Pojďme se blíže podívat na proměnnou www.

PS C:\> $www | Get-Member

TypeName: Microsoft.PowerShell.Commands.HtmlWebResponseObject

Name              MemberType Definition 
----              ---------- ---------- 
Equals            Method     bool Equals(System.Object obj) 
GetHashCode       Method     int GetHashCode() 
GetType           Method     type GetType() 
ToString          Method     string ToString() 
AllElements       Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection AllElements {get;} 
BaseResponse      Property   System.Net.WebResponse BaseResponse {get;set;} 
Content           Property   string Content {get;} 
Forms             Property   Microsoft.PowerShell.Commands.FormObjectCollection Forms {get;} 
Headers           Property   System.Collections.Generic.Dictionary[string,string] Headers {get;} 
Images            Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection Images {get;} 
InputFields       Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection InputFields {get;} 
Links             Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection Links {get;} 
ParsedHtml        Property   mshtml.IHTMLDocument2 ParsedHtml {get;} 
RawContent        Property   string RawContent {get;} 
RawContentLength  Property   long RawContentLength {get;} 
RawContentStream  Property   System.IO.MemoryStream RawContentStream {get;} 
Scripts           Property   Microsoft.PowerShell.Commands.WebCmdletElementCollection Scripts {get;} 
StatusCode        Property   int StatusCode {get;} 
StatusDescription Property   string StatusDescription {get;}

Dalšími zajímavým vlastnostmi jsou například Images nebo Links.

PS C:\> $www.Images | Format-Table tagName, title, src -Auto

tagName title src 
------- ----- --- 
IMG     AddTFS             http://powershell.cz/wp-content/uploads/AddTFS-64x64.png 
IMG                        http://powershell.cz/wp-includes/images/smilies/icon_smile.gif 
IMG                        http://powershell.cz/wp-content/uploads/AddTFS.png 
IMG                        http://powershell.cz/wp-content/uploads/tfslogin-277x300.png 
IMG                        http://powershell.cz/wp-content/uploads/tfsselect.png 
IMG                        http://powershell.cz/wp-content/uploads/tfsselect2.png 
IMG                        http://powershell.cz/wp-content/uploads/tfsOpen.png 
IMG                        http://powershell.cz/wp-content/uploads/tfschoosefile.png 
IMG     FailFastConsole    http://powershell.cz/wp-content/uploads/FailFastConsole-64x64.png 
IMG     FailFastConsole    http://powershell.cz/wp-content/uploads/FailFastConsole-150x150.png 
IMG     FailFastWarningBox http://powershell.cz/wp-content/uploads/FailFastWarningBox-150x150.png 
IMG     FailFastEvent      http://powershell.cz/wp-content/uploads/FailFastEvent-150x150.png 
IMG                        http://powershell.cz/wp-includes/images/smilies/icon_smile.gif

PS C:\> $www.Links | Format-Table title, href -Auto

title                href 
-----                ---- 
                     http://powershell.cz 
Follow me on Twitter https://twitter.com/Makovec 
RSS Feeds            http://powershell.cz/?feed=rss2 
You are Home         http://powershell.cz 
about_PowerShell.cz  http://powershell.cz/about/
 
(…)

Použitím přístupu k webu (nezávisle na použité metodě), můžeme vytvářet velice zajímavé funkce. Jednu z nich mám ve svém PowerShell profilu – kurzy zahraničních měn. Vzhledem k tomu, že na pracovním počítači mám PowerShell v3 od minulého týdne, zkusím přepsat současnou verzi (s použitím Net.WebClient) za pomoci Invoke-WebRequest.

Nejprve potřebujeme adresu, na které jsou aktuální kurzy uloženy. Jako ideální se jeví ČNB:

PS C:\> $url = 'http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt'

Poté si pomocí cmdletu stáhneme data do proměnné – asi bychom to v tomto případě dělat nemuseli a mohli bychom rovnou použít rouru pro další zpracování. Nicméně, když používám funkce, mám rád jednotlivé části oddělené.

PS C:\> Invoke-WebRequest -Uri $url

Pokud budete tento cmdlet používat častěji, budete možná používat jeho alias iwr. Dále už jenom zpracujeme stažená data. Využijeme cmdlet Select-Object – od verze 3 obsahuje nový parameter, Skip. Tím určujeme, kolik prvků od začátku kolekce chceme přeskočit. Vzhledem k tomu, že struktura staženého souboru je následující:

PS C:\> $kurzy.Content 
01.03.2013 #43 
země|měna|množství|kód|kurz 
Austrálie|dolar|1|AUD|20,153 
Brazílie|real|1|BRL|9,944 
Bulharsko|lev|1|BGN|13,129 
(…)

Přeskočíme první dvě řádky a vytvoříme si vlastní popisky jednotlivých sloupců. Veškerou práci již nyní provedeme na jedné řádce.

PS C:\> $kurzy.Content -split "`n" | Select-Object -Skip 2 | ` 
  ConvertFrom-Csv -Delimiter '|' -Header 'Zeme','Mena','Mnozstvi','Kod','Kurz'

Nejprve se ujistíme, že každá řádka bude obsahovat jeden záznam. Poté přeskočíme první dva z nich (datum a hlavičku) a ze zbývajících vytvoříme pomocí cmdletu ConvertFrom-Csv objekt. Určíme si vlastní hlavičku (názvy vlastností jednotlivých objektů). V tomto případě to děláme proto, že zde nechceme české znaky. Výsledkem jsou objekty s kurzy jednotlivých měn:

Zeme      Mena  Mnozstvi Kod Kurz 
----      ----  -------- --- ---- 
Austrálie dolar 1        AUD 20,153 
Brazílie  real  1        BRL 9,944 
Bulharsko lev   1        BGN 13,129

Pokud bychom si vytvořili převodní funkci, mohli bychom o něco jednodušeji volat následující příklad.

PS C:\> ($prevod | where Kod -eq USD).Kurz 
19,749

Jak je vidět, použili jsme novou syntaxi volání cmdletu Where-Object. Nahrazuje původní: | Where { $_.Kod –eq ‘USD’ }

Invoke-WebRequest se hodí i na složitější zpracování webových stránek. Zkusme si vyhledat nějaké informace na Bingu (budu hledat informace o mém účtu na Twitteru). Dotaz má tvar:

PS C:\> $q = ‘http://www.bing.com/search?q=twitter+makovec&qs=n&form=QBLH&filt=all&pq=twitter+makovec&sc=0-11&sp=-1&sk=’

PS C:\> $a = iwr $q

PS C:\> $a.ParsedHtml.getElementById('results_container').GetElementsByTagName('div') | where ClassName -eq 'sa_cc' | Select -First 2 -Property innerText

innerText 
--------- 
makovec (sonjaigor) on Twitter... 
David Moravec (makovec) on Twitter...

Malá poznámka – v tuto chvíli vím, že můj Twitter účet je na druhém místě (proto ona „magická konstanta“ Select –First 2). V případě parsování neznámého textu, bych musel ještě přidat kontrolu výsledků a porovnání proti známé hodnotě.

V proměnné q máme uložený link na Bing a do proměnné a si uložíme odpověď. Vzhledem k tomu, že používáme vlastnost ParsedHtml, máme přístup k jednotlivým prvkům stránky. Při krátké práci se zdrojem stránky jsem zjistil jména potřebných elementů a byl schopen je v jedné řádce získat. U výsledného textu mě pak zajímala vlastnost innerText, která obsahuje text elementu zbavených všech HTML značek. Výsledek na webu vypadá takto:

image

Vidíte, že s trochou práce se vám naplno otevírá svět webuVeselý obličej Určitě jste schopni vymyslet množství příkladů ze života, kde se popsané techniky hodí. Hodně zdaru při jejich implementaci.