programming:interaction
Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| programming:interaction [2025/01/15 09:09] – created - brainstorming admin | programming:interaction [2026/08/27 12:39] (current) – Reviewer 4 (generic): add 'Interacting is not free' — click fraud, form side effects, load multiplied by 3x, and the visibility trade-off — and a Monday-morning default design; replace the unaudited 'none of them justified anywhere' with the measured just karel.kubicek.claude | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Interaction with website | + | ====== Interaction with Websites |
| - | Brainstorming: | + | '' |
| - | * Input into website forms | + | The literature is unusually explicit about the first of those and almost silent about the rest. This page is about all of them: how deep into a site to go, which pages to pick when you go deeper, what to do on a page once you are there, and how to report the choice so a reviewer can tell what your number is a count of. |
| - | * Interacting with consent: link Privacy: | + | |
| - | * How many subpages (refer Tobias Urban) | + | |
| - | * | + | |
| - | /* | + | Neighbouring pages own the pieces that are decisions in their own right: what to do with the cookie banner |
| - | This is a comment not visible on the page. It outlines | + | |
| - | ===== Header Level 2 ===== | + | <WRAP important> |
| - | ==== Header Level 3 ==== | + | Three things this page exists to stop you getting wrong. |
| - | === Links === | + | - **"We crawled 10,000 websites" |
| + | - **Ten subpages is the folk default and it is not where the curve flattens.** The median stated '' | ||
| + | - **Navigating is not interacting.** A great deal of what a privacy measurement counts — lazily-loaded ad slots, content behind a tab or an accordion, anything a form submission triggers — does not exist at '' | ||
| + | </ | ||
| - | External links are recognized automatically: | + | ===== What to Read First ===== |
| - | Internal | + | * **On Landing and Internal |
| + | * **Beyond the Front Page: Measuring Third Party Dynamics in the Field** {[urban2020beyond]}, | ||
| + | * **Apophanies or Epiphanies? How Crawlers Impact Our Understanding of the Web** {[ahmad2020_apophanies]}, TheWebConf 2020 — what changes when you change the instrument. Read it before you argue that your crawler choice does not matter. | ||
| + | * **Towards Realistic and Reproducible Web Crawl Measurements** {[jueckstock2021_realistic]}, TheWebConf 2021 — realism as an axis you can rank designs on, rather than a limitation-section apology. | ||
| + | * **The Representativeness of Automated Web Crawls as a Surrogate for Human Browsing** {[zeber2020representativeness]}, | ||
| + | * **Reproducibility and Replicability of Web Measurement Studies** {[demir2022_reproducibility]}, TheWebConf 2022 — the checklist form of everything on this page, and the one to cite when a reviewer asks why you reported a configuration detail. | ||
| - | === Lists === | + | ===== Four Axes, Not One ===== |
| - | Lists and their levels are decided by indentation (2 spaces = 1 level) | + | " |
| - | - this is 1. item | + | ^ Axis ^ The question ^ Where it is decided ^ Where this corpus records it ^ |
| - | - 2. item | + | | **Depth** | how far below the site root does a visit go | your crawl loop | '' |
| - | - nested a. item | + | | **Selection** | //which// pages below the root | your link-picking rule | nowhere — it lives only in prose | |
| - | * bullet-point item | + | | **On-page action** | scroll, click, hover, type, wait | your per-page script | nowhere — full-text sweep only | |
| + | | **Identity** | consent choice, login, registration | before or during the visit | '' | ||
| - | === Code === | + | The two middle rows are the ones with no schema field anywhere, in this corpus or in the reporting checklists, and they are exactly the two that decide whether your crawl saw the ad. |
| - | For a short inline monospace, use '' | + | ==== Depth has more than three positions ==== |
| - | <code python> | + | The extraction' |
| - | string | + | |
| - | print(f'Hello {string}') | + | ^ Value ^ What it means ^ Web crawls in the corpus ^ |
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | |||
| + | '' | ||
| + | |||
| + | There is also a terminology trap that a keyword search will not survive: in the phishing, malvertising and spam literature **" | ||
| + | |||
| + | ===== How Deep the Field Actually Goes ===== | ||
| + | |||
| + | Depth is the **best-reported** crawl-configuration field in this corpus, though only just: of the 1,120 crawling papers, 75.1% state a depth against 69.6% for authentication — and then the field falls off a cliff. | ||
| + | |||
| + | ^ '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | |||
| + | That is worth knowing before you decide depth is not worth a sentence: it is the one crawl-configuration choice the field already has a habit of writing down, so omitting it stands out. | ||
| + | |||
| + | ^ Bucket ^ Web crawls ^ States a depth ^ On the site-depth axis ^ Of those, went beyond the landing page ^ Gives a subpage count ^ | ||
| + | | 2010–2013 | 80 | 67 (83.8%) | 35 | 27 (77.1%) | 8 (10.0%) | | ||
| + | | 2014–2017 | 130 | 109 (83.8%) | 57 | 35 (61.4%) | 11 (8.5%) | | ||
| + | | 2018–2021 | 241 | 198 (82.2%) | 121 | 72 (59.5%) | 37 (15.4%) | | ||
| + | | 2022–2024 | 253 | 207 (81.8%) | 125 | 82 (65.6%) | 33 (13.0%) | | ||
| + | | 2025–2026((Provisional: | ||
| + | |||
| + | Two readings, and they point in different directions. | ||
| + | |||
| + | The good news: **about four in five web crawls say something about depth**, and that has been roughly true since 2010 — though the direction is very slightly down, from 83.8% in the first bucket to 79.1% in the last, which [[Programming: | ||
| + | |||
| + | The bad news: the second and third numbers barely move. Sixteen years after the field started worrying about this, **the share that goes beyond the landing page is not rising**, and **only 104 of 857 web crawls (12.1%) put a number on how many subpages** — 102 of the 417 on the depth axis, 24.5%. "We also crawled subpages" | ||
| + | |||
| + | <WRAP important> | ||
| + | **The 62.8% is an upper bound, not a level.** Of the 417 papers on the site-depth axis, 262 (62.8%) went beyond the landing page. But a paper that crawled deeper has a reason to describe its traversal, and a paper that did not may simply have written "we visited the top 10k websites" | ||
| + | </ | ||
| + | |||
| + | ==== Which numbers people pick ==== | ||
| + | |||
| + | Of the 104 web crawls that state a subpage count, the distribution is bimodal, and the two modes are two different research designs. | ||
| + | |||
| + | ^ Subpages per site ^ Papers ^ Share of 104 ^ | ||
| + | | 0 (landing page only) | 4 | 3.8% | | ||
| + | | 1–4 | 29 | 27.9% | | ||
| + | | 5–9 | 15 | 14.4% | | ||
| + | | 10–19 | 21 | 20.2% | | ||
| + | | 20–49 | 16 | 15.4% | | ||
| + | | 50–99 | 2 | 1.9% | | ||
| + | | 100 or more | 17 | 16.3% | | ||
| + | |||
| + | Median 10, and it does not settle: per four-year bucket the median runs 5, 5, 10, 15, 5 on 8, 11, 37, 33 and 15 papers, which is small-sample noise rather than a trend. The five commonest values overall are 5 (14 papers), 3 (12), 10 (12), 1 (11) and 20 (8) — all small and all round. Whether they are // | ||
| + | |||
| + | The honest summary is that **the field converged on "a handful of subpages" | ||
| + | |||
| + | ===== Deeper is Not Simply More ===== | ||
| + | |||
| + | This is the part that is easy to get backwards. Crawling deeper does not uniformly increase every privacy metric, and two of the field' | ||
| + | |||
| + | ==== Urban et al.: subpages carry more ==== | ||
| + | |||
| + | Urban et al. crawled the top 10,000 websites | ||
| + | |||
| + | * **cookies: +36%.** " | ||
| + | * **trackers: about +6%**, matched against EasyPrivacy — and **2.5% of sites** showed tracking **only** on subsites, so a landing-only crawl records them as untracked. | ||
| + | * **device fingerprinting: | ||
| + | |||
| + | Their conclusion is a methodological instruction, | ||
| + | |||
| + | ==== Aqeel et al.: landing pages are heavier, internal pages are wider ==== | ||
| + | |||
| + | Aqeel et al. compared the landing page against internal pages for 1,000 sites and reached results that read as the opposite until you notice the metric {[aqeel2020_landing]}. Per page, the landing page is the bigger, busier one: | ||
| + | |||
| + | * landing pages are on average **34% larger** than internal pages (geometric mean of per-site ratios); | ||
| + | * at the 80th percentile, internal pages carry **20 tracking requests** and landing pages **28** — measured by counting requests the Brave adblock library plus EasyList would have blocked; | ||
| + | * in about **10%** of sites, internal pages have **no** trackers while the landing page does. | ||
| + | |||
| + | But the **union across internal pages is wider than any single page**: | ||
| + | |||
| + | * in the median, internal pages collectively fetch content from **18 third-party domains that never appear on the landing page**; for 10% of sites, **80 or more**; | ||
| + | * of 200 sites checked for header bidding, **17** had it on the landing page and a further **12 only on internal pages**; | ||
| + | * 36 of the 1,000 sites served their landing page over plain HTTP, but among the sites whose landing page was secure, **170 had at least one HTTP internal page**. | ||
| + | |||
| + | So the two results are compatible, and together they are more useful than either alone: | ||
| + | |||
| + | <WRAP important> | ||
| + | **Per-page density and union coverage move in opposite directions.** The landing page is the most heavily instrumented //single// page on a typical site. The set of internal pages reaches //more distinct// third parties. Which of those your number is a count of decides whether crawling deeper raises it or lowers it — and it is not something any of the reporting checklists asks for, so it is on you to say it. A per-page mean is not a prevalence, and a prevalence over sites is not a per-page mean. | ||
| + | </ | ||
| + | |||
| + | Aqeel et al. also put a number on the scale of the problem in the surrounding literature: of **119** publications they reviewed at IMC, PAM, NSDI, SIGCOMM and CoNEXT between 2015 and 2019, **41 (34.5%) need no revision, 48 (40.3%) a minor revision and 30 (25.2%) a major one** to apply to internal pages. | ||
| + | |||
| + | ==== And the effect is not only about depth ==== | ||
| + | |||
| + | Khaleesi ran the same Alexa top-10K under the same cookie policy in two configurations — a plain homepage crawl, and an " | ||
| + | |||
| + | ^ Configuration ^ Requests ^ Request chains ^ | ||
| + | | cookies allowed, homepage | 192,038 | 76,816 | | ||
| + | | cookies allowed, interactive | 575,550 | 229,151 | | ||
| + | |||
| + | That is **3.0× the requests and 3.0× the chains from the same site list**. And the difference is not only volume: 302 redirects make up **42.91%** of advertising-and-tracking redirect requests in the homepage crawl against **28.56%** interactive, | ||
| + | |||
| + | Two smaller replications in the same direction: | ||
| + | |||
| + | * Musch and Johns swept for JavaScript anti-debugging over front pages plus **three random internal subpages** {[musch2021_debug]}: | ||
| + | * Lauinger et al. state the other side plainly, and give the reason a landing-only design is often the right one: "We only crawl the homepage of each visited site due to the presence of many sites that thwart deeper traversal by requiring log-ins." | ||
| + | |||
| + | ===== Where the Increase Stops ===== | ||
| + | |||
| + | The most useful number in this literature is buried in a pre-study. Urban et al. selected 100 random sites from the top 1,000 and crawled **25, 50, 75, 100, 250, 500 and 1,000 subsites** of each, then looked at where the cookie count stopped growing {[urban2020beyond]}: | ||
| + | |||
| + | > the mean amount of accessed/ | ||
| + | |||
| + | That is the sentence to cite when a reviewer asks why you chose your number, and it is **an order of magnitude above the field's median of ten**. Their main crawl accordingly uses 100 subsites per site, not a handful. | ||
| + | |||
| + | Note two boundaries before you copy the 100 across: | ||
| + | |||
| + | * It is a **cookie-count** saturation curve on the **top 1,000** sites in 2019–2020. A different metric (distinct third-party domains, ad slots, fingerprinting scripts) and a different rank band will have a different curve — and unpopular sites have fewer subpages to give. | ||
| + | * The curve is about **how many**, not **which**. Saturating the count says nothing about whether you reached the pages that matter for your question. | ||
| + | |||
| + | A note this page has to make, because the previous version of it carried the opposite claim: the earlier text here said the increase becomes small after about **ten** subpages. That number is not in the paper. The pre-study' | ||
| + | |||
| + | ===== Choosing Which Subpages: a Sampling Decision in Disguise ===== | ||
| + | |||
| + | There is no field for this in any schema and no convention in the literature, and it changes results more than the count does. Five rules are in use: | ||
| + | |||
| + | ^ Rule ^ What it gives you ^ What it biases towards ^ Seen in ^ | ||
| + | | **First //N// same-origin links in DOM order** | cheap, deterministic, | ||
| + | | **Random //N// first-party links** | avoids the nav bias; needs a seed to be reproducible | pages reachable in one hop; misses everything behind a search box or a paginated feed | {[urban2020beyond]}, {[iqbal2022_khaleesi]}, | ||
| + | | **Random //N// with an exclusion list** | drops the pages you know are unrepresentative | your exclusion list becomes part of the method and must be published | {[reitinger2024_creepy]}: | ||
| + | | **Search-engine " | ||
| + | | **Clickstream traversal** — click a real element, then continue from wherever you land | closest to a session; the same path can be replayed across arms | whatever is clickable and static; the authors of the clearest example say so themselves | {[lin2024_browsing]} | | ||
| + | |||
| + | That last one is worth quoting, because it is the rare case of a paper naming its own selection bias precisely {[lin2024_browsing]}: | ||
| + | |||
| + | > our dataset is biased towards static inner pages which may cause us to underestimate the impact cookies have on website appearance, as we are less likely to explore the more dynamic areas of a website | ||
| + | |||
| + | Two further points a reviewer will raise: | ||
| + | |||
| + | * **Same-origin is not the same as first-party.** A link to '' | ||
| + | * **The frontier is state-dependent.** Which links exist on the landing page depends on your consent choice, your geolocation, | ||
| + | |||
| + | ==== The one page list that included internal pages is gone ==== | ||
| + | |||
| + | Aqeel et al. shipped **Hispar**: a top list of complete URLs rather than domains — around 2,000 sites, one landing page and up to 49 internal pages each, 100,000 URLs, refreshed weekly, with the internal pages discovered through Google '' | ||
| + | |||
| + | **Do not plan a study around it.** Checked on 2026-08-27: '' | ||
| + | |||
| + | **What exists instead, and what it gives you.** Since April 2022 the HTTP Archive crawls **one secondary page per site** alongside the root, and both are in the public BigQuery dataset with '' | ||
| + | |||
| + | ===== What the Crawler Does on the Page ===== | ||
| + | |||
| + | Below the depth question there is a second one that no reporting checklist covers: once the page is open, does anything happen? The corpus has no field for it, so the only way to count is to sweep the full text. Every figure in this section is an **upper bound** on "the paper did this" — it counts papers with a first-person sentence matching the pattern, and "we did not scroll" | ||
| + | |||
| + | ^ What the paper' | ||
| + | | clicks something | 282 | 32.9% | | ||
| + | | scrolls | 69 | 8.1% | | ||
| + | | waits or dwells for a stated time | 56 | 6.5% | | ||
| + | | types on the keyboard | 39 | 4.6% | | ||
| + | | fills a form | 28 | 3.3% | | ||
| + | | moves the mouse | 18 | 2.1% | | ||
| + | | hovers | 14 | 1.6% | | ||
| + | | picks links at random | 14 | 1.6% | | ||
| + | | says it aims for human-like behaviour | 14 | 1.6% | | ||
| + | | mentions bot or crawler detection | 61 | 7.1% | | ||
| + | |||
| + | The only two rows with a clear trend across the sixteen years are **scrolling** — 2.5% of web crawls in 2010–2013 against 11.1% in 2025–2026 — and **bot detection**, | ||
| + | |||
| + | ==== What a strategy can and cannot see ==== | ||
| + | |||
| + | The table below is **measured**, | ||
| + | |||
| + | < | ||
| + | strategy | ||
| + | ------------------------------------------------------------------- | ||
| + | landing page only 1 ✓ ✗ ✗ ✗ ✗ | ||
| + | landing + FIRST same-origin link (the HTTP Archive rule) | ||
| + | landing + ALL same-origin links from the landing page 4 ✓ ✓ ✗ ✗ ✗ | ||
| + | landing + all links, and scroll to the bottom of each 4 ✓ ✓ ✓ ✗ ✗ | ||
| + | landing + all links, scroll, and click every button | ||
| + | depth 2: landing, its links, and their links, with scroll and click 5 ✓ ✓ ✓ ✓ ✓ | ||
| + | |||
| + | t-landing | ||
| + | t-article | ||
| + | t-scroll | ||
| + | t-click | ||
| + | t-deep | ||
| + | |||
| + | Playwright 1.62.1, Chromium 151.0.7922.34, | ||
| </ | </ | ||
| - | For large code, use ''< | + | Reproduce with [[# |
| - | < | + | Read off the second row. Following the **first** same-origin link found nothing that the landing page had not already shown, because the first link in DOM order is the //About// page and the //About// page is empty of third parties. That is not a quirk of the fixture — it is what the HTTP Archive' |
| - | <?php echo "hello world!"; | + | |
| + | Read off the last row. Each additional capability found exactly one thing the previous one could not, and no amount of extra depth substitutes for the scroll, nor extra scrolling for the click. **These are not degrees of the same knob.** A design that goes three levels deep without scrolling still misses everything below the fold. | ||
| + | |||
| + | ==== Interacting is not free, and it is not only your cost ==== | ||
| + | |||
| + | Everything above says //do more//. Before you scale it to ten thousand sites, four consequences that a landing-page crawl does not have: | ||
| + | |||
| + | * **A click on an ad is an ad impression somebody pays for.** Ad clicks are billed. A crawl that clicks indiscriminately at scale generates click fraud, and the money comes out of an advertiser' | ||
| + | * **A form submission has a side effect on a real system.** Filling and submitting forms at scale sends real newsletter signups, real support tickets, real account registrations and real search queries against production databases. This is why EvoCrawl' | ||
| + | * **Interaction multiplies your load by more than you expect.** The Khaleesi comparison above is 3× the requests from the same list. Rate limits, politeness delays and any '' | ||
| + | * **You are more visible, and being detected is not neutral.** A crawler that clicks and scrolls looks less like a bot to a fraud model and more like a user — which is the point, and is also why the resulting impressions and clicks are harder for the site to filter out. "We made the crawler look human" and "we made our traffic indistinguishable from the traffic the site bills for" are the same sentence. | ||
| + | |||
| + | None of this argues for staying on the landing page. It argues for saying, in the paper, which interactions you performed, on how many sites, and what you did to keep them from being charged for. | ||
| + | |||
| + | ==== The code ==== | ||
| + | |||
| + | Save both files side by side; the second imports the first. | ||
| + | |||
| + | < | ||
| + | // A minimal instrumented site for the interaction probe: a landing page, three | ||
| + | // same-origin subpages, and third-party beacons that fire under DIFFERENT | ||
| + | // conditions. It is built to hit exactly the disagreements this page is about — | ||
| + | // a beacon only on a subpage, a beacon only after scrolling, a beacon only | ||
| + | // after a click, and a page only reachable two links deep. | ||
| + | // | ||
| + | // Third parties are served from a SECOND origin (127.0.0.2) so the crawler | ||
| + | // counts them as third-party the way a real measurement would. | ||
| + | // | ||
| + | // PORTABILITY: | ||
| + | // macOS does not — binding 127.0.0.2 fails with EADDRNOTAVAIL until you add the | ||
| + | // alias: | ||
| + | // (undo with `sudo ifconfig lo0 -alias 127.0.0.2`). On Windows, use WSL. | ||
| + | import http from ' | ||
| + | |||
| + | const PAGE = (title, body) => `<!doctype html>< | ||
| + | < | ||
| + | |||
| + | export function startSite({ port = 8231, tpPort = 8232 } = {}) { | ||
| + | const requests = []; // every request BOTH origins received | ||
| + | const log = (origin, url) => requests.push({ origin, url }); | ||
| + | |||
| + | const first = http.createServer((req, | ||
| + | log(' | ||
| + | const tp = (name) => `http:// | ||
| + | if (req.url === '/' | ||
| + | return send(res, PAGE(' | ||
| + | < | ||
| + | < | ||
| + | <a href="/ | ||
| + | <a href="/ | ||
| + | <a href="/ | ||
| + | </ | ||
| + | <img src=" | ||
| + | } | ||
| + | if (req.url === '/ | ||
| + | // A quiet subpage: no third party at all. Included so that "pick one | ||
| + | // subpage" | ||
| + | return send(res, PAGE(' | ||
| + | } | ||
| + | if (req.url === '/ | ||
| + | return send(res, PAGE(' | ||
| + | < | ||
| + | <img src=" | ||
| + | <a href="/ | ||
| + | <div class=tall></ | ||
| + | <img id=lazy alt=""> | ||
| + | < | ||
| + | // Fires only when the bottom of the page is reached. | ||
| + | addEventListener(' | ||
| + | if (window.scrollY + innerHeight >= document.body.scrollHeight - 50) { | ||
| + | const el = document.getElementById(' | ||
| + | if (!el.src) el.src = ' | ||
| + | } | ||
| + | }, { passive: true }); | ||
| + | </ | ||
| + | } | ||
| + | if (req.url === '/ | ||
| + | // Two links deep: only linked from /article, never from the landing page. | ||
| + | return send(res, PAGE(' | ||
| + | } | ||
| + | if (req.url === '/ | ||
| + | return send(res, PAGE(' | ||
| + | <h1>shop</ | ||
| + | <button id=b> | ||
| + | <img id=ad alt=""> | ||
| + | < | ||
| + | document.getElementById(' | ||
| + | document.getElementById(' | ||
| + | }); | ||
| + | </ | ||
| + | } | ||
| + | res.writeHead(404, | ||
| + | res.end(' | ||
| + | }); | ||
| + | |||
| + | const third = http.createServer((req, | ||
| + | log(' | ||
| + | res.writeHead(200, | ||
| + | res.end(Buffer.from(' | ||
| + | }); | ||
| + | |||
| + | const send = (res, html) => { | ||
| + | res.writeHead(200, | ||
| + | res.end(html); | ||
| + | }; | ||
| + | |||
| + | return Promise.all([ | ||
| + | new Promise((r) => first.listen(port, | ||
| + | new Promise((r) => third.listen(tpPort, | ||
| + | ]).then(() => ({ requests, close: () => { first.close(); | ||
| + | } | ||
| </ | </ | ||
| - | === Figures === | + | <file javascript interaction_probe.mjs> |
| + | // What does each interaction strategy actually see? Drives Playwright' | ||
| + | // Chromium against the local instrumented site in interaction_site.mjs and | ||
| + | // reports, per strategy, which third-party beacons were observed. | ||
| + | // | ||
| + | // npm i playwright && npx playwright install chromium | ||
| + | // node interaction_probe.mjs | ||
| + | // | ||
| + | // Read as: a ✓ means the strategy OBSERVED that third party. The fixture has | ||
| + | // five, each reachable only under a different condition, so the table is a | ||
| + | // direct statement of what a crawl design can and cannot see. | ||
| + | import { chromium } from ' | ||
| + | import { startSite } from ' | ||
| - | To use floats, you have to use the ''< | + | const PORT = 8231; |
| - | <WRAP right 50% box> | + | const TP = 8232; |
| - | {{PATH_TO_FILE|ALT_TEXT}} | + | const ORIGIN = `http:// |
| - | < | + | const site = await startSite({ port: PORT, tpPort: TP }); |
| - | </ | + | |
| - | */ | + | const BEACONS = [' |
| - | ====== References ====== | + | // Collect the third-party hostnames+paths seen during one strategy. |
| + | let chromiumVersion | ||
| - | /* | + | async function run(name, fn) { |
| - | To insert citations, follow these steps: | + | const seen = new Set(); |
| + | const browser = await chromium.launch(); | ||
| + | chromiumVersion = browser.version(); | ||
| + | const context = await browser.newContext(); | ||
| + | context.on(' | ||
| + | const u = new URL(r.url()); | ||
| + | if (u.hostname !== ' | ||
| + | }); | ||
| + | const page = await context.newPage(); | ||
| + | const pagesVisited = await fn(page); | ||
| + | await page.waitForTimeout(300); | ||
| + | await browser.close(); | ||
| + | return { name, seen, pagesVisited }; | ||
| + | } | ||
| - | | + | // Same-origin links on the current page, in DOM order — the order a crawler |
| - | | + | // gets them in unless |
| - | - Keep this section unchanged to display the bibliography. | + | const links = (page) => |
| + | | ||
| + | as.map((a) => a.href).filter((h) => new URL(h).hostname === location.hostname)); | ||
| - | If any step fails, a purple warning | + | const results = []; |
| - | */ | + | |
| + | results.push(await run(' | ||
| + | await page.goto(ORIGIN + '/' | ||
| + | return 1; | ||
| + | })); | ||
| + | |||
| + | results.push(await run(' | ||
| + | await page.goto(ORIGIN + '/' | ||
| + | const [first] = await links(page); | ||
| + | await page.goto(first); | ||
| + | return 2; | ||
| + | })); | ||
| + | |||
| + | results.push(await run(' | ||
| + | await page.goto(ORIGIN + '/' | ||
| + | const ls = await links(page); | ||
| + | for (const l of ls) await page.goto(l); | ||
| + | return 1 + ls.length; | ||
| + | })); | ||
| + | |||
| + | results.push(await run(' | ||
| + | await page.goto(ORIGIN + '/' | ||
| + | const ls = await links(page); | ||
| + | for (const l of ls) { | ||
| + | await page.goto(l); | ||
| + | await page.evaluate(() => window.scrollTo(0, | ||
| + | await page.waitForTimeout(200); | ||
| + | } | ||
| + | return 1 + ls.length; | ||
| + | })); | ||
| + | |||
| + | results.push(await run(' | ||
| + | await page.goto(ORIGIN + '/' | ||
| + | const ls = await links(page); | ||
| + | for (const l of ls) { | ||
| + | await page.goto(l); | ||
| + | await page.evaluate(() => window.scrollTo(0, | ||
| + | for (const b of await page.locator(' | ||
| + | await page.waitForTimeout(200); | ||
| + | } | ||
| + | return 1 + ls.length; | ||
| + | })); | ||
| + | |||
| + | results.push(await run(' | ||
| + | const visited = new Set(); | ||
| + | const queue = [ORIGIN + '/' | ||
| + | let depth = 0; | ||
| + | while (queue.length && depth <= 2) { | ||
| + | const next = []; | ||
| + | for (const url of queue) { | ||
| + | if (visited.has(url)) continue; | ||
| + | visited.add(url); | ||
| + | await page.goto(url); | ||
| + | await page.evaluate(() => window.scrollTo(0, | ||
| + | for (const b of await page.locator(' | ||
| + | await page.waitForTimeout(200); | ||
| + | next.push(...(await links(page))); | ||
| + | } | ||
| + | queue.length = 0; | ||
| + | queue.push(...next.filter((u) => !visited.has(u))); | ||
| + | depth++; | ||
| + | } | ||
| + | return visited.size; | ||
| + | })); | ||
| + | |||
| + | site.close(); | ||
| + | |||
| + | const W = Math.max(...results.map((r) => r.name.length)); | ||
| + | console.log( | ||
| + | [' | ||
| + | ); | ||
| + | console.log([' | ||
| + | for (const r of results) { | ||
| + | console.log( | ||
| + | [ | ||
| + | r.name.padEnd(W), | ||
| + | String(r.pagesVisited).padStart(5), | ||
| + | ...BEACONS.map((b) => (r.seen.has(b) ? ' | ||
| + | ].join(' | ||
| + | ); | ||
| + | } | ||
| + | const pwVersion = (await import(' | ||
| + | console.log(` | ||
| + | t-landing | ||
| + | t-article | ||
| + | t-scroll | ||
| + | t-click | ||
| + | t-deep | ||
| + | |||
| + | Playwright ${pwVersion}, | ||
| + | </ | ||
| + | |||
| + | ===== Forms, Logins and the Half of the Web You Are Not Measuring ===== | ||
| + | |||
| + | Of the 857 web crawls in this corpus, **553 (64.5%)** are recorded as not authenticating, | ||
| + | |||
| + | Two things worth knowing before you decide the effort is not worth it. | ||
| + | |||
| + | **Interacting with forms buys real coverage, and it is measurable.** EvoCrawl replaces "fill every field and trigger every event" with an evolutionary search over // | ||
| + | |||
| + | **Typing is not the same as setting '' | ||
| + | |||
| + | > we simulate user typing behavior by using randomized intervals for each key press and dwell times, as well as the delay times between each press | ||
| + | |||
| + | A form-filler that assigns to '' | ||
| + | |||
| + | The rest of the login problem — Shepherd-style automated login, SSO, and automated registration — is [[Programming: | ||
| + | |||
| + | ===== Your Crawler Will Be Noticed ===== | ||
| + | |||
| + | Interaction is the axis on which a crawler most visibly stops resembling a user, and the share of papers that say so is rising: **not one of the 80 web crawls in the 2010–2013 bucket describes bot or crawler detection in a first-person sentence — three mention it in passing — against 11.8% of the 2025–2026 bucket.** | ||
| + | |||
| + | The concrete failure modes to plan for: | ||
| + | |||
| + | * **A challenge you cannot pass.** Drakonakis et al., auditing authentication flows: "In 22 cases, there was some form of an anti-bot challenge that our system was not able to solve and, thus, could not proceed with registration." | ||
| + | * **Different content, not an error.** Cloaking is served, not refused. Whether you were cloaked is not visible in your success rate. | ||
| + | * **Interaction is itself the tell.** A crawler that never scrolls, never moves the mouse and clicks in the geometric centre of every element is easy to classify. Adding interaction to look more like a user and adding interaction to measure more of the page are the same action with two justifications, | ||
| + | |||
| + | The gap between crawler and user is large and one-directional. Zeber et al. compared an OpenWPM crawl against telemetry from over 50,000 opt-in Firefox users over the same period: a median of **11.6** third-party domains per crawler visit against **4.5** for humans, and **6.1** tracking domains against **1.9**, on the same site domains {[zeber2020representativeness]}. Statefulness, | ||
| + | |||
| + | ===== Which Methods Are Current ===== | ||
| + | |||
| + | Dating methods matters more here than on most pages, because the corpus' | ||
| + | |||
| + | ^ Method ^ Status ^ Evidence ^ | ||
| + | | **Landing-page-only crawls of a ranked list** | **Still the single commonest design, and still defensible for the right question** — but no longer defensible // | ||
| + | | **" | ||
| + | | **A saturation study to choose the count** | **Done once, in 2020, and not replicated.** If you do it for your own metric you will be the second | {[urban2020beyond]} | | ||
| + | | **A purpose-built internal-page top list** | **Superseded by circumstance.** Hispar is offline and its bootstrap list is retired. The HTTP Archive' | ||
| + | | **Scrolling as a routine | ||
| + | | **Mouse-movement and hover emulation** | **Never established.** Around 2% throughout, with no trend. Do not assume | ||
| + | | **LLM-agent-driven browsing** | **Genuinely new, and thin.** First appears in **2025**; **5 of 857** web crawls in the whole corpus use an agent as the browsing instrument, all in 2025–2026 | see below | | ||
| + | |||
| + | ==== The LLM-agent slice, counted honestly ==== | ||
| + | |||
| + | A full-text sweep for agent frameworks and LLM-driven browsing over the 857 web crawls returns **35** papers. Reading each one's deciding sentence puts them in six groups: 14 use an LLM for text classification with no browsing at all, 9 study agents as the //subject// of measurement, | ||
| + | |||
| + | That is **0.6% of web crawls**, and every one is from 2025 or 2026. So: | ||
| + | |||
| + | * **It is real.** Song et al. is the sharpest case for it: models trained on scripted-crawler traffic scored under 10% accuracy against traffic from 30 real users, and training on LLM-agent, persona-driven browsing instead put accuracy "into the 80% range" {[song2026_wfpllm]}. If your measurement is downstream of anything learned from crawler traffic, scripted-crawl realism is the dominant error term, not a caveat. | ||
| + | * **It is not yet a method with known properties.** Sun et al. report verified workflow completion of **87%** and **79%** in two phases across 456 data-broker sites {[sun2026_suitability]} — that is a paper about whether the technique works, which is the stage the technique is at. | ||
| + | * **The evidence base is the thinnest part of this corpus.** 2026 venue-years are incomplete by construction. Five papers is a signal, not a rate; treat "LLM agents are now how this is done" as unsupported. | ||
| + | |||
| + | ===== What to Report ===== | ||
| + | |||
| + | A methods paragraph that answers these is enough for a reviewer to know what your numbers count. None of them takes more than a clause. | ||
| + | |||
| + | - **The unit.** Pages or sites? If a site contributed several pages, is your prevalence over sites or over pages? Say which, and say how you aggregated (any-page, majority, mean). | ||
| + | - **The depth.** Landing page only, landing plus //N//, or traverse to depth //d// with budget //b//. If you stayed on the landing page, say so — it is a legitimate choice with a citable justification. | ||
| + | - **How you chose the pages.** The link-selection rule verbatim: same-origin or same-site, DOM order or random, seed if random, exclusions, and whether the frontier was harvested once and frozen or re-harvested per arm. | ||
| + | - **What happened on the page.** Scroll? Click? Type? Wait how long, and for what event — '' | ||
| + | - **What you did about the banner and the login**, with a pointer to [[Privacy: | ||
| + | - **The failures.** How many target sites yielded zero pages, how many yielded fewer subpages than you asked for, and how many hit a challenge you could not pass. All three are non-random. | ||
| + | - **The comparison, if you make one.** If you compare against a published prevalence, check the other paper' | ||
| + | |||
| + | ===== What a Complete Statement Looks Like ===== | ||
| + | |||
| + | Two sentences from Aziz and Wilson are, as far as this corpus goes, the model {[aziz2024_johnny]}: | ||
| + | |||
| + | > For each domain, we programmed the crawler to load the domain' | ||
| + | |||
| + | Five of the seven items on the checklist above are in those two sentences: the unit (domain), the depth (homepage plus nine), the selection rule (at random, from the homepage), the on-page action (scroll to the bottom) and the wait (25 seconds, stated). Plus the detail that most often silently differs and is on none of the checklists: the subpages get the **same** load-scroll-sleep treatment as the landing page, which is what makes a landing-versus-subpage comparison a comparison rather than two different instruments. The two items it does not cover are the consent treatment — which is elsewhere in that paper, whose whole design is four experimental conditions — and the failure counts. Splitting them across the methods section is the usual and fine arrangement; | ||
| + | |||
| + | Five of seven, in two sentences. That is the whole cost. | ||
| + | |||
| + | ===== If You Just Need a Default ===== | ||
| + | |||
| + | Absent a better reason — and the sections above are the better reasons — this is a defensible starting design for a privacy or security crawl over a ranked site list, and every element of it is something you can point a reviewer at: | ||
| + | |||
| + | - **Harvest the frontier once.** Visit each landing page, collect its same-site links, store them with the date. Freeze that list and replay it in every arm of the experiment. | ||
| + | - **Pick subpages at random from it, with a published seed**, and publish the exclusion list if you use one. Do not take them in DOM order unless you want the navigation chrome. | ||
| + | - **Start at ten and run a saturation pilot for //your// metric** on a few hundred sites before committing to the number. Ten is the field' | ||
| + | - **On every page, including the landing page: scroll to the bottom, then wait a fixed, stated time.** Same treatment | ||
| + | | ||
| + | - **Crawl logged out**, and say so. Getting past a login is a project, not a setting. | ||
| + | - **Log the failures** — sites yielding zero pages, sites yielding fewer subpages than asked, challenges you could not pass — and report all three. | ||
| + | |||
| + | The point of the default is not that it is optimal. It is that every line of it is a sentence you can write in the methods section, which is more than most published crawls manage. | ||
| + | |||
| + | ===== Methodology and Limitations of These Figures ===== | ||
| + | |||
| + | All corpus figures on this page come from the extraction over the seven venues in [[literature: | ||
| + | |||
| + | Three limitations specific to this page: | ||
| + | |||
| + | * **The denominator is 857, not 5,859.** Only 1,120 papers in the corpus ran a crawl at all, and 857 of those crawled the web. The depth ratios use a further-narrowed denominator of 417 for the reason given [[#Depth has more than three positions|above]]. | ||
| + | * **'' | ||
| + | * **The on-page-action figures are upper bounds by construction.** They count papers whose text matches a pattern in a first-person sentence, not papers that did the thing. | ||
| + | |||
| + | The full query log, the report script and its unedited output, the folds and their residue, the quotes spot-checked, | ||
| + | |||
| + | ===== Open Questions ===== | ||
| + | |||
| + | <WRAP todo> | ||
| + | //" | ||
| + | |||
| + | * **Nobody has repeated the saturation study.** Urban et al.'s curve is one metric (cookies), one rank band (top 1,000), one year (2019–2020). A 2026 replication for distinct third-party domains, for ad slots, and for the long tail of the ranking would settle the number the whole field is guessing at. | ||
| + | * **Selection rule versus count, held apart.** No paper in this corpus varies //which// subpages it picks while holding //how many// fixed. Ten nav links, ten random links and ten search-popular pages from the same sites would answer whether the rule or the count dominates. | ||
| + | * **What does a scroll actually add, at scale?** The fixture on this page shows it can matter. Nobody has published the distribution of "third parties visible only after scrolling" | ||
| + | * **A replacement for Hispar.** The HTTP Archive' | ||
| + | * **How much does an LLM agent actually change the measurement, | ||
| + | </ | ||
| + | |||
| + | ===== Related Pages ===== | ||
| + | |||
| + | * [[Privacy: | ||
| + | * [[Programming: | ||
| + | * [[Programming: | ||
| + | * [[Programming: | ||
| + | * [[Design: | ||
| + | * [[Privacy: | ||
| + | * [[Design: | ||
| + | |||
| + | ====== References ====== | ||
| <bibtex bibliography></ | <bibtex bibliography></ | ||
| Line 71: | Line 617: | ||
| /* This enables discussion under this article. */ | /* This enables discussion under this article. */ | ||
| ~~DISCUSSION~~ | ~~DISCUSSION~~ | ||
| + | |||
programming/interaction.1736932177.txt.gz · Last modified: by admin
