jjsm01 ddfe4bccba fix: rewrite web scraper to use network response interception
Replace the JS variable polling approach with Playwright response
interception — listen for DataTables AJAX responses at the network
layer instead of waiting for the  JS variable to populate.

- Use 'domcontentloaded' instead of 'networkidle' (site keeps
  long-poll connections open, preventing networkidle from firing)
- Capture /PaginatedTenderOpportunities responses via page.on('response')
- Paginate through all pages using table.page('next').draw()
- Verified live: 500 rows fetched, 173 multi-doc tenders found
- Full pipeline tested: 149 OCDS tenders → 141 matched & enriched
  with web UI docs → 34 tenders now have >1 document in DB
2026-07-03 09:25:21 +00:00

SA Tender Monitor

Automated South African tender monitoring system that pulls tenders from multiple online sources and makes them available for search, filtering, and notification.

Architecture

Hybrid data pipeline:

  1. eTenders OCDS API — primary source for tender discovery (titles, dates, status, buyer info). Covers all government, SOE, and public entity tenders.
  2. eTenders Web UI scraping — supplements the OCDS API by fetching the full supportDocument[] array for each tender (the OCDS API only returns 1 document per tender; the web UI shows all attached files).
  3. Supplementary sources — RSS feeds from Armscor, Health.gov.za, CIDB, sa-tenders.co.za; scraping for Eskom, Transnet, DBSA, IDC, and other SOE portals not fully captured in the OCDS API.

Key Technical Detail

The eTenders OCDS API (https://ocds-api.etenders.gov.za/api/OCDSReleases) returns OCDS-compliant JSON with one critical limitation: it only exposes a single primary advert document per tender in tender.documents[]. Supporting documents (additional attachments uploaded by the tender issuer) are NOT in the OCDS API — they only appear in the web UI's internal DataTables endpoint (/Home/PaginatedTenderOpportunities), which returns each tender row with a supportDocument[] array containing all attached files.

This system uses a hybrid approach:

  • OCDS API for discovery and metadata
  • Headless browser scraping for the full document list per tender

Sources

See docs/sources.md for the comprehensive research report covering 75+ SA tender sources with automation details.

Project Structure

sa-tender-monitor/
├── README.md
├── .gitignore
├── requirements.txt
├── docs/
│   └── sources.md          # SA tender sources research report
├── src/
│   ├── __init__.py
│   ├── ocds_client.py      # eTenders OCDS API client
│   ├── web_scraper.py      # eTenders web UI document scraper
│   ├── models.py            # Data models (Tender, Document, Buyer)
│   ├── db.py                # SQLite storage
│   ├── pipeline.py          # Orchestration: OCDS → scrape → store
│   └── config.py           # Configuration
├── data/                   # Cached/raw data (gitignored)
└── tests/
    └── test_ocds_client.py

Setup

cd /root/workspace/sa-tender-monitor
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Usage

# Pull tenders from the last 7 days
python -m src.pipeline --days 7

# Pull tenders for a specific date range
python -m src.pipeline --from 2025-06-01 --to 2025-06-30
Description
Automated SA tender monitoring system — pulls tenders from eTenders OCDS API + web UI scraping
Readme 66 KiB
Languages
Python 100%