Skip to main content

Celcat Calendar Scraper 📆

An asynchronous Python library for scraping Celcat calendar systems.

Installation 🚀

pip install celcat-scraper

Features 🌟

  • Event data filtering 🧹
  • Async/await support for better performance 🔀
  • Rate limiting with adaptive backoff ⏳
  • Optional caching support 💾
  • Optional reusable aiohttp session ♻️
  • Automatic session management 🍪
  • Batch processing of events 📦
  • Error handling and retries 🚨

Usage ⚙️

Basic example of retrieving calendar events:

import asyncio
from datetime import date, timedelta
from celcat_scraper import CelcatConfig, CelcatScraperAsync

async def main():
    # Configure the scraper
    config = CelcatConfig(
        url="https://university.com/calendar",
        username="your_username",
        password="your_password",
        include_holidays=True,
    )

    # Create scraper instance and get events
    async with CelcatScraperAsync(config) as scraper:
        start_date = date.today()
        end_date = start_date + timedelta(days=30)

        # Recommended to store events locally and reduce the amout of requests
        file_path = "store.json"
        events = scraper.deserialize_events(file_path)

        events = await scraper.get_calendar_events(
            start_date, end_date, previous_events=events
        )

        for event in events:
            print(f"Event {event['id']}")
            print(f"Course: {event['category']} - {event['course']}")
            print(f"Time: {event['start']} to {event['end']}")
            print(f"Location: {', '.join(event['rooms'])} at {', '.join(event['sites'])} - {event['department']}")
            print(f"Professors: {', '.join(event['professors'])}")
            print("---")

        # Save events for a future refresh
        scraper.serialize_events(events, file_path)

if __name__ == "__main__":
    asyncio.run(main())

Filtering 🧹

Celcat Calendar data is often messy, and needs to be processed before it can be used. For example, the same course may have several different names in different events. Filtering allows these attributes to be standardized.

Usage ⚙️

ℹ️ Info: Each filter argument is optional. When course_strip_redundant is enabled, using remembered_strips is recommended.

⚠️ Warning: Disabling filters will require you to reset your previous events and refetch to undo changes.

import asyncio
from datetime import date, timedelta
import json
from celcat_scraper import CelcatFilterConfig, FilterType, CelcatConfig, CelcatScraperAsync

async def main():
    # Load remembered_strips from a file
    remembered_strips = []
    try:
        with open("remembered_strips.json", "r") as f:
            remembered_strips = json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        remembered_strips = []

    # Create a list of manual course replacements
    course_replacements = {"English - S2": "English", "Mathematics": "Maths"}

    # Configure a filter
    filter_config = CelcatFilterConfig(
        filters = {
            FilterType.COURSE_TITLE,
            FilterType.COURSE_STRIP_MODULES,
            FilterType.COURSE_STRIP_CATEGORY,
            FilterType.COURSE_STRIP_PUNCTUATION,
            FilterType.COURSE_GROUP_SIMILAR,
            FilterType.COURSE_STRIP_REDUNDANT,
            FilterType.PROFESSORS_TITLE,
            FilterType.ROOMS_TITLE,
            FilterType.ROOMS_STRIP_AFTER_NUMBER,
            FilterType.SITES_TITLE,
            FilterType.SITES_REMOVE_DUPLICATES,
        }
        course_remembered_strips=remembered_strips,
        course_replacements=course_replacements,
    )

    config = CelcatConfig(
        url="https://university.com/calendar",
        username="your_username",
        password="your_password",
        include_holidays=True,
        # Pass the filter as an argument
        filter_config=filter_config,
    )

    async with CelcatScraperAsync(config) as scraper:
        start_date = date.today()
        end_date = start_date + timedelta(days=30)

        events = scraper.deserialize_events("store.json")
        events = await scraper.get_calendar_events(
            start_date, end_date, previous_events=events
        )

        scraper.serialize_events(events, file_path)

    # Save the updated remembered_strips back to file
    with open("remembered_strips.json", "w") as f:
        json.dump(scraper.filter_config.course_remembered_strips, f)

if __name__ == "__main__":
    asyncio.run(main())

Available Filters 🧹

Filter Description Example
*_TITLE Capitalize only the first letter of each word MATHS CLASS -> Maths Class
COURSE_STRIP_MODULES Remove modules from courses names Maths [DPAMAT2D] -> Maths
COURSE_STRIP_CATEGORY Remove category from course names Maths CM -> Maths
COURSE_STRIP_PUNCTUATION Remove ".,:;!?" from text Math. -> Math
COURSE_GROUP_SIMILAR Search for all event names and group ones containing another Maths, Maths S1 -> Maths
COURSE_STRIP_REDUNDANT Extract parts removed by the previous filter and remove them from all other courses Physics S1 -> Physics
ROOMS_STRIP_AFTER_NUMBER Remove all text after the first number found Room 403 32 seats -> Room 403
SITES_REMOVE_DUPLICATES Remove duplicates from the list Building A, Building A -> Building A

Metadata

Release files for celcat-scraper 1.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for celcat-scraper 1.1.3
File Size Uploaded
celcat_scraper-1.1.3.tar.gz 15.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for celcat-scraper 1.1.3
File Interpreter ABI Platform
celcat_scraper-1.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 32.1 kB

Release files / celcat_scraper-1.1.3.tar.gz

Download URL celcat_scraper-1.1.3.tar.gz
Size 15.2 kB
Tags Source
SHA-256 checksum
How to use checksums
bf728347032a9d18da57d62acd133937cc236550de6ee4fad0213a18338431d0
BLAKE2b-256 checksum
How to use checksums
eda0137ab9dc1de625f5370a27671a5bb88c4bda1eecbb9899ae7b3568be0366
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 19, 2025.

Transparency log

Release files / celcat_scraper-1.1.3-py3-none-any.whl

Download URL celcat_scraper-1.1.3-py3-none-any.whl
Size 16.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
78f1d9eb83d8e2bcea9c97c084237aea0d5031718d0a740e8cd8bc49515817fb
BLAKE2b-256 checksum
How to use checksums
48aa708ac232c54f094dd404d399dd20455e0575526a2dc22b3372917dbcc145
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 19, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

1.1.3 This release

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page