html-get 2.24.9 → 2.24.11
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/package.json +1 -1
- package/src/index.js +65 -56
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "html-get",
|
|
3
3
|
"description": "Get the HTML from any website, fine-tuned for correction & speed",
|
|
4
4
|
"homepage": "https://nicedoc.com/microlinkhq/html-get",
|
|
5
|
-
"version": "2.24.
|
|
5
|
+
"version": "2.24.11",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"main": "src/index.js",
|
|
8
8
|
"bin": {
|
package/src/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { parseUrl, isMediaUrl, isPdfUrl } = require('@metascraper/helpers')
|
|
3
|
+
const { parseUrl, isMediaUrl, isPdfUrl, memoizeOne } = require('@metascraper/helpers')
|
|
4
4
|
const { readFile, writeFile } = require('fs/promises')
|
|
5
5
|
const timeSpan = require('@kikobeats/time-span')()
|
|
6
6
|
const debug = require('debug-logfmt')('html-get')
|
|
7
|
-
const {
|
|
7
|
+
const { execFileSync } = require('child_process')
|
|
8
8
|
const PCancelable = require('p-cancelable')
|
|
9
9
|
const { AbortError } = require('p-retry')
|
|
10
10
|
const htmlEncode = require('html-encode')
|
|
@@ -28,15 +28,7 @@ const PDF_SIZE_TRESHOLD = 150 * 1024 // 150kb
|
|
|
28
28
|
const fetch = PCancelable.fn(
|
|
29
29
|
async (
|
|
30
30
|
url,
|
|
31
|
-
{
|
|
32
|
-
getTemporalFile,
|
|
33
|
-
mutool,
|
|
34
|
-
pandoc,
|
|
35
|
-
reflect = false,
|
|
36
|
-
timeout = REQ_TIMEOUT,
|
|
37
|
-
toEncode,
|
|
38
|
-
...opts
|
|
39
|
-
},
|
|
31
|
+
{ getTemporalFile, mutool, pandoc, reflect = false, timeout = REQ_TIMEOUT, toEncode, ...opts },
|
|
40
32
|
onCancel
|
|
41
33
|
) => {
|
|
42
34
|
const reqTimeout = reflect ? timeout / 2 : timeout
|
|
@@ -55,9 +47,7 @@ const fetch = PCancelable.fn(
|
|
|
55
47
|
})
|
|
56
48
|
|
|
57
49
|
const redirects = []
|
|
58
|
-
req.on('redirect', res =>
|
|
59
|
-
redirects.push({ statusCode: res.statusCode, url: res.url })
|
|
60
|
-
)
|
|
50
|
+
req.on('redirect', res => redirects.push({ statusCode: res.statusCode, url: res.url }))
|
|
61
51
|
|
|
62
52
|
try {
|
|
63
53
|
const res = await req
|
|
@@ -65,8 +55,7 @@ const fetch = PCancelable.fn(
|
|
|
65
55
|
const html = await (async () => {
|
|
66
56
|
const contentType = getContentType(res.headers)
|
|
67
57
|
|
|
68
|
-
const officeFormat =
|
|
69
|
-
pandoc && getOfficeFormat({ contentType, url: [res.url, url] })
|
|
58
|
+
const officeFormat = pandoc && getOfficeFormat({ contentType, url: [res.url, url] })
|
|
70
59
|
|
|
71
60
|
// a recognized office file never goes through mutool, even if the
|
|
72
61
|
// response is mislabeled as application/pdf
|
|
@@ -86,7 +75,17 @@ const fetch = PCancelable.fn(
|
|
|
86
75
|
if (officeFormat) {
|
|
87
76
|
const file = getTemporalFile(res.url, officeFormat)
|
|
88
77
|
await writeFile(file.path, res.body)
|
|
89
|
-
|
|
78
|
+
try {
|
|
79
|
+
const converted = await pandoc(officeFormat, file.path)
|
|
80
|
+
if (converted) return converted
|
|
81
|
+
debug('pandoc:empty', { url: res.url, format: officeFormat })
|
|
82
|
+
} catch (error) {
|
|
83
|
+
debug('pandoc:error', {
|
|
84
|
+
url: res.url,
|
|
85
|
+
format: officeFormat,
|
|
86
|
+
message: error.message || error
|
|
87
|
+
})
|
|
88
|
+
}
|
|
90
89
|
}
|
|
91
90
|
|
|
92
91
|
return contentType === 'text/html' || !isMediaUrl(url)
|
|
@@ -232,12 +231,7 @@ const isFetchMode = url => {
|
|
|
232
231
|
}
|
|
233
232
|
|
|
234
233
|
const defaultGetMode = (url, { prerender }) => {
|
|
235
|
-
if (
|
|
236
|
-
prerender === false ||
|
|
237
|
-
isMediaUrl(url) ||
|
|
238
|
-
isPdfUrl(url) ||
|
|
239
|
-
isOfficeUrl(url)
|
|
240
|
-
) {
|
|
234
|
+
if (prerender === false || isMediaUrl(url) || isPdfUrl(url) || isOfficeUrl(url)) {
|
|
241
235
|
return 'fetch'
|
|
242
236
|
}
|
|
243
237
|
if (prerender === true) return 'prerender'
|
|
@@ -246,41 +240,60 @@ const defaultGetMode = (url, { prerender }) => {
|
|
|
246
240
|
|
|
247
241
|
const defaultGetTemporalFile = (input, ext) => {
|
|
248
242
|
const hash = crypto.createHash('sha256').update(input).digest('hex')
|
|
249
|
-
const filepath = path.join(
|
|
250
|
-
os.tmpdir(),
|
|
251
|
-
ext === undefined ? hash : `${hash}.${ext}`
|
|
252
|
-
)
|
|
243
|
+
const filepath = path.join(os.tmpdir(), ext === undefined ? hash : `${hash}.${ext}`)
|
|
253
244
|
return { path: filepath }
|
|
254
245
|
}
|
|
255
246
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
247
|
+
// use execFileSync (no shell) to resolve the binary path: faster than execSync
|
|
248
|
+
// and free of any interpolation surface.
|
|
249
|
+
const whichSync = bin => {
|
|
250
|
+
try {
|
|
251
|
+
return execFileSync('which', [bin], {
|
|
252
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
253
|
+
})
|
|
254
|
+
.toString()
|
|
255
|
+
.trim()
|
|
256
|
+
} catch (_) {}
|
|
257
|
+
}
|
|
267
258
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
259
|
+
// probe the binary once, not per request: the default runners are wired as
|
|
260
|
+
// default parameters (evaluated on every call), so memoizeOne keeps the probe
|
|
261
|
+
// (`which` and, for pandoc, `--list-input-formats`) from re-spawning each time.
|
|
262
|
+
const defaultMutool = memoizeOne(() => {
|
|
263
|
+
const mutoolPath = whichSync('mutool')
|
|
264
|
+
if (!mutoolPath) return
|
|
265
|
+
return (...args) => $(`${mutoolPath} draw -q -F html ${args}`)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
const defaultPandoc = memoizeOne(() => {
|
|
269
|
+
try {
|
|
270
|
+
const pandocPath = whichSync('pandoc')
|
|
271
|
+
if (!pandocPath) return
|
|
272
|
+
// a broken/incompatible pandoc can throw here (or on the runner below); in
|
|
273
|
+
// either case conversion stays disabled instead of breaking every getHTML
|
|
274
|
+
// call through the default-parameter evaluation
|
|
275
|
+
const supported = new Set(
|
|
276
|
+
execFileSync(pandocPath, ['--list-input-formats'], {
|
|
272
277
|
stdio: ['pipe', 'pipe', 'ignore']
|
|
273
278
|
})
|
|
274
279
|
.toString()
|
|
275
280
|
.trim()
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
.split(/\s+/)
|
|
282
|
+
)
|
|
283
|
+
return async (format, filepath) => {
|
|
284
|
+
if (!supported.has(format)) return
|
|
285
|
+
// array form keeps `filepath` a single argv entry even if it has spaces
|
|
286
|
+
const { stdout } = await $(pandocPath, [
|
|
287
|
+
`--from=${format}`,
|
|
288
|
+
'--to=html',
|
|
289
|
+
'--standalone',
|
|
290
|
+
'--embed-resources',
|
|
291
|
+
filepath
|
|
292
|
+
])
|
|
293
|
+
return stdout.trim() ? stdout : undefined
|
|
294
|
+
}
|
|
295
|
+
} catch (_) {}
|
|
296
|
+
})
|
|
284
297
|
|
|
285
298
|
const getContent = PCancelable.fn(
|
|
286
299
|
(
|
|
@@ -372,12 +385,7 @@ module.exports = PCancelable.fn(
|
|
|
372
385
|
|
|
373
386
|
let shadowDOM = hasShadowDOM($)
|
|
374
387
|
|
|
375
|
-
if (
|
|
376
|
-
mode === 'fetch' &&
|
|
377
|
-
getBrowserless &&
|
|
378
|
-
shadowDOM &&
|
|
379
|
-
prerender !== false
|
|
380
|
-
) {
|
|
388
|
+
if (mode === 'fetch' && getBrowserless && shadowDOM && prerender !== false) {
|
|
381
389
|
debug('shadow DOM detected, retrying with prerender', { url: targetUrl })
|
|
382
390
|
const prerenderPromise = getContent(targetUrl, 'prerender', {
|
|
383
391
|
getBrowserless,
|
|
@@ -411,3 +419,4 @@ module.exports.PDF_SIZE_TRESHOLD = PDF_SIZE_TRESHOLD
|
|
|
411
419
|
module.exports.isFetchMode = isFetchMode
|
|
412
420
|
module.exports.getContent = getContent
|
|
413
421
|
module.exports.defaultMutool = defaultMutool
|
|
422
|
+
module.exports.defaultPandoc = defaultPandoc
|