How to get a random player's head?

You need a loot table (archive) and the command /loot (archive) for this.

Firstly, the loot table that gives (or summons or inserts or whatever) the head of the player it is invoked on.
Minimal file structure of the datapack:

<datapack_name>
├pack.mcmeta
└data
 └head_loot
  └loot_tables
   └head.json

Here head_loot is an arbitrarily chosen namespace, which is reused in the command later. The same with head.json, only the file extension needs to be .json.

Contents of pack.mcmeta can vary depending on what you want, but this is a minimum example:

{"pack":{"pack_format":3,"description":""}}

Contents of head.json:

{
 "pools": [
  {
   "rolls": 1,
   "entries": [
    {
     "type":"minecraft:item",
     "name":"minecraft:player_head",
     "functions":
      [
       {
        "function":"fill_player_head",
        "entity":"this"
       }
      ]
    }
   ]
  }
 ]
}

Now you can create heads of arbitrary (currently online) players. There are multiple ways to do this, the one that is actually intended for exactly this is:

/execute as @r run loot spawn ~ ~ ~ loot head_loot:head

This runs "as" a random player, but not "at" their position and summons a player head item entity at the execution position (a command block or whatever).

Make sure that the datapack is enabled, this command tells you that:

/datapack list

If you just changed something in the files, use /reload.