Here's a basic way to do it, based on this answer:
(Get-ChildItem -Path c:\temp\ -Recurse -Filter *.html -File) |
foreach { ren $_.fullname ([uri]::EscapeDataString($_))}
Originally I had written it without the () around the Get-ChildItem, but found it was still reading directory information while the rename had already renamed the first item. Then the rename reencoded the first item a second time, thus making it a bit munged up.
Here's a cleaner version that handles the files first and then folders:
$files = Get-ChildItem -Path c:\temp\ -Recurse -Filter *.html -File
$folders = Get-ChildItem -Path c:\temp\ -Recurse -Filter *.html -Directory
$files | foreach { ren $_.fullname ([uri]::EscapeDataString($_))}
$folders | foreach { ren $_.fullname ([uri]::EscapeDataString($_))}