Skip to content

Instantly share code, notes, and snippets.

@ci7lus
Created August 13, 2020 07:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ci7lus/29efeac02148c2a68eaa4169cff5f924 to your computer and use it in GitHub Desktop.
Save ci7lus/29efeac02148c2a68eaa4169cff5f924 to your computer and use it in GitHub Desktop.
https://booth.pm の検索結果における新規商品を取得
import { defineComponent } from "ironpipe"
import axios from "axios"
import domino from "domino"
/**
* booth-search.ts
* MIT License (c) 2020 ci7lus
*/
module.exports = defineComponent({
name: "booth-search",
version: "0.0.1",
props: {
timer: {
type: "$.interface.timer",
default: {
intervalSeconds: 60 * 15,
},
},
searchUrl: {
type: "string",
label: "Search page url",
description:
"Booth's search page url (example: https://booth.pm/ja/browse/%E3%82%B9%E3%83%9E%E3%83%9B%E3%82%B1%E3%83%BC%E3%82%B9%E3%83%BB%E3%82%AB%E3%83%90%E3%83%BC?adult=only&new_arrival=true&sort=new )",
},
maxPage: {
type: "string",
description: "page to fetch (number)",
optional: true,
},
ng: {
type: "string",
label: "NG Shops",
description: "shops list to exclude (separate with ,)",
optional: true,
},
},
dedupe: "unique",
async run() {
const ng = this.ng?.split(",") || []
const url = new URL(this.searchUrl)
;[
["adult", "include"],
["new_arrival", "true"],
["sort", "new"],
].map(([k, v]) => {
if (!url.searchParams.has(k)) url.searchParams.set(k, v)
})
for (let page of [...Array(parseInt(this.maxPage || "2") || 2).keys()]
.reverse()
.map((n) => n + 1)) {
if (page && page !== 1) {
url.searchParams.set("page", page.toString())
} else {
url.searchParams.delete("page")
}
const r = await axios.get(url.href, {
headers: {
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36",
Cookie: "adult=t",
},
responseType: "text",
timeout: 5000,
})
if (r.status !== 200) {
console.error(`ステータスコードが${r.status}でした`)
return
}
try {
const document = domino.createDocument(r.data)
const container = document.querySelector("div.market-items")
if (!container) {
console.error("アイテムコンテナが見つかりませんでした")
continue
}
const itemsElement = container.querySelectorAll("li.item-card")
const items = Array.from(itemsElement)
.filter((item) => {
const brand = item.getAttribute("data-product-brand")
return !brand || !ng.includes(brand)
})
.map((item) => {
const brand = item.getAttribute("data-product-brand")
const itemTitleNode: HTMLLinkElement | null = item.querySelector(
"div.item-card__title a"
)
if (!itemTitleNode) {
throw new Error("div.item-card__title a")
}
const title = itemTitleNode.textContent!
const itemLink = itemTitleNode.href!
const id = item.getAttribute("data-product-id") || itemLink
const price = item.getAttribute("data-product-price")
const isAdult = !!item.querySelector("div.adult")
const shopImage =
(item.querySelector(
"div.item-card__shop-info a div img"
) as HTMLImageElement | null)?.src?.replace("48x48", "128x128") ||
null
const shopName =
item
.querySelector("div.item-card__shop-name")
?.textContent?.trim() || null
const eventName =
item.querySelector(".eventname-flag__name")?.textContent || null
const imagesNode = item.querySelector(
".item-card__thumbnail-images"
)
const images = Array.from(
imagesNode?.querySelectorAll(".item-card__thumbnail-image") || []
)
.map((imageNode) => {
return imageNode
.getAttribute("data-original")
?.replace("c/300x300_a2_g5/", "")
})
.filter((s) => typeof s === "string") as string[]
return {
title,
id,
link: itemLink,
isAdult,
eventName,
price,
image: images[0],
author: {
name: shopName,
link: brand && `http://${brand}.booth.pm`,
email: brand && `http://${brand}.booth.pm`,
image: shopImage,
},
}
})
items
.sort((a, b) => (a.id < b.id ? -1 : 1))
.map((item) => {
this.$emit(item, {
id: item.link,
summary: item.title,
})
})
} catch (error) {
console.error(error)
break
}
}
},
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment