PowerShell web scraping doesn't work in background - powershell

I wrote a script that works fine:
# Use Internet Explorer
$ie = New-Object -ComObject 'internetExplorer.Application'
$ie.Visible= $true # Make it visible
# Set Credentials
$username="name.surname#mail.com"
$password="password"
#Navigate to URL
$ie.Navigate("https://service.post.ch/zopa/dlc/app/?service=dlc-web&inMobileApp=false&inIframe=false&lang=fr#!/main")
While ($ie.Busy -eq $true) {Start-Sleep -Seconds 3;}
# Login
$usernamefield = $ie.document.getElementByID('isiwebuserid')
$usernamefield.value = "$username"
$passwordfield = $ie.document.getElementByID('isiwebpasswd')
$passwordfield.value = "$password"
$Link = $ie.document.getElementByID('actionLogin')
$Link.click()
Start-Sleep -seconds 5
# Find file to download
$link = $ie.Document.getElementsByTagName('A') | where-object {$_.innerText -like 'post_adressdaten*'}
$link.click()
Start-Sleep -seconds 3
# Press "Alt + s" on the download dialog
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("%s")
Start-Sleep -seconds 3
# Quit Internet Explorer
$ie.Quit()
But if I change $ie.Visible= $true to $ie.Visible= $false the script doesn't work.
Why?
Because of these two lines:
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("%s")
In these two lines I'm working on the download dialog box of Internet Explorer and if the browser works in background the the script cannot click on it.
How can I send the input in background or in alternative how to keep Internet Explorer always on top?

As your own answer implies, in order to be able to send keystrokes to an application with [System.Windows.Forms.SendKeys]::SendWait(), it must have a window that is (a) visible and (b) has the (input) focus.
A simpler and faster alternative to the technique shown in your answer - where you use ad-hoc compilation of C# code that wraps WinAPI functions via P/Invoke declarations, via Add-Type - is the following:
# Create an Internet Explorer instance and make it visible.
$ie = New-Object -ComObject 'internetExplorer.Application'; $ie.Visible= $true
# Activate it (give it the focus), via its PID (process ID).
(New-Object -ComObject WScript.Shell).AppActivate(
(Get-Process iexplore | Where-Object MainWindowHandle -eq $ie.hWnd).Id
)
Taking a step back:
GUI scripting (automating a task by simulating user input to a GUI) is inherently unreliable; for instance, the user may click away from the window that is expected to have the focus.
While there is no built in solution, it sounds like Selenium offers robust programmatic browser control.
The third-party Selenium PowerShell module is a PowerShell-friendly wrapper for it (available via the PowerShell Gallery and therefore with Install-Module Selenium), but I don't know if it still works (the project is looking for maintainers as of this writing).

