Files
blackcanyontickets/eventscrape.md
2025-07-08 12:31:31 -06:00

108 lines
3.2 KiB
Markdown

{\rtf1\ansi\ansicpg1252\cocoartf2822
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cspthree\c0\c0\c0;}
\margl1440\margr1440\vieww11520\viewh8400\viewkind0
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0
\f0\fs28 \cf2 Got it \'97 if the main site dev is a bottleneck, scraping is your best move.\
\
Here\'92s a battle-tested, lightweight scraping plan to monitor https://blackcanyontickets.com/events and detect when the currently active event changes, then extract details from the redirected event page to trigger a calendar update.\
\
\uc0\u11835 \
\
\uc0\u55358 \u56816 Scraper Stack Recommendation\
\
Tool Purpose\
node-fetch or axios Follow (or block) redirect from /events\
cheerio Parse HTML from the actual event page\
node-cron or supabase.functions.schedule() Run on a schedule\
fs or Supabase Store last seen event slug for diffing\
\
\
\uc0\u11835 \
\
\uc0\u9989 Working Scraper Skeleton (Node.js)\
\
import fetch from 'node-fetch';\
import cheerio from 'cheerio';\
import fs from 'fs/promises';\
\
const REDIRECT_URL = 'https://blackcanyontickets.com/events';\
const BASE_URL = 'https://blackcanyontickets.com';\
\
async function getCurrentEventSlug() \{\
const res = await fetch(REDIRECT_URL, \{ redirect: 'manual' \});\
return res.headers.get('location') || null;\
\}\
\
async function fetchEventDetails(slug) \{\
const res = await fetch(`$\{BASE_URL\}$\{slug\}`);\
const html = await res.text();\
const $ = cheerio.load(html);\
\
return \{\
slug,\
title: $('h1').first().text().trim(),\
date: $('[data-event-date]').text().trim(), // tweak selector to match\
time: $('[data-event-time]').text().trim(), // tweak selector to match\
\};\
\}\
\
async function loadLastSeenSlug() \{\
try \{\
return await fs.readFile('./last_slug.txt', 'utf-8');\
\} catch \{\
return null;\
\}\
\}\
\
async function saveLastSeenSlug(slug) \{\
await fs.writeFile('./last_slug.txt', slug);\
\}\
\
async function run() \{\
const currentSlug = await getCurrentEventSlug();\
if (!currentSlug) return console.log('No event redirect found');\
\
const lastSeen = await loadLastSeenSlug();\
if (currentSlug === lastSeen) \{\
return console.log('No new event');\
\}\
\
const details = await fetchEventDetails(currentSlug);\
console.log('\uc0\u55356 \u57247 \u65039 New event found:', details);\
\
// TODO: Push to calendar / Supabase / webhook\
\
await saveLastSeenSlug(currentSlug);\
\}\
\
run();\
\
\
\uc0\u11835 \
\
\uc0\u55357 \u56658 Optional: Add Cron Job\
\
With node-cron:\
\
import cron from 'node-cron';\
\
cron.schedule('*/15 * * * *', () => \{\
run();\
\});\
\
Or deploy to:\
\'95 A lightweight VM\
\'95 Supabase Edge Function (on trigger)\
\'95 GitHub Actions (with secrets)\
\
\uc0\u11835 \
\
\uc0\u55357 \u56615 Next Steps\
\'95 Paste in a real event HTML snippet if you want me to write exact cheerio selectors\
\'95 Want to output .ics or send it straight to Google Calendar?\
\'95 Want this wrapped as a Docker container or systemd service?\
\
You\'92re one command away from auto-watching your own platform.}