Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Friday Fun: Testing Google Chrome Bookmarks with PowerShell

Posted on November 23, 2012November 1, 2013

I was cleaning up and organizing bookmarks in Google Chrome today and decided to find out where they were stored on my computer. I found the Bookmarks file in a local app data folder. Opening it up in Notepad I was pleasantly surprised to discover it is in JSON. Excellent! This gives me an opportunity to try out some of the new web cmdlets in PowerShell v3 and build a little tool to help find broken links.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

The first step is to convert the file from JSON into PowerShell objects.

$File = "$env:localappdata\Google\Chrome\User Data\Default\Bookmarks"
$data = Get-content $file | out-string | ConvertFrom-Json

To convert from JSON, the input needs to be one long string. Get-Content writes an array of strings so by piping to Out-String first, ConvertFrom-JSON is happy. Here's what I end up with.

$data

checksum                                roots                                                      version
--------                                -----                                                      -------
03a5a00f42bb4860f6f8dd4d543e34af        @{bookmark_bar=; other=; synced=}                                1

The roots property is where things are stored.

$data.roots | format-list

bookmark_bar : @{children=System.Object[]; date_added=12989558548133917; date_modified=12998163702118866; id=1;
               name=Bookmarks bar; type=folder}
other        : @{children=System.Object[]; date_added=12989558548133917; date_modified=12998151911083334; id=2;
               name=Other bookmarks; type=folder}
synced       : @{children=System.Object[]; date_added=12989558548133917; date_modified=0; id=3; name=Mobile bookmarks;
               type=folder}

As far as I know these are hard-coded properties. Each property can have a nested property called children which will be a collection of bookmarks and subfolders.

$data.roots.bookmark_bar