The closest thing I have found is this and it's ugly as hell:
# Start Internet Explorer on top
Add-Type -TypeDefinition #"
using System;
using System.Runtime.InteropServices;
public class Win32SetWindow {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
"#
$ie = new-object -comobject InternetExplorer.Application;
$ie.visible = $true;
[Win32SetWindow]::SetForegroundWindow($ie.HWND) # <-- Internet Explorer window on top
# Set Credentials
$username="name.surname#mail.com"
$password="password"
#Navigate to URL
$ie.Navigate("https://service.post.ch/zopa/dlc/app/?service=dlc-web&inMobileApp=false&inIframe=false&lang=fr#!/main")
While ($ie.Busy -eq $true) {Start-Sleep -Seconds 3;}
# Login
$usernamefield = $ie.document.getElementByID('isiwebuserid')
$usernamefield.value = "$username"
$passwordfield = $ie.document.getElementByID('isiwebpasswd')
$passwordfield.value = "$password"
$Link = $ie.document.getElementByID('actionLogin')
$Link.click()
Start-Sleep -seconds 5
# Find file to download
$link = $ie.Document.getElementsByTagName('A') | where-object {$_.innerText -like 'post_adressdaten*'}
$link.click()
Start-Sleep -seconds 3
# Press "Alt + s" on the download dialog
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("%n{TAB}{ENTER}") # or use SendWait("%s")
Start-Sleep -seconds 3
# Quit Internet Explorer
$ie.Quit()

Related

IE Automation with Powershell send ENTER Key

I am doing an IE Automation with ServiceNow where there is an option to fill the search data but there is no search button available to use the CLICK method. So I am looking for the method to enter key like {ENTER} or {~} once I filled the search data. But I am in a middle stage of PowerShell scripting and not sure how to use that.
If someone could help me with the method that would be greatly appreciate.
$IE = New-Object -ComObject InternetExplorer.application
$IE.FullScreen = $false
$IE.Visible = $true
$IE.Navigate($ServiceNowURL)
While ($IE.Busy -eq $true)
{
Start-Sleep -Milliseconds 50
}
$Enter = Read-Host 'To continue press ENTER'
#Enter
$Search = $IE.Document.IHTMLDocument3_getElementsByTagName('input') | ? {$_.id -eq 'sysparm_search'}
$EnterValue = $Search.value() = $TicketNumber
First, you need to active IE window and bring it to front using AppActivate, then set focus to the search area using focus(). After that, you can send Enter key using SendKeys.
I use https://www.google.com to search as an example and you can refer to my code sample below. I test it and it works well:
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
$ie = New-Object -ComObject 'InternetExplorer.Application'
$ie.Visible=$true
$ie.Navigate("https://www.google.com") #change it to your own url
while($ie.ReadyState -ne 4 -or $ie.Busy) {Start-Sleep -m 100}
$search=$ie.Document.getElementsByName("q")[0] #change it to your own selector
$search.value="PowerShell" #change it to your own search value
Sleep 5
$ieProc = Get-Process | ? { $_.MainWindowHandle -eq $ie.HWND }
[Microsoft.VisualBasic.Interaction]::AppActivate($ieProc.Id)
$search.focus()
[System.Windows.Forms.SendKeys]::Sendwait("{ENTER}");

powershell click on internet explorer popup

I need to use PowerShell to hit close on this pop up window which appears when I open internet explorer. Hitting enter key also closes the pop up.
What I've tried
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
$ie = new-object -com internetexplorer.application
$ie.visible = $true
$ie.navigate('http://website/')
while ($ie.busy) { Start-Sleep 3 }
[Microsoft.VisualBasic.Interaction]::AppActivate("internet explorer")
[System.Windows.Forms.SendKeys]::Sendwait("{ENTER}");
Start-Sleep 3
$link = $ie.Document.getElementsByTagName('Button') | where-object { $_.innerText -eq 'Simple Setup' }
$link.click()
Start-Sleep 2
$ie.quit()
Continuing from my comment.
Others have run into this dialog and others, and, as stated, used Selenium, AutoIT, et., to deal with that; while others have tried different means.
For Example:
# using the process handle of that dialog
$ws = New-Object -ComObject WScript.Shell
$ld = (gps iex* | where {$_.MainWindowTitle }).id
if($ld.Count -gt 1)
{
$ws.AppActivate($ld[1])
$ws.sendkeys("{ENTER}")
}
# Using the WASP module
# (note - though the code for this module is still available, the DLL is not. So, you have to compile that yourself.)
Import-Module WASP
while ($true) {
[System.Threading.Thread]::Sleep(200)
$confirmation = Select-Window iexplore
if ($confirmation -ne $null)
{
Select-ChildWindow -Window $confirmation |
Select-Control -title "OK" -recurse |
Send-Click
}
}
btw..
"Hitting enter key also closes the pop up"
... that is because modal dialogs always take focus until they are dismissed.

Need help to Convert Powershell IE auto login script to firefox having same function

I am successfully getting my scenario on below powershell script to auto login to the website xyz.com via IE but some functionality are not working properly, whereas firefox is doing same job perfectly. hence need help to convert the same script to perform same job in firefox browser using powershell.
$ie = New-Object -ComObject 'internetExplorer.Application'
$ie.Visible= $true # Make it visible
$username="username"
$password="password"
$Tenant="abcdefgh"
$ie.Navigate("htt:/xyz.com/login") ## website is taken as example
While ($ie.Busy -eq $true) {Start-Sleep -Seconds 3;}
$usernamefield = $ie.document.getElementByID('txtudsr')
$usernamefield.value = "$username"
$passwordfield = $ie.document.getElementByID('txtpswd')
$passwordfield.value = "$password"
$Tenantfield = $ie.document.getElementByID('txttfenant')
$Tenantfield.value = "$Tenant"
$Link = $ie.document.getElementByID('btn_login')
$Link.click()
While ($ie.Busy -eq $true) {Start-Sleep -Seconds 30;}
$Link = $ie.document.getElementByID('btn_Continue')
$Link.click()
While ($ie.Busy -eq $true) {Start-Sleep -Seconds 10;}
$Link = $ie.document.getElementByID('statusUnAvailable')
$Link.click()
$ie.Quit()
I don't think it's a good idea do the same on firefox as you do for IE.
Your best friend will be selenium, it is the modern way to do browser automation nowadays.
I normally use selenium on Python, but there are some articles about selenium on PowerShell too. Just google it.
https://github.com/adamdriscoll/selenium-powershell
https://gist.github.com/Jaykul/d16a390e36ec3ba54cd5e3f760cfb59e

Download an Excel document from website with Powershell

recently I began to learn PowerShell to automate my job tasks.
So I want to access a web page and click on a button that download automatically an Excel file. This is the button that I want to click on:
<div class="NormalButton">
<a class="ActiveLink" title="Excel" alt="Excel" onclick="$find('ctl32').exportReport('EXCELOPENXML');" href="javascript:void(0)" style="padding:3px 8px 3px 8px;display:block;white-space:nowrap;text-decoration:none;">Excel</a>
</div>
This would be my PowerShell script:
$ie = New-Object -com "InternetExplorer.Application"
$ie.Navigate("http://test.test/")
$ie.Visible = $true
$link = $ie.Document.getElementsByTagName('a') | Where-Object {$_.onclick -eq "$find('ctl32').exportReport('EXCELOPENXML');"}
$link.click()
If I try to run it from the console I receive the error "You cannot call a method on a null-valued expression."
If its useful I'm using PowerShell 4.0 and the webpage has a delay until the report is loaded.
I have completed it by the following code:
[void][System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
[void][System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
$url = "https://webpage.com"
$ie = New-Object -com internetexplorer.application
$ie.navigate($url)
$ie.StatusBar = $false
$ie.ToolBar = $false
$ie.visible = $true
#Get Excel
Start-Sleep -s 40
$btnExcel = $ie.Document.links | where-object { $_.outerText -eq 'Excel' -and $_.innerText -eq 'Excel' }
$btnExcel.click()
# Get Internet Explorer Focus
Start-Sleep -s 5
[Microsoft.VisualBasic.Interaction]::AppActivate("internet explorer")
[System.Windows.Forms.SendKeys]::SendWait("{F6}");
[System.Windows.Forms.SendKeys]::SendWait("{TAB}");
[System.Windows.Forms.SendKeys]::SendWait(" ");
Start-Sleep 1
[System.Windows.Forms.SendKeys]::SendWait("$file");
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}");
# Get Internet Explorer Focus
Start-Sleep -s 1
[Microsoft.VisualBasic.Interaction]::AppActivate("internet explorer")
[System.Windows.Forms.SendKeys]::SendWait("^{F4}");
Thank you all for you time :)
I Think the Your problem is because you are using Double Quotes which powershell threat it like variable and Try to expand it, so try to change it to Single quote, from:
$link = $ie.Document.getElementsByTagName('a') | Where-Object {$_.onclick -eq "$find('ctl32').exportReport('EXCELOPENXML');"}
to:
$link = $ie.Document.getElementsByTagName('a') | Where-Object {$_.onclick -eq '$find('ctl32').exportReport('EXCELOPENXML');'}
Also, you can change the -eq to -match and take just a portion of it like:
$_.onclick -match '$find('ctl32').exportReport'
For more information see: About Quoting Rules

Open IE maximized from Powershell script?

Pardon me if this is too simple a question, but I'm not finding anything in the help files or online so far regarding doing this. I'm opening up a new browser window to test the login/logout feature of a web based application, but I want to open the IE window in maximized mode. I could set the size as:
$ie.height = 1024
$ie.width - 768
But is there a keyword or anything that I can use to just open it up maximized automatically or would I need to query the screen size first and then fill in the values from that query?
/matt
(new-object -com wscript.shell).run("url",3)
Solved the Problem of starting IE maximized with following:
Function maxIE
{
param($ie)
$asm = [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
$ie.Width = $screen.width
$ie.Height =$screen.height
$ie.Top = 0
$ie.Left = 0
}
cls
$ie = new-object -com "InternetExplorer.Application"
$ie.visible = $true
maxIE $ie
while ($ie.busy) {sleep -milliseconds 50}
$ie.navigate("http://www.google.com")
I couldn't get any specific answer to work, but did get a combination to work. The order in which these are called is important else it doesn't work.
Full Example:
$ie = New-Object -Com "InternetExplorer.Application"
$urls = #("http://www.google.com","http://www.yahoo.com")
$ie.Visible = $true
CLS
write-output "Loading pages now..."
#Maximize IE window
$asm = [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
$ie.height = $screen.height
#Open webpages
foreach ($link in $urls) {
$ie.Navigate2($link, 0x1000)
}
#close first blank tab
$sa = New-Object -ComObject Shell.Application
$tab = $sa.windows() | Where {$_.Name -match 'Internet Explorer' -and $_.LocationName -eq ''}
$tab.quit()
If you have the PowerShell Community Extensions 1.2 (PSCX) installed on PowerShell 2.0, I have verified that this works:
Pscx\Start-Process IExplore.exe; Start-Sleep 3; $hwnd = Get-ForegroundWindow
$sig = #'
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
'#
Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
[Win32.NativeMethods]::ShowWindowAsync($hwnd, 3)
It is a little dicey because it is using a wait (start-sleep) of 3 secs to wait for IE to open and then it uses a PSCX cmdlet to get the window handle of the foreground window. If you only have one instance of IExplore running then you can use this to get that handle:
#(Get-Process IExplore)[0].MainWindowHandle
PowerShell 2.0 is required for the Add-Type support that allows us to call down to the Win32 API.
BTW from a quick Bing search it seems that getting IE to start maximized is a pretty common problem. For instance, with Start-Process you can specify -WindowStyle Maximized but IE doesn't honor that.
Just incase anyone else needs help I ended up just calling up iexplore maximized and then connected to it. You need to sleep because it calls too fast to connect. I'm sure there is a better way but I couldn't figure it out.
start iexplore -WindowStyle maximized
Start-Sleep -seconds 1
$ie = (New-Object -COM "Shell.Application").Windows() | ? { $_.Name -eq "Internet Explorer" }
$ie.navigate("URL")
#We will use the Win32 API function ShowWindowAsync, and spawn an IE Window Maximized.
#Parameters can be used for ShowWindowAsync
$Hide = 0
$Normal = 1
$Minimized = 2
$Maximized = 3
$ShowNoActivateRecentPosition = 4
$Show = 5
$MinimizeActivateNext = 6
$MinimizeNoActivate = 7
$ShowNoActivate = 8
$Restore = 9
$ShowDefault = 10
$ForceMinimize = 11
#Specify an interwebs address :)
$URL="http://www.google.com/"
#Create internetexplorer.application object
$IE=new-object -com internetexplorer.application
#Set some parameters for the internetexplorer.application object
$IE.TheaterMode = $False
$IE.AddressBar = $True
$IE.StatusBar = $False
$IE.MenuBar = $True
$IE.FullScreen = $False
$IE.visible = $True
#Navigate to the URL
$IE.navigate2($URL)
#the C#-style signature of an API function
$code = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
#add signature as new type to PowerShell (for this session)
$type = Add-Type -MemberDefinition $code -Name myAPI -PassThru
#Magic:
$type::ShowWindowAsync($IE.HWND[0], $Maximized)