Scraper
Turns websites that have no API into JSON your Shortcuts and Scriptable scripts can read. One generic Playwright script for the easy sites, custom code for the awkward ones.
Why this exists
Many niche sites don't have an API, even though you wish they'd serve their information more easily. I've always wanted to plug these sites into real, usable programs like Shortcuts and Scriptable.
This setup does that, or tries to, depending on the site. It uses Playwright, a Python library that runs Chromium (the open-source engine behind Chrome) as an invisible browser. It clicks, types and logs in just like a person would, only automatically. We are basically the robots that CAPTCHA tests are trying to catch.
Two ways to use it
A config file covers most simple sites. You describe the page in a small JSON file (where to go, how to log in, which bits to grab) and scraper.py does the rest. No code to write.
A custom script is for sites a config can't handle. McDonald's myRestaurant logs in through Microsoft SSO and buries shifts in messy HTML, so it has its own script, scrape_roster.py. That one is the real-world example further down this page.
Files
Setup
-
Download the code
Clone or download the repo, then open a terminal (like Command Prompt) in the folder by pasting the commands below.
git clone https://github.com/Walter-CK/WalterCK.git cd WalterCK/scraper -
Install Python
You need Python 3.10 or newer. If running
python --versionin a terminal doesn't show that, download it from python.org. On Windows, tick "Add Python to PATH" during install. You'll need it for a lot of other stuff anyway. -
Install the dependencies
pip install -r requirements.txt playwright install chromiumThe second command downloads the Chromium browser that Playwright runs its robot on.
-
Add your login
Copy
.env.exampleto a new file called.envand fill in whatever the site needs. Some sites have no login at all. The demo accepts any values:QUOTES_USERNAME=any-name-works QUOTES_PASSWORD=any-password-worksThis file never leaves your machine. It's listed in.gitignore, so a normalgit pushwon't include it. Don't paste your real credentials anywhere else, for security ofc. Only automate logins you're allowed to: some sites ban bots in their terms and can lock your account. -
Run it
python scraper.py sites/quotes-demo.json --showThe
--showflag pops up a visible browser window so you can watch the login happen and confirm it works. Once you've seen it succeed, drop the flag and it runs invisibly.A successful run ends with a line like
Wrote 10 items to output/quotes-demo.json. If it fails partway through, it saves a screenshot and the page's HTML intooutput/so you can see exactly what it was looking at when it broke. -
Point it at your own site
Copy
sites/quotes-demo.json, change the URLs, and swap in your own selectors (right-click a page, Inspect, and copy the CSS selector). The keys:Key What it does urlThe page to read once you're logged in. wait_forA selector that only exists once the page has loaded its data. loginOptional. Login page URL, the username, password and submit selectors, and the .envnames holding your credentials. Addnext_selectorfor sites that ask for the username first.extract.rowsThe selector for one repeating item (a quote, a notice, a shift). extract.fieldsWhat to pull out of each row. "span.text"gives the text,"a@href"gives an attribute.
Real-world example: McDonald's roster
This is the one I built the whole thing for. myRestaurant has no API, no calendar export and no notification when a new roster drops, so scrape_roster.py logs in through Microsoft SSO, reads the roster page and turns every shift into JSON. The rest of this page walks through that setup.
What comes out
Every run produces a fresh roster.json, shifts grouped by the roster's own weekly periods:
{
"last_updated": "2026-09-25T09:25:00+10:00",
"weeks": [
{
"week_start": "21/09/26",
"week_end": "27/09/26",
"shifts": [
{
"date": "23/09/26",
"start": "1:00 PM",
"end": "4:00 PM",
"location": "BERRINBA QLD",
"hours": "3:00"
}
]
}
]
}
hours is a duration, not a decimal ("3:15" means 3 hours 15 minutes, not 3.15 hours.)
Getting it onto your phone
The script runs on a computer, but the point is having your shifts on your phone without opening anything. The way I've got it wired up:
Push to GitHub after each run
The script commits and pushes roster.json automatically if it changed, so the latest roster is always sitting in the repo with no manual upload step.
Run it via GitHub Actions
A GitHub Actions workflow runs the script daily in the cloud, ensuring a newly published roster shows up automatically without requiring a local machine to remain powered on.
Pull it into a Shortcut
An iOS Shortcut fetches roster.json straight from GitHub, reads through each week and shift, and sets an alarm ahead of each one. Private repo? The Shortcut authenticates with a fine-grained token scoped to read-only access on just this repo.
GitHub Actions Setup
GitHub Actions automates the scraping process entirely in the background.
-
Repo Secrets
Store your myRestaurant credentials as GitHub Repository Secrets (
MYJOB_USERNAMEandMYJOB_PASSWORD). The workflow securely injects these during execution. -
The Workflow File
The
scrape (1).ymlfile defines the environment. It installs Python, sets up the Playwright browser, executes the script, and commits the updatedroster.jsonback to the repository. -
The Schedule
The workflow utilizes cron to run automatically every day at 12:00 AM UTC (
0 0 * * *). It also supportsrepository_dispatch, allowing the iOS Shortcut to trigger a fresh scrape on demand. -
Token Setup
To allow the iOS Shortcut to read the private repository and trigger dispatches, generate a GitHub Personal Access Token (PAT). Scope this token strictly to read/write access for this specific repository and provide it to the Shortcut during setup.
What the Shortcut actually does
The Shortcut is the other half of this system. It reads roster.json and turns it into something you actually look at, without opening the myRestaurant app.
-
Pulls your latest roster
Fetches
roster.jsonstraight from GitHub and groups your shifts into This Week and Next Week, showing the day, start time and end time for each one. -
Sets alarms ahead of each shift
Choose Create/Change Alarms, tell it how many minutes before a shift you want waking up, and it adds one alarm per upcoming shift automatically.
-
Keeps your alarms in sync
Run it again after a new roster's published and it clears out the old shift alarms before creating the new ones, so you're never left with a stale alarm for a shift that got moved or dropped.
-
Triggers a fresh scrape on demand
Choose Re-scan shifts and the Shortcut asks GitHub to run the scraper again right away, instead of waiting for the next scheduled run so you can check the moment a new roster gets published.
roster.json (and running your own copy of the scraper, or one adapted to your employer's portal). It's shared as a working example to adapt, not a one-tap install.
Built by Walter MacLean. Source and issues on GitHub.