UNPKG

rsshub

Version:
313 lines (310 loc) 11.2 kB
import { t as rofetch } from "./ofetch-C3ts-Hud.mjs"; import { t as cache_default } from "./cache-BkqOokyU.mjs"; import { load } from "cheerio"; import sanitizeHtml from "sanitize-html"; //#region lib/routes/kleinanzeigen/utils/get-product-page.ts /** * Parse item infos about a product page * @param url url of the product page * @returns */ const getProductPage = (url) => cache_default.tryGet(url, async () => { const $ = load(await rofetch(url)); const product = $("#viewad-product"); const sellerProfile = $("#viewad-profile-box"); const title = product.find("#viewad-title").find(".is-hidden").remove().end().text().trim(); const price = product.find(".boxedarticle--price").text().trim() + " " + product.find(".boxedarticle--details--shipping").text().trim(); const address = product.find("[itemprop=\"address\"]").text().trim(); const description = sanitizeHtml(product.find("[itemprop=\"description\"]").html() || "", { allowedTags: ["br"] }); const imagesHtml = product.find("#viewad-image").toArray().map((img) => ({ src: $(img).attr("src"), alt: $(img).attr("alt") })).filter((img) => img.src).map((img) => `<img src="${img.src}" alt="${img.alt}" />`).join("<br>"); const category = [...$(".breadcrump .breadcrump-link").toArray().slice(1).map((x) => $(x).text().trim()), product.find(".addetailslist--detail").filter((i, el) => $(el).text().includes("Art")).find(".addetailslist--detail--value").text().trim()].join(" > "); return { title, link: url, description: `${price}<br>${address}<br><br>${description}<br>${imagesHtml}<br>`, author: [{ name: sellerProfile.find(".userprofile-vip a").text().trim(), url: sellerProfile.find(".userprofile-vip a").attr("href") }], category: [category] }; }); //#endregion //#region lib/routes/kleinanzeigen/utils/parse-listing-page.ts /** * parse listing page to get product infos * @param $ CheerioAPI data * @returns */ const parseListingPage = ($) => Promise.all($("li.ad-listitem.fully-clickable-card").not(".badge-topad").toArray().map((item) => { return getProductPage(`https://www.kleinanzeigen.de${$(item).find("article").attr("data-href")}`); })); //#endregion //#region lib/routes/kleinanzeigen/utils/resolve-category.ts const category2Id = { "Alle Kategorien": 0, "Auto, Rad & Boot": 289, Autos: 216, "Autoteile & Reifen": 223, "Boote & Bootszubehör": 211, "Fahrräder & Zubehör": 217, "Motorräder & Motorroller": 305, "Motorradteile & Zubehör": 306, "Nutzfahrzeuge & Anhänger": 276, "Reparaturen & Dienstleistungen": 280, "Wohnwagen & -mobile": 220, "Weiteres Auto, Rad & Boot": 241, Dienstleistungen: 297, Altenpflege: 236, "Babysitter/-in & Kinderbetreuung": 237, Elektronik: 161, "Haus & Garten": 80, "Künstler/-in & Musiker/-in": 191, "Reise & Event": 294, "Tierbetreuung & Training": 133, "Umzug & Transport": 238, "Weitere Dienstleistungen": 298, "Eintrittskarten & Tickets": 231, "Bahn & ÖPNV": 286, "Comedy & Kabarett": 254, Gutscheine: 287, Kinder: 252, Konzerte: 255, Sport: 257, "Theater & Musical": 251, "Weitere Eintrittskarten & Tickets": 256, "Audio & Hifi": 172, "Dienstleistungen Elektronik": 226, Foto: 245, "Handy & Telefon": 173, Haushaltsgeräte: 176, Konsolen: 279, Notebooks: 278, PCs: 228, "PC-Zubehör & Software": 225, "Tablets & Reader": 285, "TV & Video": 175, Videospiele: 227, Wearables: 405, "Wearables Zubehör": 406, "Weitere Elektronik": 168, "Familie, Kind & Baby": 17, "Baby- & Kinderkleidung": 22, "Baby- & Kinderschuhe": 19, "Baby-Ausstattung": 258, "Babyschalen & Kindersitze": 21, "Kinderwagen & Buggys": 25, Kinderzimmermöbel: 20, Spielzeug: 23, "Weiteres Familie, Kind & Baby": 18, "Freizeit, Hobby & Nachbarschaft": 185, "Esoterik & Spirituelles": 265, "Essen & Trinken": 248, Freizeitaktivitäten: 187, "Handarbeit, Basteln & Kunsthandwerk": 282, "Kunst & Antiquitäten": 240, Modellbau: 249, "Reise & Eventservices": 233, Sammeln: 234, "Sport & Camping": 230, Trödel: 250, "Verloren & Gefunden": 189, "Weiteres Freizeit, Hobby & Nachbarschaft": 242, Badezimmer: 91, Büro: 93, Dekoration: 246, "Dienstleistungen Haus & Garten": 239, "Gartenzubehör & Pflanzen": 89, Heimtextilien: 90, Heimwerken: 84, "Küche & Esszimmer": 86, "Lampen & Licht": 82, Schlafzimmer: 81, Wohnzimmer: 88, "Weiteres Haus & Garten": 87, Haustiere: 130, Fische: 138, Hunde: 134, Katzen: 136, Kleintiere: 132, Nutztiere: 135, Pferde: 139, "Vermisste Tiere": 283, Vögel: 243, Zubehör: 313, Immobilien: 195, "Auf Zeit & WG": 199, Container: 402, Eigentumswohnungen: 196, "Ferien- & Auslandsimmobilien": 275, "Garagen & Stellplätze": 197, Gewerbeimmobilien: 277, "Grundstücke & Gärten": 207, "Häuser zum Kauf": 208, "Häuser zur Miete": 205, Mietwohnungen: 203, Neubauprojekte: 403, "Weitere Immobilien": 198, Jobs: 102, Ausbildung: 118, "Bau, Handwerk & Produktion": 111, "Büroarbeit & Verwaltung": 114, "Gastronomie & Tourismus": 110, "Kundenservice & Call Center": 105, "Mini- & Nebenjobs": 107, Praktika: 125, "Sozialer Sektor & Pflege": 123, "Transport, Logistik & Verkehr": 247, "Vertrieb, Einkauf & Verkauf": 117, "Weitere Jobs": 109, "Mode & Beauty": 153, "Beauty & Gesundheit": 269, Damenbekleidung: 154, Damenschuhe: 159, Herrenbekleidung: 160, Herrenschuhe: 158, "Taschen & Accessoires": 156, "Uhren & Schmuck": 157, "Weiteres Mode & Beauty": 155, "Musik, Filme & Bücher": 73, "Bücher & Zeitschriften": 76, "Büro & Schreibwaren": 281, Comics: 284, "Fachbücher, Schule & Studium": 77, "Film & DVD": 79, "Musik & CDs": 78, Musikinstrumente: 74, "Weitere Musik, Filme & Bücher": 75, Nachbarschaftshilfe: 401, "Unterricht & Kurse": 235, Computerkurse: 260, "Kochen & Backen": 263, "Kunst & Gestaltung": 264, "Musik & Gesang": 262, Nachhilfe: 268, Sportkurse: 261, Sprachkurse: 271, Tanzkurse: 267, Weiterbildung: 266, "Weitere Unterricht & Kurse ": 270, "Verschenken & Tauschen": 272, Tauschen: 273, Verleihen: 274, Verschenken: 192 }; /** * Resolve the Category string to categoryId * @param category: string category string as entered into the Kleinanzeigen search * @returns */ const resolveCategory = (category) => category2Id[category] || 0; //#endregion //#region lib/routes/kleinanzeigen/utils/resolve-location.ts /** * Resolve the Location string to locationId * @param location location string as entered into the Kleinanzeigen search * @returns */ const resolveLocation = async (location) => { const url = new URL("https://www.kleinanzeigen.de/s-ort-empfehlungen.json"); url.searchParams.append("query", location); const urlString = url.href; const res = await cache_default.tryGet(urlString, async () => await rofetch(urlString)); const locationEntry = Object.entries(res).findLast((x) => x[1] === location); if (!locationEntry) return null; return Number.parseInt(locationEntry[0].slice(1)); }; //#endregion //#region lib/routes/kleinanzeigen/utils/search.ts /** * parse listing page to get product infos * @param $ CheerioAPI data * @returns */ const search = async (opts) => { const url = new URL("https://www.kleinanzeigen.de/s-suchanfrage.html"); if (opts.categoryId) url.searchParams.append("categoryId", opts.categoryId.toString()); else if (opts.category) { const locId = resolveCategory(opts.category); url.searchParams.append("categoryId", locId?.toString() || ""); } else url.searchParams.append("categoryId", ""); if (opts.locationId) url.searchParams.append("locationId", opts.locationId.toString()); else if (opts.location) { const locId = await resolveLocation(opts.location); url.searchParams.append("locationId", locId?.toString() || ""); } else url.searchParams.append("locationId", ""); url.searchParams.append("keywords", opts.query || ""); url.searchParams.append("locationStr", ""); url.searchParams.append("radius", opts.radius?.toString() || ""); url.searchParams.append("sortingField", opts.sortingField || "SORTING_DATE"); url.searchParams.append("adType", ""); url.searchParams.append("posterType", ""); url.searchParams.append("maxPrice", opts.maxPrice?.toString() || ""); url.searchParams.append("minPrice", opts.minPrice?.toString() || ""); url.searchParams.append("buyNowEnabled", opts.minPrice ? "true" : "false"); url.searchParams.append("shippingCarrier", opts.shippingCarrier || ""); url.searchParams.append("shipping", ""); const items = await parseListingPage(load(await rofetch(url.href))); return { title: `Kleinanzeigen Offers: ${opts.query || opts.category} ${opts.minPrice || opts.maxPrice ? `${opts.minPrice || 0}€ - ${opts.maxPrice}` : ""}`, link: url.href, language: "de", item: items }; }; //#endregion //#region lib/routes/kleinanzeigen/search.ts const route = { path: "/search/:routeParams", categories: ["shopping"], example: "/kleinanzeigen/search/category=PCs&location=Berlin&radius=20", parameters: { routeParams: "Extra parameters, see the table below" }, description: `::: tip Parameter | Name | Description | Default | | --------------- | ------------------------------------------------------------------------- | ------------- | | query | Search Query | undefined | | category | Category (as named on Kleinanzeigen) | undefined | | categoryId | Category ID (advanced) | undefined | | location | Location (as named on Kleinanzeigen) | undefined | | locationId | Location ID (advanced) | undefined | | radius | Radius in KM around the Location | 0 | | sortingField | Order of the Products (SORTING\\_DATE, PRICE\\_AMOUNT, PRICE\\_AMOUNT\\_DESC) | SORTING\\_DATE | | minPrice | minimal Price | undefined | | maxPrice | maximal Price | undefined | | shippingCarrier | Shipping Carrier (e.g. DHL, HERMES) | undefined | :::`, features: { requireConfig: false, requirePuppeteer: false, antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false }, radar: [], name: "Search", maintainers: ["LunyaaDev"], handler }; function handler(ctx) { const { routeParams } = ctx.req.param(); const params = new URLSearchParams(routeParams); return search({ query: params.get("query") || void 0, category: params.get("category") || void 0, categoryId: params.get("categoryId") || void 0, location: params.get("location") || void 0, locationId: params.get("locationId") || void 0, radius: params.get("radius") || void 0, sortingField: params.get("sortingField") || void 0, minPrice: params.get("minPrice") || void 0, maxPrice: params.get("maxPrice") || void 0, shippingCarrier: params.get("shippingCarrier") || void 0 }); } //#endregion export { route };