summaryrefslogtreecommitdiff
path: root/site
diff options
context:
space:
mode:
Diffstat (limited to 'site')
-rw-r--r--site/artoftea.fnl78
-rw-r--r--site/ipuer.fnl81
-rw-r--r--site/ozchai.fnl70
3 files changed, 229 insertions, 0 deletions
diff --git a/site/artoftea.fnl b/site/artoftea.fnl
new file mode 100644
index 0000000..382b0ef
--- /dev/null
+++ b/site/artoftea.fnl
@@ -0,0 +1,78 @@
+(import-macros {: map} :lib.macro)
+
+(local peg
+ (if (pick-values 1 (pcall require :lpeg))
+ (require :lpeg)
+ (require :vendor.lpeglj)))
+(local parser (require :parser))
+(local number (require :lib.number))
+(local fetcher (require :fetcher))
+
+(fn url-formatter [path page]
+ (.. "https://artoftea.ru/" path "/?page=" page))
+
+(local product-peg
+ (* ;; id
+ (parser.anywhere
+ (parser.tag :div {:class "front-image"}
+ (parser.tag :a {:href (peg.Cg (parser.till "\"") :url)}
+ (parser.tag :img {:src (peg.Cg (parser.till "\"") :image)
+ :title "*" :class "*" :alt "*"}))))
+ (parser.anywhere
+ (parser.tag :div {:class "name"}
+ (parser.tag :a {:href "*"} (peg.Cg (parser.till "</a>") :title))))
+ (parser.anywhere
+ (parser.tag :p {:class "description"}
+ (peg.Cg (parser.till "</p>") :description)))
+ (+
+ (*
+ (parser.anywhere
+ (parser.tag :option {:value "*" :selected "selected"}
+ (* (peg.Cg parser.pegs.number :weight) " гр" parser.pegs.spaces)))
+ (parser.anywhere
+ (parser.tag :p {:class "price"}
+ (parser.tag :span {:id "*"}
+ (peg.Cg (parser.till "</span>") :price)))))
+ (parser.anywhere
+ (parser.tag :p {:class "price"}
+ (parser.tag :span {:id "*"}
+ (peg.Cg (parser.till "</span>") :price)))))
+ (parser.anywhere
+ (parser.tag :input {:type "hidden"
+ :name "product_id"
+ :value (peg.Cg parser.pegs.number :id)}))
+ (parser.anywhere
+ (parser.tag :button {:type "*" :onclick "*" :class "*"} "Купить"))))
+
+(fn normalize [product]
+ (local year
+ (number.string->number
+ (: (parser.anywhere
+ (* (peg.C (^ (peg.R "09") 4))
+ (parser.maybe " ")
+ (- "г" (peg.P "гр"))))
+ :match product.title)))
+ (local weight (number.string->number product.weight))
+ (local price (number.string->number product.price))
+
+ {:site "artoftea"
+ :id product.id
+ :url product.url
+ :description product.description
+ :image product.image
+ :year year
+ :price price
+ :weight weight
+ :price-per (if (and price weight (< 0 weight))
+ (/ (math.ceil (* (/ price weight) 10)) 10)
+ nil)})
+
+(fn products []
+ (fetcher.from-html
+ url-formatter
+ [{:path "redtea" :category "Красный чай"}
+ {:path "greentea" :category "Зеленый чай"}]
+ normalize
+ product-peg))
+
+{: products}
diff --git a/site/ipuer.fnl b/site/ipuer.fnl
new file mode 100644
index 0000000..f878912
--- /dev/null
+++ b/site/ipuer.fnl
@@ -0,0 +1,81 @@
+(import-macros {: map} :lib.macro)
+
+(local peg
+ (if (pick-values 1 (pcall require :lpeg))
+ (require :lpeg)
+ (require :vendor.lpeglj)))
+(local number (require :lib.number))
+(local parser (require :parser))
+(local fetcher (require :fetcher))
+
+(fn url-formatter [path page]
+ (.. "https://ipuer.ru/catalog/" path "/?p=" page))
+
+(local product-peg
+ (* ;; id
+ (parser.anywhere
+ (parser.tag :div
+ {:data-id (peg.Cg parser.pegs.number :id) :class "*"}))
+ ;; url and image
+ (parser.anywhere
+ (parser.tag :a {:href (peg.Cg (parser.till "\"") :url)}
+ (parser.tag :img {:src (peg.Cg (parser.till "\"") :image) :alt "*"})))
+ ;; title
+ (parser.anywhere
+ (parser.tag :div {:class "card-product_title"}
+ (parser.tag :a {:href "*"}
+ (parser.tag :span {} (peg.Cg (parser.till "</span>") :title)))))
+ ;; price
+ (parser.anywhere
+ (parser.tag :span {:class "card-price"}
+ (* (peg.Cg
+ (* parser.pegs.number
+ (parser.maybe (* " " parser.pegs.number)))
+ :price)
+ " р.")))
+ (parser.anywhere
+ (+ (parser.tag :a {:data-url "*" :class "*" :data-add-text "*"} "В корзину")
+ (parser.tag :a {:data-url "*" :class "*"} "В корзину")))))
+
+(fn normalize [product]
+ (local year
+ (number.string->number
+ (: (parser.anywhere
+ (* (peg.C (^ (peg.R "09") 4))
+ (parser.maybe " ")
+ (- "г" (peg.P "гр"))))
+ :match product.title)))
+ (local weight
+ (number.string->number
+ (: (parser.anywhere (* (peg.C parser.pegs.number) (parser.maybe " ") "гр"))
+ :match product.title)))
+ (local price (number.string->number product.price))
+
+ {:site "ipuer"
+ :id product.id
+ :url (.. "https://ipuer.ru" product.url)
+ :title product.title
+ :description ""
+ ;; FIXME: parse all editions into different projects
+ :image (.. "https://ipuer.ru" product.image)
+ :year year
+ :price price
+ :weight weight
+ :category product.category
+ :price-per (if (and price weight (< 0 weight))
+ (/ (math.ceil (* (/ price weight) 10)) 10)
+ nil)})
+
+(fn products []
+ (fetcher.from-html
+ url-formatter
+ [{:path "shen-puer" :category "Шен пуэр"}
+ {:path "shu-puer" :category "Шу пуэр"}
+ {:path "drugoy-chay"}
+ {:path "blagovoniya" :category "Благовония"}
+ {:path "posuda" :category "Посуда"}
+ {:path "282" :category "Посуда"}]
+ normalize
+ product-peg))
+
+{: products}
diff --git a/site/ozchai.fnl b/site/ozchai.fnl
new file mode 100644
index 0000000..90c4edc
--- /dev/null
+++ b/site/ozchai.fnl
@@ -0,0 +1,70 @@
+(import-macros {: map} :lib.macro)
+
+(local http (require :http))
+(local array (require :lib.array))
+(local json (require :vendor.json))
+
+(local %all-products-partuid 176163172341)
+
+(fn string->number [str]
+ (if str
+ (tonumber (pick-values 1 (str:gsub "[^0-9.]" "")))
+ nil))
+
+(fn request [partuid slice]
+ (print (.. "https://store.tildaapi.com/api/getproductslist/"
+ "?storepartuid="
+ partuid
+ "&recid=280779251&c=1723216515077"
+ "&getparts=true&getoptions=true&slice=%d&size=36"))
+ (let [(status headers body)
+ (luna.http.request
+ "GET"
+ (string.format
+ (.. "https://store.tildaapi.com/api/getproductslist/"
+ "?storepartuid="
+ partuid
+ "&recid=280779251&c=1723216515077"
+ "&getparts=true&getoptions=true&slice=%d&size=36")
+ slice)
+ {:Content-Type "application/json"
+ :User-Agent (http.random-user-agent)}
+ "")]
+ (json.decode body)))
+
+(fn walk-slices [partuid]
+ (fn gather [slice knil]
+ (let [{: nextslice : products} (request partuid slice)
+ res (array.concat knil products)]
+ (if (= 0 (# products))
+ knil
+ (do
+ (os.execute "sleep 1")
+ (gather (+ slice 1) res)))))
+ (gather 1 []))
+
+(fn normalize [_ product]
+ (local gallery (json.decode product.gallery))
+ (local weight (string->number (. (. product.editions 1) :Вес)))
+ (local price (string->number (. (. product.editions 1) :price)))
+
+ {:site "ozchai"
+ :id product.url
+ :url product.url
+ :title product.title
+ :description product.descr
+ ;; FIXME: parse all editions into different projects
+ :image (if (< 0 (# gallery))
+ (. (. gallery 1) :img)
+ "")
+ :weight weight
+ :price price
+ :price-per (if (and price weight (< 0 weight))
+ (/ (math.ceil (* (/ price weight) 10)) 10)
+ nil)
+ :characteristics product.characteristics})
+
+(fn products []
+ (map normalize (walk-slices %all-products-partuid)))
+
+{: products}