From f817604858891edb79e26459dae884b158774db1 Mon Sep 17 00:00:00 2001 From: yyamashita Date: Wed, 6 May 2026 22:20:00 +0900 Subject: =?UTF-8?q?Add=204=20new=20venue=20scrapers:=20Meets=20=E5=A4=A7?= =?UTF-8?q?=E5=A1=9A,=20WARP=20=E5=90=89=E7=A5=A5=E5=AF=BA,=20FLAT=20?= =?UTF-8?q?=E8=A5=BF=E8=8D=BB=E7=AA=AA,=20Pitbar=20=E8=A5=BF=E8=8D=BB?= =?UTF-8?q?=E7=AA=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit meets-otsuka: rinky.info プラットフォーム。div.blog-entry.event-wrap[event-date] から日付・タイトル・出演者・時間・価格・チケットURLを取得。 warp-kichijoji: WordPress カスタムテーマ。

YYYY
MM

で 年月を取得、article.schedules-box から各イベントをパース。 flat-nishiogikubo: Wix サイトのため JS レンダリング必須。エラーを返す プレースホルダー実装(Playwright 等への移行が必要)。 pitbar-nishiogikubo: freecalend.com (mem25771) から取得を試みるが、 ボット遮断のため現状はエラー。URL パターン・代替策をコメントに記載。 SCRAPE_TARGETS.md に状態列(✅/⚠️)を追加。 Co-Authored-By: Claude Sonnet 4.6 --- app/scrapers/meets-otsuka.ts | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 app/scrapers/meets-otsuka.ts (limited to 'app/scrapers/meets-otsuka.ts') diff --git a/app/scrapers/meets-otsuka.ts b/app/scrapers/meets-otsuka.ts new file mode 100644 index 0000000..57cf120 --- /dev/null +++ b/app/scrapers/meets-otsuka.ts @@ -0,0 +1,79 @@ +/** + * 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: "大塚", +}; + +export const scraper: Scraper = { + venue, + async scrape(): Promise { + 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; + }, +}; -- cgit v1.2.3