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('https://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 'https://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 https://powershell.cz/wp-content/uploads/AddTFS-64x64.png
IMG https://powershell.cz/wp-includes/images/smilies/icon\_smile.gif
IMG https://powershell.cz/wp-content/uploads/AddTFS.png
IMG https://powershell.cz/wp-content/uploads/tfslogin-277x300.png
IMG https://powershell.cz/wp-content/uploads/tfsselect.png
IMG https://powershell.cz/wp-content/uploads/tfsselect2.png
IMG https://powershell.cz/wp-content/uploads/tfsOpen.png
IMG https://powershell.cz/wp-content/uploads/tfschoosefile.png
IMG FailFastConsole https://powershell.cz/wp-content/uploads/FailFastConsole-64x64.png
IMG FailFastConsole https://powershell.cz/wp-content/uploads/FailFastConsole-150x150.png
IMG FailFastWarningBox https://powershell.cz/wp-content/uploads/FailFastWarningBox-150x150.png
IMG FailFastEvent https://powershell.cz/wp-content/uploads/FailFastEvent-150x150.png
IMG                        https://powershell.cz/wp-includes/images/smilies/icon\_smile.gif

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

title href
----- ----
                     https://powershell.cz
Follow me on Twitter https://twitter.com/Makovec
RSS Feeds https://powershell.cz/?feed=rss2
You are Home https://powershell.cz
about_PowerShell.cz  https://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 = 'https://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 = ‘https://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.

- David Moravec, MVP