/** * Meets 大塚 — https://meets.rinky.info/events * * rinky.info プラットフォーム。イベントは以下の構造: *
*

タイトル

*

アーティスト

*

OPEN 18:30 / START 19:00

* 価格 *
*/ import * as cheerio from "cheerio"; import type { Scraper, VenueMeta } from "./base"; import type { EventInput } from "~/lib/db.server"; export const venue: VenueMeta = { id: "meets-otsuka", name: "Meets 大塚", url: "https://meets.rinky.info", area: "大塚", capacity: 100, }; function parseHtml(html: string): EventInput[] { const $ = cheerio.load(html); const events: EventInput[] = []; $("div.blog-entry.event-wrap").each((_, el) => { const $el = $(el); const date = $el.attr("event-date") ?? ""; if (!date.match(/^\d{4}-\d{2}-\d{2}$/)) return; const $link = $el.find("h2 a").first(); const title = $link.text().trim(); if (!title) return; const detailPath = $link.attr("href") ?? null; const sourceUrl = detailPath ? `${venue.url}${detailPath}` : null; const artist = $el.find("p.act span").map((_, s) => $(s).text().trim()).get().join("、") || null; const timeText = $el.find("p.time").first().text(); const openMatch = timeText.match(/OPEN\s*(\d{2}:\d{2})/i); const startMatch = timeText.match(/START\s*(\d{2}:\d{2})/i); const price = $el.find("span.ticket-price__label").first().text().trim() || null; const bgStyle = $el.find("div.image-bg").attr("style") ?? ""; const imgMatch = bgStyle.match(/url\(["']?([^"')]+)["']?\)/); const imageUrl = imgMatch?.[1] ?? null; const ticketUrl = $el.find("a[href*='livepocket'], a[href*='eplus'], a[href*='pia'], a[href*='ticket']") .first().attr("href") ?? null; events.push({ venue_id: venue.id, title, artist, date, open_time: openMatch?.[1] ?? null, start_time: startMatch?.[1] ?? null, price, ticket_url: ticketUrl, image_url: imageUrl, source_url: sourceUrl, }); }); return events; } export const scraper: Scraper = { venue, async scrape(): Promise { const now = new Date(); const urls = [0, 1, 2].map((offset) => { const d = new Date(now.getFullYear(), now.getMonth() + offset, 1); const ym = `${d.getFullYear()}/${String(d.getMonth() + 1).padStart(2, "0")}`; return `https://meets.rinky.info/events?date=${encodeURIComponent(ym)}`; }); const htmls = await Promise.all( urls.map((url) => fetch(url).then((r) => (r.ok ? r.text() : ""))) ); const seen = new Set(); return htmls.flatMap(parseHtml).filter((e) => { const key = `${e.date}|${e.title}`; if (seen.has(key)) return false; seen.add(key); return true; }); }, };