children      : {@{date_added=12993566428951635; id=67; name=PowerShell.org • View unanswered posts; type=url;
                url=http://powershell.org/discuss/search.php?search_id=unanswered}, @{date_added=12989558569500214;
                id=5; name=HostGator.com Control Panel; type=url;
                url=http://gator1172.hostgator.com:2082/frontend/x3/index.php?post_login=91229198020615},
                @{date_added=12989558569506214; id=7; name=Vet Followers; type=url;
                url=https://www.socialoomph.com/vetfollowers}, @{date_added=12989558569508214; id=8; name=Google+;
                type=url;
                url=https://plus.google.com/up/start/?continue=https://plus.google.com/&type=st&gpcaz=3cba226a}...}
date_added    : 12989558548133917
date_modified : 12998163702118866
id            : 1
name          : Bookmarks bar
type          : folder

Here's what an child item looks like:

date_added : 12998151910630334
id : 76
name : Facebook
type : url
url : http://www.facebook.com/

Awesome. All I need to do is get the url.

$data.roots.bookmark_bar.children | select Name,url

name                                                        url
----                                                        ---
PowerShell.org • View unanswered posts                      http://powershell.org/discuss/search.php?search_id=unans...
HostGator.com Control Panel                                 http://gator1172.hostgator.com:2082/frontend/x3/index.ph...
Vet Followers                                               https://www.socialoomph.com/vetfollowers
Google+                                                     https://plus.google.com/up/start/?continue=https://plus....
Bottlenose                                                  http://bottlenose.com/home#streams/everything
Power Tweet                                                 javascript:(function(){url="http://www.twylah.com/bookma...
Facebook                                                    http://www.facebook.com/
Blog Dashboard                                              https://jdhitsolutions.com/blog/wp-admin/index.php

To validate if the URL is good, I can use Invoke-Webrequest.

invoke-webrequest -Uri http://www.facebook.com  -UseBasicParsing | Select StatusCode
  StatusCode
  ----------
         200

All I want is the status code so I'm using basic parsing to speed things up. Now that I have the basics, I can turn this into a script.

#requires -version 3.0

#comment based help is here

[cmdletbinding()]

Param (
[Parameter(Position=0)]
[ValidateScript({Test-Path $_})]
[string]$File = "$env:localappdata\Google\Chrome\User Data\Default\Bookmarks",
[switch]$Validate
)

Write-Verbose -Message "Starting $($MyInvocation.Mycommand)"

#A nested function to enumerate bookmark folders
Function Get-BookmarkFolder {
[cmdletbinding()]
Param(
[Parameter(Position=0,ValueFromPipeline=$True)]
$Node
)

Process {
    
 foreach ($child in $node.children) {
   #get parent folder name
   $parent = $node.Name
   if ($child.type -eq 'Folder') {
     Write-Verbose "Processing $($child.Name)"
     Get-BookmarkFolder $child
   }
   else {
        $hash= [ordered]@{
          Folder = $parent
          Name = $child.name
          URL = $child.url
          Added = [datetime]::FromFileTime(([double]$child.Date_Added)*10)
          Valid = $Null
          Status = $Null
        }
        If ($Validate) {
          Write-Verbose "Validating $($child.url)"
          if ($child.url -match "^http") {
            #only test if url starts with http or https
            Try {
              $r = Invoke-WebRequest -Uri $child.url -DisableKeepAlive -UseBasicParsing
              if ($r.statuscode -eq 200) {
                $hash.Valid = $True
              } #if statuscode
              else {
                $hash.valid = $False
              }
              $hash.status = $r.statuscode
              Remove-Variable -Name r -Force
            }
            Catch {
              Write-Warning "Could not validate $($child.url)"
              $hash.valid = $False
              $hash.status = $Null
            }
              
            } #if url
              
    } #if validate
        #write custom object
        New-Object -TypeName PSobject -Property $hash
  } #else url
 } #foreach
 } #process
} #end function

#convert Google Chrome Bookmark filefrom JSON
$data = Get-Content $file | Out-String | ConvertFrom-Json

#these should be the top level "folders"
$data.roots.bookmark_bar | Get-BookmarkFolder 
$data.roots.other | Get-BookmarkFolder 
$data.roots.synced | Get-BookmarkFolder

Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"

This script converts the bookmarks file from JSON and creates custom objects for each bookmark using the nested Get-BookMarkFolder function. This processes each child if it is a url. If it is a folder then it passes the folder name recursively to Get-BookmarkFolder. Because validation might be time consuming, I made it optional with -Validate. I also converted the date_added property into a user-friendly datetime format. The value in the file is a file time, i.e. number of ticks since 1/1/1601. Although the actual value needs to be multiplied by 10 to get the correct date time.

When I run the script, without validation I get an object like this for each bookmark.

Folder : Misc
Name : [Release][Alpha0.6] CyanogenMod 9 Touchpad - RootzWiki
URL : http://rootzwiki.com/topic/15509-releasealpha06-cyanogenmod-9-touchpad/
Added : 8/15/2012 10:42:49 PM
Valid :
Status :

If I validate it will look like this:

Folder : Misc
Name : [Release][Alpha0.6] CyanogenMod 9 Touchpad - RootzWiki
URL : http://rootzwiki.com/topic/15509-releasealpha06-cyanogenmod-9-touchpad/
Added : 8/15/2012 10:42:49 PM
Valid : True
status : 200

Bookmarks that fail will show as False. I haven't gotten around to figuring out how to rewrite the bookmarks file, but I would probably end up using ConvertTo-JSON.

The web cmdlets in PowerShell v3 can be a lot of fun to work with. But one word of caution: do NOT try to turn the script in the ISE using -validate. It is possible that Invoke-WebRequest will begin consuming all memory on your computer, unless you make sure the ISE process is completely killed.

I hope you'll download Get-ChromeBookmark and let me know what you think.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

5 thoughts on “Friday Fun: Testing Google Chrome Bookmarks with PowerShell”

  1. Nitin says:
    November 24, 2012 at 2:33 am

    Neat Stuff

    Simply Awesome

  2. Pingback: Microsoft Most Valuable Professional (MVP) – Best Posts of the Week around Windows Server, Exchange, SystemCenter and more – #4 - TechCenter - Blog - TechCenter – Dell Community
  3. Pingback: Microsoft Most Valuable Professional (MVP) – Best Posts of the Week around Windows Server, Exchange, SystemCenter and more – #4 - Dell TechCenter - TechCenter - Dell Community
  4. dbuendiabiego Buendía says:
    December 26, 2012 at 5:57 am

    The date_added is actually in nanoseconds. A tick is 10 nanoseconds, thats why you had to multiply by ten.

    1. Jeffery Hicks says:
      December 26, 2012 at 7:29 am

      Thanks for the clarification.

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d