@toolpath/tool-scraper 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/columns.d.ts +5 -3
- package/dist/columns.js +11 -5
- package/dist/conventions.d.ts +18 -0
- package/dist/conventions.js +18 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.js +30 -0
- package/dist/families/emuge.d.ts +3 -1
- package/dist/families/emuge.js +8 -4
- package/dist/node/cli.js +6 -1
- package/dist/records.d.ts +57 -12
- package/dist/records.js +37 -13
- package/dist/registry.d.ts +26 -1
- package/dist/registry.js +40 -2
- package/dist/vendors/destinytool/records.js +8 -0
- package/dist/vendors/emuge/records.d.ts +83 -3
- package/dist/vendors/emuge/records.js +184 -16
- package/dist/vendors/kennametal/family.d.ts +119 -0
- package/dist/vendors/kennametal/family.js +155 -0
- package/dist/vendors/kennametal/index.d.ts +1 -0
- package/dist/vendors/kennametal/index.js +1 -0
- package/dist/vendors/kennametal/records.d.ts +16 -0
- package/dist/vendors/kennametal/records.js +32 -0
- package/dist/vendors/kennametal/scrape.d.ts +25 -2
- package/dist/vendors/kennametal/scrape.js +28 -3
- package/package.json +1 -1
|
@@ -24,8 +24,12 @@
|
|
|
24
24
|
* characters.
|
|
25
25
|
*/
|
|
26
26
|
import { Parser } from 'htmlparser2';
|
|
27
|
+
import { FAMILY_TITLE_COLUMN } from '../../conventions.js';
|
|
27
28
|
import { VendorResponseError } from '../../errors.js';
|
|
28
29
|
import { BRANDS } from '../../identity.js';
|
|
30
|
+
import { pause, REQUEST_DELAY_MS } from '../../scrape.js';
|
|
31
|
+
import { fetchFamily } from './family.js';
|
|
32
|
+
import { PRODUCT_LINE_COLUMN } from './records.js';
|
|
29
33
|
export const BASE = 'https://www.{host}/us/en/products/fam/_jcr_content/root/' +
|
|
30
34
|
'responsivegrid/{node}.variants.{code}.html' +
|
|
31
35
|
'?query={query}&uom=metric';
|
|
@@ -194,16 +198,37 @@ export function columnNames(header) {
|
|
|
194
198
|
* `tags` is a sequence of `[name, value]` pairs appended to every row as
|
|
195
199
|
* constant columns — used to tag facts the table doesn't state, e.g. the
|
|
196
200
|
* thread system on a tap family.
|
|
201
|
+
*
|
|
202
|
+
* `options.familyTitle` adds two more of exactly that kind. They are tags
|
|
203
|
+
* rather than parsed columns because that is what they are: one string per
|
|
204
|
+
* family, constant down its whole table, which is the case the `tags` seam was
|
|
205
|
+
* built for.
|
|
197
206
|
*/
|
|
198
|
-
export async function scrapeFamily(fetcher, code, brand = 'kennametal', tags = []) {
|
|
207
|
+
export async function scrapeFamily(fetcher, code, brand = 'kennametal', tags = [], options = {}) {
|
|
199
208
|
const url = variantsUrl(code, brand);
|
|
200
209
|
const { header, rows: dataRows } = parseVariantTable(await fetcher.text(url));
|
|
201
210
|
if (header === null) {
|
|
202
211
|
throw new VendorResponseError(`family ${code}`, 'the vendor returned no variants');
|
|
203
212
|
}
|
|
213
|
+
// After the table, so a family the vendor no longer publishes fails on the
|
|
214
|
+
// table it has no rows for rather than on a title nobody would have read.
|
|
215
|
+
const titled = [];
|
|
216
|
+
if (options.familyTitle === true) {
|
|
217
|
+
await pause(options.delayMs ?? REQUEST_DELAY_MS);
|
|
218
|
+
const { title, productLine } = await fetchFamily(fetcher, code, brand);
|
|
219
|
+
// A page with no heading writes no columns, rather than a column of empty
|
|
220
|
+
// strings that reads as a vendor stating an empty name. `unionHeader` is
|
|
221
|
+
// not in play here — this header is positional — so an absent tag is
|
|
222
|
+
// simply a narrower CSV.
|
|
223
|
+
if (title !== '')
|
|
224
|
+
titled.push([FAMILY_TITLE_COLUMN, title]);
|
|
225
|
+
if (productLine !== null)
|
|
226
|
+
titled.push([PRODUCT_LINE_COLUMN, productLine]);
|
|
227
|
+
}
|
|
204
228
|
const names = columnNames(header);
|
|
205
229
|
const kept = names.filter((name) => name !== null);
|
|
206
|
-
const
|
|
230
|
+
const allTags = [...tags, ...titled];
|
|
231
|
+
const csvHeader = [...kept, ...allTags.map(([name]) => name)];
|
|
207
232
|
const rows = dataRows.map((row) => {
|
|
208
233
|
// `dataRows` is filtered to rows exactly as long as the header, and
|
|
209
234
|
// `names` has one entry per header cell, so a length mismatch here means
|
|
@@ -218,7 +243,7 @@ export async function scrapeFamily(fetcher, code, brand = 'kennametal', tags = [
|
|
|
218
243
|
if (name !== null)
|
|
219
244
|
out[name] = row[index]?.[0] ?? '';
|
|
220
245
|
});
|
|
221
|
-
for (const [name, value] of
|
|
246
|
+
for (const [name, value] of allTags)
|
|
222
247
|
out[name] = value;
|
|
223
248
|
return out;
|
|
224
249
|
});
|