Web scraping retrieves web pages and extracts structured data from their HTML when no public backend is available. In PowerShell, PowerHTML’s ConvertFrom-Html turns retrieved HTML into a navigable document that XPath queries can search. section 13.3 section 13.3.1
Treat scraping as a two-stage workflow: query a results page, then visit each detail page. This matters when one search returns several records, each with information exposed only through its own details view. section 13.3.1
Build a maintainable scraper by separating the navigation and extraction steps:
- Construct the search URL from the requested identifier and parse its response into an HTML document. section 13.3.1
- Use browser developer tools to inspect a representative link and copy its XPath, then generalize the query around stable attributes such as class and href. section 13.3.1
- Enumerate matching result links, remove link-specific suffixes from IDs, and form the detail URL for every result. section 13.3.1
- Select detail-page nodes and normalize their text into useful fields, such as architecture, supported products, and superseding updates. section 13.3.1
- Request external pages in integration tests and assert stable shapes and allowed values rather than brittle, fixed page contents. section 13.4.1
Finding superseding Microsoft updates
A KB search can return separate Windows Server 2016 and Windows 10 results. Select all matching update links, request each scoped detail page, then extract its architecture, products, and the linked KB updates that supersede it. Parse each superseding link into a custom object containing its KB number, title, and ID. section 13.3.1
Listing 13.4 Find-KbSupersedence.ps1
Function Find-KbSupersedence {
param(
$KbArticle
)
$UriHost = 'https:/ /www.catalog.update.microsoft.com/'
$SearchUri = $UriHost + 'Search.aspx?q=' +
$KbArticle
$Search = ConvertFrom-Html -URI $SearchUri
$xPath = '//*[' + #1
'@class="contentTextItemSpacerNoBreakLink" ' +
'and @href="javascript:void(0);"]'
$Search.SelectNodes($xPath) | ForEach-Object { #2
$Title = $_.InnerText.Trim() #3
$Id = $_.Id.Replace('_link', '')
$DetailsUri = $UriHost + #4
"ScopedViewInline.aspx?updateid=$($Id)"
$Headers = @{"accept-language"="en-US,en;q=0.9"}
$Request = Invoke-WebRequest -Uri $DetailsUri -Headers $Headers
$Details = ConvertFrom-Html -Content $Request
$xPath = '//*[@id="archDiv"]' #5
$Architecture = $Details.SelectSingleNode($xPath).InnerText
$Architecture = $Architecture.Replace('Architecture:', '').Trim()
$xPath = '//*[@id="productsDiv"]' #6
$Products = $Details.SelectSingleNode($xPath).InnerText
$Products = $Products.Replace('Supported products:', '')
$Products = $Products.Split(',').Trim()
$xPath = '//*[@id="supersededbyInfo"]' #7
$DivElements = $Details.SelectSingleNode($xPath).Elements("div")
if ($DivElements.HasChildNodes) {
$SupersededBy = $DivElements.Elements("a") | Foreach-Object {
$KB = [regex]::Match($_.InnerText.Trim(), 'KB[0-9]{7}')
[pscustomobject]@{
KbArticle = $KB.Value
Title = $_.InnerText.Trim()
ID = [guid]$_.Attributes.Value.Split('=')[-1]
}
}
}
[pscustomobject]@{ #8
KbArticle = $KbArticle
Title = $Title
Id = $Id
Architecture = $Architecture
Products = $Products
SupersededBy = $SupersededBy
}
}
}
This reusable function ties URL construction, XPath-based result discovery, detail requests, and field extraction into one KB-supersedence lookup. section 13.3.1
External sites change. Validate both presence and shape: IDs can match a GUID pattern, products can be non-null, architectures can be limited to expected values, and a known update can return at least a minimum number of superseding records. These checks expose markup or data changes without failing merely because new updates appear. section 13.4.1








