Thursday, October 16, 2014

Web Automation with PowerShell aka the Ghost at the Machine

PowerShell is good at running and manipulating Windows applications.  For example, I once had a client that needed a process to log on to a web site, fill in a number of text boxes and click a button to the download a file.  I was able to accomplish this quickly with PowerShell.


In this blog, I want to demonstrate how  to automate applications such as Internet Explorer using PowerShell. I have a script below that uses a web site that provides a map with zip codes for a specified location.  The PowerShell script will navigate to the site, enter a location into the address text box and click the button to look display the map. 


Before we begin I need to point out that the script uses a free module called WASP which provides a number of convenient automation CommandLets such as sending key strokes to an application.  Typically a module adds extra support for a specific type of functionality.  Unless you have already downloaded WASP, you will need to do so.  You can download it from http://wasp.codeplex.com/.  After you download it, extract the file WASP.DLL and copy it to a folder where PowerShell looks for modules. 


To determine where PowerShell searches for modules, enter the following command.

$env:PSModulePath

In the output, you should see a list of folder paths separated by semi colons.  This environment variable holds the places PowerShell will look for modules.  Pick one of the folders, ideally one under your documents folder, and create a new folder named WASP in it.  Then paste the unzipped WASP.dll in that folder.  A module must have its own folder. 

Now we are ready.  Let's review the Web Automation script below.
Import-Module WASP
stop-process -processname iexplore*   #  End any other Explorer sessions.
start-sleep 2  # Wait above to finish.
$ie = New-Object -comobject InternetExplorer.Application
$ie.visible = $true
$ie.silent = $true
$ie.Navigate( $url )
while( $ie.busy){Start-Sleep 1}
Select-Window "iexplore" | Set-WindowActive
$txtArea=$ie.Document.getElementsByName("address")
Foreach($element in $txtArea)
    {
        $element.value = "Boston MA"
    }
$btnFind=$ie.Document.getElementsByName("BUTTON")
Foreach($btn in $btnFind)
    {
        $btn.Click()
    }

Let's review this code in detail.  First we need to import the WASP module which do with the line below.
Import-Module WASP
The next line simply stores the URL we are going to use into a variable as shown below.

Then we need to stop any other Internet Explorer processes so our program won't get confused when it tries to being up an IE window.
stop-process -processname iexplore*   #  End any other Explorer sessions.
Now let's review several lines of  related code below.
$ie = New-Object -comobject InternetExplorer.Application
$ie.visible = $true
$ie.silent = $true
$ie.Navigate( $url )
The first line above creates an instance of Internet Explorer and places a reference to it into the variable $ie.  The line "$ie.visible = $true"  makes the IE application visible.  The line "$ie.silent = $true "  says to suppress messages.  Finally the line "$ie.Navigate( $url )" tells IE to go to the URL specified in the $url variable.
IE needs time to finish the above tasks which we give it with the line below.
while( $ie.busy){Start-Sleep 1}
Since IE is in the background, we need to bring it activate and select it using WASP CommandLets as shown below.
Select-Window "iexplore" | Set-WindowActive
Ok. The screen is up and waiting for input.  To fill in the address field, we need to get a reference to it first which we do as follows.
$txtArea=$ie.Document.getElementsByName("address")

Now we can use the reference to fill in the text box.  Here's where each web automation script will be different.  To navigate around the web page, we use DOM but to do that you need to know what the web page source code is.  In IE, you can right mouse click on the page and select View Source to see it.  Then you need to review it to see what the elements are and how they are identified.  If the element has a unique id, you can use the GetElementByID method to get a reference to it, i.e. $myelement = $ie.GetElementByID.  In this case, all we have is a name.  Unfortunately, GetElementsByName returns an array of elements even if there is only one which is true here.  So we need to loop through the array to enter the text as shown below.
Foreach($element in $txtArea)
    {
        $element.value = "Boston MA"
    }
In the code above, the Foreach iterates over the $txtArea array storing each item into $element.  We use this in the loop to set the value, i.e.   $element.value = "Boston MA".  Ok.  It's a bit awkward but Web Automation can be like that since you don't usually have any control over the HTML of the page.
Ok.  The location is entered, now we need to click the Find button.  As before, there is no unique ID so we'll use GetElementsByName and loop through the collection. 
$btnFind=$ie.Document.getElementsByName("BUTTON")
Foreach($btn in $btnFind)
    {
        $btn.Click()
    }
In the above code, we are using the button click method to execute the find.

If it worked, you see the page with a map showing Boston, Massachusetts and the zip codes for the area. 
Internet Explorer is only one of many applications that can be automated.  Office applications, Win form applications, and many more applications can be automated in a similar manner.  This feature can help automate a routine manual task to save you time or be integrated into a production job.  It's another reason why PowerShell is such a valuable tool.
Thanks,
Bryan

No comments:

Post a Comment