A static site listing on-duty ("nöbetçi") pharmacies in Turkish provinces, built with Astro. Pharmacy data is scraped daily from official provincial chamber of pharmacists pages and committed as static JSON, so the deployed site needs no backend.
The scraping approach (selectors, parsing logic) is ported from the earlier
Gleam implementation on this repository's main branch, a scraper for the same
Antalya source page using headless Chrome. This version uses a plain fetch +
cheerio instead, since the source pages turned out
to be server-rendered and don't require JS execution.
src/
data/ scraped CityData JSON, one file per city (e.g. antalya.json)
lib/
cities.ts registry of known cities (slug, name, source URL)
types.ts Pharmacy / CityData contract shared with the UI
pages/ Astro pages (UI is owned separately from the scraper)
scripts/
scrape.ts scraper entrypoint, run per city
| Command | Action |
|---|---|
npm install |
Install dependencies |
npm run scrape -- <slug> |
Scrape a city (e.g. npm run scrape -- antalya) and write src/data/<slug>.json |
npm run dev |
Start local dev server |
npm run build |
Build the static site to ./dist/ |
npm run preview |
Preview the production build locally |
.github/workflows/deploy.yml builds the site and deploys dist/ to Cloudflare
Pages (project eczane) via cloudflare/wrangler-action@v3. It runs on push to
main and can be triggered manually via workflow_dispatch. The workflow does
not run the scraper — pharmacy data is committed to the repo, so the site builds
from whatever JSON is currently in src/data/.
Pharmacy data is refreshed on a host machine by running scripts/update-data.sh,
which re-scrapes antalya, commits any changes under src/data/, and pushes to
origin main. Schedule it with cron, for example daily at 06:17:
17 6 * * * /path/to/repo/scripts/update-data.sh
Each push then triggers the Cloudflare Pages deploy above.
- Add an entry to
src/lib/cities.tswith aslug, displayname, and the province's on-duty pharmacy source URL. - Inspect the source page's HTML and adjust the selectors in
scripts/scrape.tsif the markup differs from Antalya's (.ilce,.ilcebas,.nobetciDivstructure). - Run
npm run scrape -- <slug>and verifysrc/data/<slug>.jsonlooks correct (plausible pharmacy count, mostly non-null coordinates). - If you want the host-side refresh to cover the new city, add a corresponding
npm run scrape -- <slug>line toscripts/update-data.sh.