![r2-bike (r2-bike.com) scraper avatar](https://images.apifyusercontent.com/75G8Enah1yxvz50RTTkWwmTeiLp6jdHg1YKF3WVnsqg/rs:fill:92:92/aHR0cHM6Ly9pLmltZ3VyLmNvbS9GWUJ1Rm9nLnBuZw.webp)
r2-bike (r2-bike.com) scraper
DeprecatedView all Actors![r2-bike (r2-bike.com) scraper](https://images.apifyusercontent.com/75G8Enah1yxvz50RTTkWwmTeiLp6jdHg1YKF3WVnsqg/rs:fill:92:92/aHR0cHM6Ly9pLmltZ3VyLmNvbS9GWUJ1Rm9nLnBuZw.webp)
![](data:image/png;base64,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)
This Actor is deprecated
This Actor is unavailable because the developer has decided to deprecate it. Would you like to try a similar Actor instead?
See alternative Actors![r2-bike (r2-bike.com) scraper](https://images.apifyusercontent.com/75G8Enah1yxvz50RTTkWwmTeiLp6jdHg1YKF3WVnsqg/rs:fill:92:92/aHR0cHM6Ly9pLmltZ3VyLmNvbS9GWUJ1Rm9nLnBuZw.webp)
r2-bike (r2-bike.com) scraper
strajk/r2-bike-r2-bike-com-scraper
Scrapes products titles, prices, images and availability. Does NOT scrape product details. Uses Crawlee (Apify v3).
Dockerfile
1FROM apify/actor-node-playwright-firefox:16
2
3COPY package.json ./
4
5RUN npm --quiet set progress=false \
6 && npm install aws-crt \
7 && npm install --only=prod --no-optional
8
9COPY . ./
INPUT_SCHEMA.json
1{
2 "title": "r2-bike (r2-bike.com) scraper",
3 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details. Uses Crawlee (Apify v3).",
4 "type": "object",
5 "schemaVersion": 1,
6 "properties": {
7 "mode": {
8 "title": "Mode",
9 "description": "",
10 "type": "string",
11 "editor": "select",
12 "default": "TEST",
13 "prefill": "TEST",
14 "enum": [
15 "TEST",
16 "FULL"
17 ],
18 "enumTitles": [
19 "TEST",
20 "FULL"
21 ]
22 },
23 "APIFY_USE_MEMORY_REQUEST_QUEUE": {
24 "sectionCaption": "Advanced",
25 "sectionDescription": "Advanced options, use only if you know what you're doing.",
26 "title": "Use in-memory request queue instead of the native one",
27 "description": "In-memory request queue can reduce costs, but it may case issues with longer runs due to non-persistence.",
28 "type": "boolean",
29 "default": false,
30 "editor": "checkbox"
31 },
32 "APIFY_DONT_STORE_IN_DATASET": {
33 "title": "Don't store in dataset",
34 "description": "If set to true, the actor will not store the results in the default dataset. Useful when using alternative storage, like own database",
35 "type": "boolean",
36 "default": false,
37 "editor": "checkbox"
38 },
39 "PG_CONNECTION_STRING_NORMALIZED": {
40 "title": "Postgres connection string for normalized data",
41 "description": "If set, actor will store normalized data in Postgres database in PG_DATA_TABLE and PG_DATA_PRICE_TABLE tables",
42 "type": "string",
43 "editor": "textfield"
44 },
45 "PG_DATA_TABLE": {
46 "title": "Postgres table name for product data",
47 "description": "Table name for storing product name, url, image, ...",
48 "type": "string",
49 "editor": "textfield"
50 },
51 "PG_DATA_PRICE_TABLE": {
52 "title": "Postgres table name for price data",
53 "description": "Table name for storing price, original price, stock status, ...",
54 "type": "string",
55 "editor": "textfield"
56 }
57 },
58 "required": [
59 "mode"
60 ]
61}
apify.json
1{
2 "name": "r2-bike-r2-bike-com-scraper",
3 "version": "0.1",
4 "buildTag": "latest",
5 "env": null,
6 "defaultRunOptions": {
7 "build": "latest",
8 "timeoutSecs": 3600,
9 "memoryMbytes": 4096
10 }
11}
main.js
1import { Actor } from "apify3";
2import {
3 CheerioCrawler,
4 createCheerioRouter,
5 utils as crawleeUtils,
6} from "crawlee";
7import { Session } from "@crawlee/core";
8import playwright from "playwright";
9import { init, parsePrice, save, toNumberOrNull } from "./_utils/common.js";
10
11const LABELS = {
12 INDEX: `INDEX`,
13 PRODUCTS: `PRODUCTS`,
14};
15
16var MODE;
17
18(function (MODE) {
19 MODE["TEST"] = "TEST";
20 MODE["FULL"] = "FULL";
21})(MODE || (MODE = {}));
22
23async function enqueueInitial(mode, crawler) {
24 if (mode === MODE.FULL) {
25 await crawler.addRequests([
26 {
27 userData: { label: LABELS.INDEX },
28 url: `https://r2-bike.com/en/brands`,
29 },
30 ]);
31 } else if (mode === MODE.TEST) {
32 await crawler.addRequests([
33 {
34 userData: { label: LABELS.PRODUCTS },
35 url: `https://r2-bike.com/en/shimano`,
36 },
37 ]);
38 }
39}
40
41const router = createCheerioRouter();
42
43router.addHandler(LABELS.INDEX, async ({ enqueueLinks }) => {
44 await enqueueLinks({
45 selector: `.vendor-index-group-wrapper li a`, // e.g. `en/shimano`
46 baseUrl: `https://r2-bike.com/`, // needed for correctly absolute URLs, otherwise it would be `https://r2-bike.com/en/en/shimano`, not sure why ¯\_(ツ)_/¯
47 userData: { label: LABELS.PRODUCTS },
48 });
49});
50
51router.addHandler(LABELS.PRODUCTS, async ({ crawler, $, request, log }) => {
52 log.info(`[PRODUCTS] ${request.url}`);
53
54 if (!request.url.match(/_s(\d+)$/)) {
55 // on first page
56 const paginationText = $(`.list-pageinfo .page-current`).text().trim(); // eg. `Page 1 of 11`
57 const match = paginationText.match(/(\d+) of (\d+)/);
58 if (!match)
59 log.error(
60 `[PRODUCTS] Failed to parse pagination text: ${paginationText}`
61 );
62 const [, currentPage, totalPages] = match ?? [];
63 if (Number(totalPages) > 1)
64 log.info(`[PRODUCTS] Found ${totalPages} pages, enqueuing`);
65 for (let i = 2; i <= Number(totalPages); i++) {
66 // skip first page, that is already handled
67 void crawler.addRequests([
68 {
69 url: `${request.url}_s${i}`, // eg. https://r2-bike.com/en/shimano_s2
70 userData: { label: LABELS.PRODUCTS },
71 },
72 ]);
73 }
74 }
75
76 const products = [];
77 const $products = $(
78 `#product-list .product-wrapper[itemprop="itemListElement"]`
79 ); // itemprop to avoid selecting last fake tile, which is actually "Next page" link
80 log.info(`[PRODUCTS] ${request.url} - found ${$products.length} products`);
81 $products.each(async (i, el) => {
82 const pid = $(`.product-cell`, el)
83 .attr(`id`) // result-wrapper_buy_form_106016
84 ?.replace(`result-wrapper_buy_form_`, ``); // 106016
85 if (!pid)
86 return log.error(
87 `[PRODUCTS] Failed to parse pid from ${i + 1}th product on ${
88 request.url
89 }`
90 );
91
92 const url = $(`meta[itemprop="url"]`, el).attr(`content`);
93 const img = $(`meta[itemprop="image"]`, el).attr(`content`);
94 const name = $(`h4[itemprop="name"]`, el).text().trim();
95
96 const priceRaw = $(`.price_wrapper .price`, el).text().trim(); // e.g. 1,98 €*
97 const price = parsePrice(priceRaw).amount;
98
99 const priceOrigRaw = $(`.price-uvp`, el).text().trim(); // e.g. MSRP: 5,95 €
100 const priceOrig = parsePrice(priceOrigRaw).amount;
101
102 const inStock = $(`.delivery-status`, el).text().includes(`available`);
103
104 const product = {
105 pid,
106 name,
107 url,
108 img,
109 inStock,
110 currentPrice: toNumberOrNull(price),
111 originalPrice: toNumberOrNull(priceOrig),
112 currency: `EUR`,
113 };
114 products.push(product);
115 });
116 await save(products);
117});
118
119void Actor.main(async () => {
120 const input = await Actor.getInput();
121 const { mode = MODE.FULL, ...rest } = input ?? {};
122 await init({ actorNameOverride: `r2-bike-com` }, rest);
123 const crawler = new CheerioCrawler({
124 requestHandler: router,
125 preNavigationHooks: [
126 async ({ session }, gotOptions) => {
127 const userData = session.userData;
128 gotOptions.headers = userData.headers; // real-like headers obtained from Firefox
129 gotOptions.headers.Cookie = userData.cookies
130 .map((c) => `${c.name}=${c.value}`)
131 .join(`; `); // real cookies obtained from Firefox
132 // gotOptions.proxyUrl = `http://127.0.0.1:9090` // NOTE: uncomment for local debugging
133 },
134 ],
135 maxConcurrency: 1, // not brave enough for concurrency
136 maxRequestRetries: 0, // not brave enough for concurrency
137 sessionPoolOptions: {
138 maxPoolSize: 1, // not brave enough for concurrency
139 sessionOptions: {
140 maxAgeSecs: 60 * 60 * 2, // 2 hours, default is 50m
141 maxUsageCount: 1000, // default is 50, let's use as much as possible, until we get blocked
142 },
143 createSessionFunction: async (sessionPool) => {
144 console.log(
145 `[SESSION] Creating new session, will use Firefox to unblock (should take ~10s)`
146 );
147 const session = new Session({ sessionPool });
148 await unblock(session);
149 return session;
150 },
151 },
152 });
153 await enqueueInitial(mode, crawler);
154 await crawler.run();
155});
156
157async function unblock(session) {
158 const browser = await playwright.firefox.launch({
159 // headless: false, // NOTE: uncomment for debugging
160 // proxy: { server: `http://127.0.0.1:9090` }, // NOTE: uncomment for local debugging
161 });
162 const browserContext = await browser.newContext({ ignoreHTTPSErrors: true });
163 await browserContext.addCookies([
164 {
165 name: `eu_cookie_store`,
166 value: `{"b209404849c0357500f7a82a6899961a":true,"3940b498c8a17157f69d757a80ff3421":true,"1d3c65b2b03ef35e14df6b163ea3a1f6":false,"0a3fbfc21a86a28c8961999929c374f3":true,"9b88c95a15e018c3f8038a7d0160145c":true,"dd31d974a78cdd704acaa6bf15da506c":true,"d86cf69a8b82547a94ca3f6a307cf9a6":false,"d323dff6f7de41c0b9af4c35e21dc032":false,"b83d1ac867f35569c614e298f645fffe":true,"21affb15e1316adac24b26db8e421a9d":false,"2d1fc55f933c039b2e04ff9034134b4d":true,"4d60ab2c6d11d753267484006c23e54c":false,"970cfba66b8380fb97b742e4571356c6":false}`,
167 domain: `r2-bike.com`,
168 path: `/`,
169 },
170 {
171 name: `r2_user_delivery_country`,
172 value: `CZ`, // TODO: make it configurable
173 domain: `r2-bike.com`,
174 path: `/`,
175 },
176 {
177 name: `r2_user_delivery_country_ip_backup`,
178 value: `CZ`, // TODO: make it configurable
179 domain: `r2-bike.com`,
180 path: `/`,
181 },
182 {
183 name: `r2_user_delivery_country_tax_1`,
184 value: `21`, // TODO: make it configurable
185 domain: `r2-bike.com`,
186 path: `/`,
187 },
188 {
189 name: `r2_user_delivery_country_tax_2`,
190 value: `10`, // TODO: make it configurable
191 domain: `r2-bike.com`,
192 path: `/`,
193 },
194 {
195 name: `ledgerCurrency`,
196 value: `EUR`,
197 domain: `r2-bike.com`,
198 path: `/`,
199 },
200 ]);
201
202 const page = await browserContext.newPage();
203 // page.on(`console`, msg => console.log(`⚪️ Playwright log (${msg.type()}) ${msg.text()}`))
204
205 let headersToSet;
206
207 await page.route(`**/*`, (route) => {
208 const request = route.request();
209 const url = request.url();
210 const method = request.method(); // GET, POST, etc.
211 const resourceType = request.resourceType(); // document, stylesheet, image, ...
212 // console.log(`🔵 Playwright route: ${method} ${url} (${resourceType})`)
213
214 // use the first main request to store the sent headers
215 if (!headersToSet) headersToSet = pickHeaders(request.headers());
216
217 route.continue();
218 });
219
220 // Go to product listing page which sets 95 products per page to current session
221 await page.goto(`https://r2-bike.com/navi.php?h=58&Sortierung=1&af=95`); // h=58 is "Shimano", Sortierung=1 is "Sort by name" – both are not important, but some values need to be set
222 // Wait for some time to pass basic Cloudflare Javascript checks
223 await crawleeUtils.sleep(5000); // TODO: Be smarter, 3000s is enough for r2-bike.com, but not for g2.com
224 // Get all cookies and store them for subsequent requests
225 const cookies = await page.context().cookies();
226 session.userData = { headers: headersToSet, cookies };
227}
228
229function pickHeaders(headers) {
230 // Pick just the headers that gotScraping can correctly handle (= order)
231 // This seems to be needed mainly to avoid setting Host header, which when set, was at the end of the headers list, which Cloudflare did not like
232 // If we skip the Host header, then gotScraping will set it automatically, and in the correct order
233
234 // taken from https://github.com/apify/header-generator/blob/1b0fd217b6fa0beaf42b9de321e47ac5f1d4cebf/src/data_files/headers-order.json#L62
235 const headersList = [
236 `sec-ch-ua`,
237 `sec-ch-ua-mobile`,
238 `user-agent`,
239 `User-Agent`,
240 `accept`,
241 `Accept`,
242 `accept-language`,
243 `Accept-Language`,
244 `accept-encoding`,
245 `Accept-Encoding`,
246 `dnt`,
247 `DNT`,
248 `referer`,
249 `Referer`,
250 `cookie`,
251 `Cookie`,
252 `Connection`,
253 `upgrade-insecure-requests`,
254 `Upgrade-Insecure-Requests`,
255 `te`,
256 `sec-fetch-site`,
257 `sec-fetch-mode`,
258 `sec-fetch-user`,
259 `sec-fetch-dest`,
260 `Sec-Fetch-Mode`,
261 `Sec-Fetch-Dest`,
262 `Sec-Fetch-Site`,
263 `Sec-Fetch-User`,
264 ];
265 return headersList.reduce((acc, header) => {
266 if (headers[header]) acc[header] = headers[header];
267 return acc;
268 }, {});
269}
package.json
1{
2 "name": "r2-bike-r2-bike-com-scraper",
3 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details. Uses Crawlee (Apify v3).",
4 "type": "module",
5 "scripts": {
6 "start": "node ./main.js",
7 "push-to-apify-platform": "npx apify push"
8 },
9 "dependencies": {
10 "apify3": "npm:apify@^3.0.2",
11 "crawlee": "*",
12 "@crawlee/core": "*",
13 "playwright": "*",
14 "pg": "*",
15 "pg-connection-string": "*",
16 "dotenv": "*",
17 "find-config": "*",
18 "@elastic/elasticsearch": "*",
19 "filenamify": "*",
20 "@crawlee/memory-storage": "*"
21 },
22 "apify": {
23 "title": "r2-bike (r2-bike.com) scraper",
24 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details. Uses Crawlee (Apify v3).",
25 "isPublic": true,
26 "isDeprecated": false,
27 "isAnonymouslyRunnable": true,
28 "notice": "",
29 "pictureUrl": "",
30 "seoTitle": "",
31 "seoDescription": "",
32 "categories": [
33 "ECOMMERCE"
34 ]
35 }
36}
.actor/actor.json
1{
2 "actorSpecification": 1,
3 "name": "r2-bike-r2-bike-com-scraper",
4 "title": "r2-bike (r2-bike.com) scraper",
5 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details. Uses Crawlee (Apify v3).",
6 "version": "0.1.0",
7 "storages": {
8 "dataset": {
9 "actorSpecification": 1,
10 "title": "r2-bike (r2-bike.com) scraper",
11 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details. Uses Crawlee (Apify v3).",
12 "views": {
13 "overview": {
14 "title": "Overview",
15 "description": "Overview of the most important fields",
16 "transformation": {
17 "fields": [
18 "pid",
19 "name",
20 "url",
21 "img",
22 "inStock",
23 "currentPrice",
24 "originalPrice",
25 "currency"
26 ]
27 },
28 "display": {
29 "component": "table",
30 "columns": [
31 {
32 "label": "Pid",
33 "field": "pid",
34 "format": "text"
35 },
36 {
37 "label": "Name",
38 "field": "name",
39 "format": "text"
40 },
41 {
42 "label": "Url",
43 "field": "url",
44 "format": "link"
45 },
46 {
47 "label": "Img",
48 "field": "img",
49 "format": "image"
50 },
51 {
52 "label": "In Stock",
53 "field": "inStock",
54 "format": "boolean"
55 },
56 {
57 "label": "Current Price",
58 "field": "currentPrice",
59 "format": "number"
60 },
61 {
62 "label": "Original Price",
63 "field": "originalPrice",
64 "format": "number"
65 },
66 {
67 "label": "Currency",
68 "field": "currency",
69 "format": "text"
70 }
71 ]
72 }
73 }
74 }
75 }
76 }
77}
.actor/logo.png
_utils/common.js
1import { createHash } from 'crypto'
2import os from "os"
3import path from "path"
4// eslint-disable-next-line @apify/apify-actor/no-forbidden-node-internals
5import fs from "fs"
6import pg from "pg"
7import pgConnectionString from 'pg-connection-string'
8import { config } from 'dotenv'
9import findConfig from "find-config"
10import { Client as ElasticClient } from "@elastic/elasticsearch"
11import filenamify from 'filenamify'
12import { Configuration, Dataset } from 'crawlee'
13import { MemoryStorage } from '@crawlee/memory-storage'
14
15config({ path: findConfig(`.env`) })
16
17const elasticIndexName = `actors-monorepo-shops`
18
19const globalLogsProps = {
20 __NODE_STARTED: new Date().toISOString(),
21}
22
23let actorName
24let pgClient
25let pgClientNormalized
26let elasticClient
27export async function init ({ actorNameOverride }, restInput) {
28 parseEnvFromInput(restInput)
29
30 if (os.platform() === `darwin`) {
31 const filePath = process.argv[1] // ~/Projects/apify-actors-monorepo/actors/foo.ts
32 const basename = path.basename(filePath) // foo.ts
33 actorName = actorNameOverride ?? basename.split(`.`)[0] // foo
34 const gitBranch = fs.readFileSync(path.join(process.cwd(), `..`, `.git/HEAD`), `utf8`)
35 .split(` `)[1]
36 .trim()
37 .replace(`refs/heads/`, ``)
38 const gitCommit = fs.readFileSync(path.join(process.cwd(), `..`, `.git/refs/heads/${gitBranch}`), `utf8`)
39 const gitCommitShort = gitCommit.substring(0, 7)
40 globalLogsProps.__GIT_COMMIT = gitCommitShort
41 }
42
43 if (process.env.APIFY_USE_MEMORY_REQUEST_QUEUE === `true`) { // dotenv -> bool-like vars are strings
44 Configuration.getGlobalConfig().useStorageClient(new MemoryStorage())
45 }
46
47 if (process.env.APIFY_IS_AT_HOME) {
48 actorName = actorNameOverride ?? process.env.APIFY_ACTOR_ID // Name would be better, but it's not in ENV
49 }
50
51 /* ELASTIC */
52 /* ======= */
53 if (process.env.ELASTIC_CLOUD_ID) {
54 elasticClient = new ElasticClient({
55 cloud: { id: process.env.ELASTIC_CLOUD_ID },
56 auth: { apiKey: process.env.ELASTIC_CLOUD_API_KEY },
57 })
58
59 // const mapping = await elasticClient.indices.getMapping({ index: actorName })
60
61 // eslint-disable-next-line no-inner-declarations
62 async function enforceIndexMapping () {
63 const doesIndexExist = await elasticClient.indices.exists({ index: elasticIndexName })
64 if (!doesIndexExist) await elasticClient.indices.create({ index: elasticIndexName })
65 await elasticClient.indices.putMapping({
66 index: elasticIndexName,
67 body: {
68 properties: {
69 _discount: { type: `float` },
70 originalPrice: { type: `float` },
71 currentPrice: { type: `float` },
72 },
73 },
74 })
75 }
76
77 try {
78 await enforceIndexMapping()
79 } catch (err) {
80 if (err.message.includes(`cannot be changed from type`)) {
81 console.log(`Elastic index ${elasticIndexName} already exists with incorrect mappings. As existing mapping cannot be changed, index will be deleted and recreated.`)
82 await elasticClient.indices.delete({ index: elasticIndexName })
83 await enforceIndexMapping()
84 }
85 }
86 }
87
88 /* POSTGRESQL */
89 /* ========== */
90 if (process.env.PG_CONNECTION_STRING) {
91 const pgConfig = pgConnectionString(process.env.PG_CONNECTION_STRING)
92 // const pgPool = new pg.Pool(pgConfig)
93
94 pgClient = new pg.Client(pgConfig)
95 await pgClient.connect()
96
97 // Check if table exists and have proper columns
98 const { rows: tables } = await pgClient.query(`
99 SELECT table_name
100 FROM information_schema.tables
101 WHERE table_schema = 'public'
102 `)
103
104 // eslint-disable-next-line camelcase
105 const tableExists = tables.some(({ table_name }) => table_name === process.env.PG_DATA_TABLE)
106 if (!tableExists) {
107 throw new Error(`Table ${process.env.PG_DATA_TABLE} does not exist in database ${pgConfig.database}`)
108 }
109
110 // TODO: Handle pgClient closing
111 }
112
113 if (process.env.PG_CONNECTION_STRING_NORMALIZED) {
114 const pgConfig = pgConnectionString(process.env.PG_CONNECTION_STRING_NORMALIZED)
115
116 pgClientNormalized = new pg.Client(pgConfig)
117 await pgClientNormalized.connect()
118
119 // Check if table exists and have proper columns
120 const { rows: tables } = await pgClientNormalized.query(`
121 SELECT table_name
122 FROM information_schema.tables
123 WHERE table_schema = 'public'
124 `)
125
126 // eslint-disable-next-line camelcase
127 const tableMainExists = tables.some(({ table_name }) => table_name === process.env.PG_DATA_TABLE)
128 // eslint-disable-next-line camelcase
129 const tablePricesExists = tables.some(({ table_name }) => table_name === process.env.PG_DATA_PRICE_TABLE)
130 if (!tableMainExists) throw new Error(`Table ${process.env.PG_DATA_TABLE} does not exist in database ${pgConfig.database}`)
131 if (!tablePricesExists) throw new Error(`Table ${process.env.PG_DATA_PRICE_TABLE} does not exist in database ${pgConfig.database}`)
132
133 // TODO: Handle pgClient closing
134 }
135}
136
137// inspired by @drobnikj
138// TODO: Similar, but less obfuscated for easier debugging
139export const createUniqueKeyFromUrl = (url) => {
140 const hash = createHash(`sha256`)
141 const cleanUrl = url.split(`://`)[1] // Remove protocol
142 hash.update(cleanUrl)
143 return hash.digest(`hex`)
144}
145
146/**
147 *
148 * @param {Date} datetime
149 * @return {Promise<void>}
150 */
151export const sleepUntil = async (datetime) => {
152 const now = new Date()
153 const difference = datetime - now
154 if (difference > 0) {
155 return new Promise((resolve) => {
156 setTimeout(resolve, difference)
157 })
158 }
159 return Promise.resolve()
160}
161
162// TODO: Uff, nicer! But at least it's tested
163export function parsePrice (string) {
164 let amount, currency
165 const noText = string.replace(/[^\d,.]/g, ``)
166 const decimals = noText.match(/([,.])(\d{2})$/)
167 if (decimals) {
168 const decimalSeparator = decimals[1] // ?
169 // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
170 const decimalAmount = decimals[2] // ?
171 const mainAmount = noText.split(decimalSeparator)[0].replace(/\D/g, ``)
172 amount = parseFloat(mainAmount + `.` + decimalAmount) // ?
173 } else {
174 const justNumbers = noText.replace(/[,.]/g, ``)
175 amount = parseInt(justNumbers)
176 }
177 return { amount, currency }
178}
179
180export function toNumberOrNull (str) {
181 // TODO: Handle better, but only after adding test
182 if (str === undefined) return null
183 if (str === null) return null
184 if (str === ``) return null
185 const num = Number(str)
186 if (Number.isNaN(num)) return null
187 return num
188}
189
190export async function save (objs) {
191 if (!Array.isArray(objs)) objs = [objs]
192 if (objs.length === 0) return console.log(`No data to save.`)
193
194 const objsExtended = objs.map(async (obj) => {
195 const objExtended = {
196 ...obj,
197 actorName,
198 ...globalLogsProps,
199 // __NODE_VERSION: global.process.versions.node,
200 // __NODE_UPTIME: global.process.uptime().toFixed(2), // seconds, 2 decimals
201 }
202 // if run on Apify
203 if (process.env.APIFY_IS_AT_HOME) {
204 objExtended.__APIFY_ACTOR_ID = process.env.APIFY_ACTOR_ID
205 objExtended.__APIFY_ACTOR_RUN_ID = process.env.APIFY_ACTOR_RUN_ID
206 objExtended.__APIFY_ACTOR_BUILD_ID = process.env.APIFY_ACTOR_BUILD_ID
207 objExtended.__APIFY_ACTOR_BUILD_NUMBER = process.env.APIFY_ACTOR_BUILD_NUMBER
208 objExtended.__APIFY_ACTOR_TASK_ID = process.env.APIFY_ACTOR_TASK_ID
209 if (process.env.APIFY_DONT_STORE_IN_DATASET !== `true`) { // Note: dotenv is not casting vars, so they are strings
210 await Dataset.pushData(obj)
211 }
212 }
213 return objExtended
214 })
215 // if runs on local machine (MacOS)
216 if (os.platform() === `darwin`) {
217 const cwd = process.cwd() // ~/Projects/apify-actors-monorepo/actors
218 const storageDir = path.join(cwd, `${actorName}.storage`) // ~/Projects/apify-actors-monorepo/actors/foo.storage
219 if (!fs.existsSync(storageDir)) fs.mkdirSync(storageDir)
220 const dataDir = path.join(storageDir, `data`) // ~/Projects/apify-actors-monorepo/actors/foo.storage/data
221 if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir)
222 for (const objExtended of objsExtended) {
223 const id = String(objExtended.id ?? objExtended.pid) // ?? uuidv4()
224 const fileName = `${filenamify(id)}.json`
225 const dataFilePath = path.join(dataDir, fileName) // ~/Projects/apify-actors-monorepo/actors/foo.storage/data/foo.json
226 fs.writeFileSync(dataFilePath, JSON.stringify(objExtended, null, 2))
227 }
228 }
229
230 if (pgClient) {
231 const objsPg = objs.map((obj) => ({
232 ...obj,
233 // TODO: This is becoming not nice, and not clear
234 shop: actorName,
235 scrapedAt: new Date().toISOString().split(`T`)[0],
236 }))
237
238 const columns = getColumns(objsPg)
239 const values = getValues(objsPg)
240 const queryString = `
241 INSERT INTO public."${process.env.PG_DATA_TABLE}" (${columns})
242 VALUES (${values})
243 `
244 try {
245 const { rowCount } = await pgClient.query(queryString)
246 console.log(`[save] saved to database: ${JSON.stringify(rowCount)}`)
247 } catch (err) {
248 if (err.message.includes(`violates unique constraint`)) console.warn(`PostgresSQL: violates unique constraint`)
249 else throw err
250 }
251 }
252
253 // Only make sense for HlidacShopu
254 if (pgClientNormalized) {
255 const objsPgData = objs.map((obj) => ({
256 shop: actorName,
257 pid: obj.pid,
258 name: obj.name,
259 url: obj.url,
260 img: obj.img,
261 }))
262
263 const objsPgDataPrice = objs.map((obj) => ({
264 shop: actorName,
265 pid: obj.pid,
266 scrapedAt: new Date().toISOString().split(`T`)[0],
267 currentPrice: obj.currentPrice,
268 originalPrice: obj.originalPrice,
269 inStock: obj.inStock,
270 }))
271
272 const queryString = `
273 INSERT INTO public."${process.env.PG_DATA_TABLE}" (${getColumns(objsPgData)})
274 VALUES (${getValues(objsPgData)})
275 ON CONFLICT DO NOTHING
276 `
277 try {
278 const { rowCount } = await pgClientNormalized.query(queryString)
279 console.log(`[save] saved to database (data): ${JSON.stringify(rowCount)}`)
280 } catch (err) {
281 if (err.message.includes(`violates unique constraint`)) console.warn(`PostgresSQL: violates unique constraint`)
282 else throw err
283 }
284
285 const queryStringPrice = `
286 INSERT INTO public."${process.env.PG_DATA_PRICE_TABLE}" (${getColumns(objsPgDataPrice)})
287 VALUES (${getValues(objsPgDataPrice)})
288 ON CONFLICT DO NOTHING
289 `
290 try {
291 const { rowCount } = await pgClientNormalized.query(queryStringPrice)
292 console.log(`[save] saved to database (price): ${JSON.stringify(rowCount)}`)
293 } catch (err) {
294 if (err.message.includes(`violates unique constraint`)) console.warn(`PostgresSQL: violates unique constraint`)
295 else throw err
296 }
297 }
298
299 if (elasticClient) {
300 // .index creates or updates the document
301 // .create creates a new document if it doesn't exist, 409 if it does
302 // try {
303 // const res = await elasticClient.index({
304 // index: `actors-monorepo-shops`, // TODO: Consider using actorName
305 // id, // foo-bar
306 // document: objExtended, // {...}
307 // })
308 // } catch (err) {
309 // // https://discuss.elastic.co/t/elasticsearch-503-ok-false-message-the-requested-deployment-is-currently-unavailable/200583
310 // if (err.message.includes(`requested resource is currently unavailable`)) console.log(`Elasticsearch is unavailable, skipping, but not aborting`)
311 // else throw err
312 // }
313 }
314}
315
316function getColumns (objs) {
317 return Object.keys(objs[0]).map((key) => `"${key}"`).join(`, `)
318}
319
320function getValues (objs) {
321 return objs.map(objPg => Object.values(objPg).map((value) => {
322 // escape strings to prevent SQL injection
323 if (typeof value === `string`) return `'${value.replace(/'/g, `''`)}'`
324 // convert to DB specific null
325 if (typeof value === `undefined` || value === null) return `NULL`
326 return value
327 }).join(`, `)).join(`), (`)
328}
329
330export function parseEnvFromInput (input) {
331 const env = {}
332 for (const key in input) {
333 if (key === key.toUpperCase()) env[key] = input[key]
334 }
335 console.log(`[parseEnvFromInput] ${JSON.stringify(env)}`)
336 Object.assign(process.env, env)
337}
338
339export const isInspect =
340 process.execArgv.join().includes(`--inspect`) ||
341 // @ts-ignore
342 process?._preload_modules?.join(`|`)?.includes(`debug`)
Developer
Maintained by Community
Categories