A third-party cookie tracker
Why it matters This is the classic way an ad network follows one person across many shops. Once you can see how a single cookie joins separate visits into one profile, cookie banners and browser settings start to make sense.
Think of a festival wristband. Each stall you visit can scan it. No stall knows much on its own, but the company that issued the wristband sees every stall you went to.
AdCo's cookie is that wristband. When Sam first views a shop page, AdCo's report comes back with a Set-Cookie header (the response line that asks the browser to store a cookie, which you parsed in lesson 35) that stores an adco_id cookie in Sam's browser. From then on, every report to AdCo, from any of the three shops, carries the same adco_id.
Third-party cookie: a cookie belonging to a site other than the one in the address bar. AdCo's cookie is third-party on every shop, because Sam is on the shop, not on AdCo.
In the log, that shows up as the same cookie_id on records from different shops. Grouping records by that ID rebuilds AdCo's view of Sam.
Grouping with a dictionary of sets
You will group records into a dictionary. Each key is a cookie ID; each value is a set of sites seen with that ID. A set is like a list that ignores duplicates, so visiting ShopA five times still counts ShopA once.
seen = {}
seen.setdefault("abc", set()).add("shopa.example")
seen.setdefault("abc", set()).add("shopa.example") # already there, no change
seen.setdefault("abc", set()).add("shopb.example")
print(seen) # {'abc': {'shopa.example', 'shopb.example'}}setdefault(key, set()) means "give me the set for this key, creating an empty one first if there is none".
One detail: the very first report has cookie_id set to None, because Sam's browser did not have the cookie yet. Skip those records.
The defence, in one line
This only works because one cookie jar is shared by every site Sam visits. In the next lesson you switch on two browser defences, partitioned storage (a jar per shop) and blocking third-party cookies, and measure how the profile falls apart.
In other languages
JavaScript, grouping with a Map of Sets:
const seen = new Map();
for (const r of records) {
if (r.cookie_id === null) continue;
if (!seen.has(r.cookie_id)) seen.set(r.cookie_id, new Set());
seen.get(r.cookie_id).add(r.site);
}Go, with a map of maps used as sets:
seen := map[string]map[string]bool{}
for _, r := range records {
if r.CookieID == "" { continue }
if seen[r.CookieID] == nil { seen[r.CookieID] = map[string]bool{} }
seen[r.CookieID][r.Site] = true
}Recap
- A third-party cookie works like a wristband every stall can scan.
- Grouping log records by
cookie_idrebuilds the tracker's profile. - It only works when every site shares one cookie jar.
Write join_by_cookie(records) that returns a dict: each cookie_id (skip None) maps to the set of sites seen with it. Then run AdCo's log for the world with seed=7 and Sam's first 12 pages, and store the result of your function in profiles.
This lesson is locked
Lessons open one at a time. Finish the previous lesson to unlock this one.