summaryrefslogtreecommitdiff
path: root/app/scrapers/meets-otsuka.ts
blob: 57cf120c5d726093f451504cf16c08821305327d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
 * Meets 大塚 — https://meets.rinky.info/events
 *
 * rinky.info プラットフォーム。イベントは以下の構造:
 *   <div class="blog-entry event-wrap" event-date="YYYY-MM-DD">
 *     <h2><a href="/events/ID">タイトル</a></h2>
 *     <p class="act"><span>アーティスト</span></p>
 *     <p class="time">OPEN 18:30 / START 19:00</p>
 *     <span class="ticket-price__label">価格</span>
 *     <div class="image-bg" style="background-image: url(...)">
 */
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: "大塚",
};

export const scraper: Scraper = {
  venue,
  async scrape(): Promise<EventInput[]> {
    const res = await fetch("https://meets.rinky.info/events");
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const $ = cheerio.load(await res.text());
    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;

      // background-image: url("...")
      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;
  },
};