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)
| File | Size | Uploaded | |
|---|---|---|---|
| celcat_scraper-1.1.3.tar.gz | 15.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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