# Watching a page for changes

A script that scans a page on a schedule, compares it with the last capture, and says so only when it changed.

Page Scanner has no watcher of its own, and needs none: `scan` writes the page's text, `diff`
compares two captures and exits `1` when they differ, and cron or launchd runs the two on a
schedule. The script below joins them. It keeps the last capture, and a dated copy of the page and
of what changed each time it does change.

```sh
#!/bin/sh
# watch-page.sh <address> <folder> [heading]
# Scans the page, compares it with the last capture, and keeps the new one only when it changed.
url=$1
dir=$2
section=$3
mkdir -p "$dir" || exit 2

page-scanner scan --url "$url" --markdown only --main-content --hide all \
  --out "$dir/new.md" --wait 120 > /dev/null || exit 2

if [ ! -f "$dir/last.md" ]; then
  mv "$dir/new.md" "$dir/last.md"
  exit 0
fi

if [ -n "$section" ]; then
  page-scanner diff "$dir/last.md" "$dir/new.md" --section "$section" > "$dir/changes.diff"
else
  page-scanner diff "$dir/last.md" "$dir/new.md" > "$dir/changes.diff"
fi
status=$?

if [ "$status" -eq 1 ]; then
  stamp=$(date +%Y%m%d-%H%M)
  cp "$dir/new.md" "$dir/$stamp.md"
  mv "$dir/changes.diff" "$dir/$stamp.diff"
  mv "$dir/new.md" "$dir/last.md"
  echo "$url changed, see $dir/$stamp.diff"
else
  rm -f "$dir/new.md" "$dir/changes.diff"
fi
exit "$status"
```

Save it as `watch-page.sh`, `chmod +x watch-page.sh`, and try it by hand twice:

```bash
./watch-page.sh https://example.com/pricing ~/Watch/pricing "Plans > Pro"
```

The first run keeps the capture as `last.md`. Each run after that exits `0` and leaves nothing
behind when the page is the same, or exits `1`, prints one line and writes
`20260930-0800.md` and `20260930-0800.diff` beside it when it changed. It exits `2` when the scan
failed or the two captures could not be compared, and `last.md` is left as it was.

## Keeping out what moves on its own

A page that shows a clock, a visitor count or a "latest posts" rail changes on every load, and a
watch on the whole of it reports a change every time. Three things in the script keep that out.

- **The heading.** The third argument is passed to `diff --section`, which compares only the
  section under that heading, down to the next heading of the same level or higher. A path such
  as `"Plans > Pro"` picks a heading under another. On a page with live counters at the top, a
  whole-page comparison changed on every run and the same two captures compared by one section
  under them did not. The section is found by its words, not by the page's markup, so a redesign
  that keeps the heading keeps the watch.
- **`--main-content`** writes only the page's main content, leaving out navigation, sidebars and
  the footer. If `diff` then says neither capture has your heading, the heading sits outside what
  it found: take the flag out of the script.
- **`--hide all`** hides ads, cookie banners, chat widgets and pop-ups before the capture, whatever
  the extension's settings say, so a rotating ad is not a change.

What none of them can drop is a part that changes on every load inside the section you watch, such
as a label an ad script writes into a table's caption. Pick a heading further down, around less.
Which changes matter is left to you: the `.diff` is the whole of the answer.

## Running it on a schedule

The scheduler is your system's, set up as in [On a schedule](https://docs.pagescanner.app/cli/commands#on-a-schedule): Chrome
running in the profile you connected, and on the older port-and-token path the daemon started at
login. Each run opens the page in a background tab of that Chrome and shows the debugger bar for
the length of the scan, so a page you are signed in to is watched signed in.

The script prints one line on stdout when the page changed and nothing when it did not; what
`scan` and `diff` say along the way goes to stderr. So whatever runs it can pass that one line on.

**cron**, every hour at minute 5. cron mails a job's output to `MAILTO`, and with stderr sent to a
log that output is the one line, only when the page changed:

```bash
PATH=/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin
MAILTO=you@example.com
5 * * * * ~/bin/watch-page.sh https://example.com/pricing ~/Watch/pricing "Plans > Pro" 2>> ~/Watch/watch.log
```

That needs a computer that can send mail. Without one, add a notification after the `echo`
instead: `osascript -e 'display notification "The pricing page changed" with title "Page Scanner"'`
on macOS, `notify-send` on Linux.

**launchd** runs it every hour with `StartInterval` set to 3600 in place of
`StartCalendarInterval`, and `ProgramArguments` running `~/bin/watch-page.sh` with its arguments
through `zsh -lc`, as in the morning scan's agent.

## Several pages

Give each page its own folder and its own line in the crontab. A folder holds one page's
`last.md`, so two pages sharing one would be compared with each other.
