2

I can download the Google Chrome installer easily as follows:

Invoke-WebRequest "http://dl.google.com/chrome/install/latest/chrome_installer.exe" -OutFile "$env:Temp\chrome_installer.exe"

However, for Opera, I want specifically the latest 64-bit version. On the download page at https://www.opera.com/download there is a handy link to that:

https://download.opera.com/download/get/?partner=www&opsys=Windows&arch=x64

enter image description here

When I click on the "64 bit" link, it automatically starts the download of the latest executable, but using Invoke-WebRequest on that url does not download the file:

Invoke-WebRequest "https://download.opera.com/download/get/?partner=www&opsys=Windows&arch=x64" -OutFile "$env:Temp\opera_installer.exe"

How can I manipulate a url like this to:

  1. Download the file as if I clicked on the link on the download page?
  2. Get the name of the file that is downloaded (as I see that the full file version is in the file downloaded)?
  3. Redirect that download to a destination of my choosing?
YorSubs
  • 3,194
  • 7
  • 37
  • 60

2 Answers2

3

To access the installers you could use the following URI:

https://get.opera.com/pub/opera/desktop/

Depending on the version you want you can do:

Invoke-WebRequest "https://get.opera.com/pub/opera/desktop/92.0.4561.43/win/Opera_92.0.4561.43_Setup_x64.exe" -OutFile "$env:Temp\opera_installer.exe"

Working with link mentioned above you could do something like this:

#Set source URI
$uri = "https://get.opera.com/pub/opera/desktop/"

#As the links are sorted by version the last link is the newest version
(Invoke-WebRequest -uri $uri).links[-1] | %{
    #Parse string to link and as we want the Windows version add 'win/', filter for 'Setup_x64\.exe$'
    $uri = [Uri]::new([Uri]$uri, $_.href).AbsoluteUri + 'win/'
    (Invoke-WebRequest $uri).links | ?{$_.href -match 'Setup_x64\.exe$'} | %{
        #Build new Uri, download file and write it to disk
        $uri = [Uri]::new([Uri]$uri, $_.href)
        Invoke-WebRequest -Uri $uri -OutFile "C:\tmp\$($uri.Segments[-1])"
    }
}
Toni
  • 1,738
  • 1
  • 3
  • 11
  • Thanks, but won't the specific name of the file change with new versions? How will I grab the latest version regardless of the name? – YorSubs Nov 06 '22 at 11:41
  • can't follow you, this ```Invoke-WebRequest "https://get.opera.com/pub/opera/desktop/92.0.4561.43/win/Opera_92.0.4561.43_Setup_x64.exe" -OutFile "$env:Temp\opera_installer.exe"``` can't end in a download loop – Toni Nov 06 '22 at 14:25
  • Sorry, it's fine, turns out it was just really slow for me when I was downloading it. – YorSubs Nov 06 '22 at 18:07
  • 1
    @YorSubs, in _Windows PowerShell_, it pays to set `$ProgressPreference = 'SilentlyContinue'` before downloading, because the progress display significantly slows down the operation (no longer a concern in _PowerShell (Core) 7+_). – mklement0 Nov 06 '22 at 18:56
  • I think I have come across this before, but like so many things, I've forgotten that and haven't used that option in years, and it's a very significant speedup; I'm seeing something like 4 or 5 times faster with the `ProgressPreference` set to silent. – YorSubs Nov 07 '22 at 10:46
  • @YorSubs, yes, the slowdown you experience _by default_ is very unfortunate (as noted, fortunately no longer a concern in PowerShell (Core)). As for this solution: While using an HTML parser is unquestionably better than using regex-based _text_ parsing, the problem is that it relies on the _Internet Explorer_ engine, which is no longer available by default in recent Windows versions. There is no replacement as part of PowerShell itself, but third-party libraries can help: see [this answer](https://stackoverflow.com/a/60658511/45375). – mklement0 Nov 10 '22 at 23:06
1

It looks like the download URL you found isn't just a chain of straight redirects but involves a JavaScript file that dynamically builds the ultimate target URL, so Invoke-WebRequest cannot be used with it.

However, building on Toni's helpful answer, you can do some - simple - web scraping to determine the latest version number and to derive the download URL from it:

$VerbosePreference = 'Continue'

$downloadRootUrl = 'https://get.opera.com/pub/opera/desktop/'
$downloadTargetFile = 'Opera_Setup.exe' 

# Get the version listing and match the *last* <a> element's href attribute,
# assumed to contain the latest version number.
Write-Verbose "Determining latest version via $downloadRootUrl..."
if ((Invoke-RestMethod $downloadRootUrl) -notmatch '(?s)^.+<a href="([^/"]+).+$') {
  throw "Could not determine latest version."
}

# Extract the version number, via the automatic $Matches variable.
$latestVersion = $Matches[1]

# Construct the full download URI based on the version number.
$downloadUrl = $downloadRootUrl + ('{0}/win/Opera_{0}_Setup_x64.exe' -f $latestVersion)

Write-Verbose "Downloading installer from $downloadUrl..."
& {
  # Temporarily silence the progress stream, because in Windows PowerShell 
  # its display slows things down significantly.
  $ProgressPreference = 'SilentlyContinue'
  try {
    Invoke-RestMethod -ErrorAction Stop -OutFile $downloadTargetFile $downloadUrl
  } catch {
    throw $_
  }
}

Write-Verbose "Installer for version $latestVersion successfully downloaded to $downloadTargetFile."
mklement0
  • 382,024
  • 64
  • 607
  • 775