metascraper-defuddle 5.50.7 → 5.51.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/README.md +19 -0
- package/package.json +2 -2
- package/src/index.d.ts +6 -1
- package/src/index.js +38 -16
package/README.md
CHANGED
|
@@ -14,6 +14,25 @@
|
|
|
14
14
|
$ npm install metascraper-defuddle --save
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
const metascraper = require('metascraper')([require('metascraper-defuddle')()])
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
It runs a single [Defuddle](https://github.com/kepano/defuddle) `parseInternal` pass — not the high-level `Defuddle()`, whose `parse()` re-runs on low `wordCount` to recover _content_. This connector extracts metadata, which is read from meta tags/schema regardless of content removals, so those retries add nothing here.
|
|
24
|
+
|
|
25
|
+
The Defuddle result is memoized by HTML, so the same page is defuddled once even when several rules (or another consumer, such as a markdown renderer) request it; the same URL re-fetched with different HTML still re-extracts.
|
|
26
|
+
|
|
27
|
+
### Options
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
require('metascraper-defuddle')({ preprocess, defuddleOpts })
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **preprocess** `(document, html) => void` — mutate the parsed DOM before extraction (e.g. strip noise, normalize flattened shadow-DOM content).
|
|
34
|
+
- **defuddleOpts** `object` — options forwarded to Defuddle's `parseInternal` (e.g. `{ removeLowScoring: false }` to keep link-heavy blocks).
|
|
35
|
+
|
|
17
36
|
## License
|
|
18
37
|
|
|
19
38
|
**metascraper-defuddle** © [Microlink](https://microlink.io), released under the [MIT](https://github.com/microlinkhq/metascraper/blob/master/LICENSE.md) License.<br>
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "metascraper-defuddle",
|
|
3
3
|
"description": "A Defuddle connector for metascraper",
|
|
4
4
|
"homepage": "https://github.com/microlinkhq/metascraper/packages/metascraper-defuddle",
|
|
5
|
-
"version": "5.
|
|
5
|
+
"version": "5.51.0",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
7
7
|
"main": "src/index.js",
|
|
8
8
|
"author": {
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"ava": {
|
|
46
46
|
"timeout": "15s"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "20f037012985c0a65acfc5d15c5147082746ea86"
|
|
49
49
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
type Options = {
|
|
2
|
+
preprocess?: (document: Document, html: string) => void,
|
|
3
|
+
defuddleOpts?: import('defuddle/node').DefuddleOptions
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
declare function rules(options?: Options): import('metascraper').Rules
|
|
2
7
|
export = rules
|
package/src/index.js
CHANGED
|
@@ -1,24 +1,46 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { composeRule, getHtml } = require('@metascraper/helpers')
|
|
4
|
+
const debug = require('debug-logfmt')('metascraper-defuddle')
|
|
4
5
|
const asyncMemoizeOne = require('async-memoize-one')
|
|
5
6
|
const { parseHTML } = require('linkedom')
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
// Cache key = HTML + url + the identity of `preprocess`/`defuddleOpts`, so a
|
|
9
|
+
// call with the same HTML but a different hook or options (e.g. another
|
|
10
|
+
// metascraper-defuddle() instance or defuddleExtract consumer) re-extracts
|
|
11
|
+
// instead of reusing a result produced with different ones.
|
|
12
|
+
const isSameExtraction = (
|
|
13
|
+
[html, url, opts = {}],
|
|
14
|
+
[oldHtml, oldUrl, oldOpts = {}]
|
|
15
|
+
) =>
|
|
16
|
+
html === oldHtml &&
|
|
17
|
+
url === oldUrl &&
|
|
18
|
+
opts.preprocess === oldOpts.preprocess &&
|
|
19
|
+
opts.defuddleOpts === oldOpts.defuddleOpts
|
|
20
|
+
|
|
21
|
+
// Single Defuddle `parseInternal` pass, memoized. See README for the
|
|
22
|
+
// `preprocess` / `defuddleOpts` options and the rationale.
|
|
23
|
+
const extractMemo = asyncMemoizeOne(
|
|
24
|
+
async (html, url, { preprocess, defuddleOpts } = {}) => {
|
|
25
|
+
const { DefuddleClass } = await import('defuddle/node')
|
|
26
|
+
try {
|
|
27
|
+
const { document } = parseHTML(html)
|
|
28
|
+
if (preprocess) preprocess(document, html)
|
|
29
|
+
return new DefuddleClass(document, { url }).parseInternal(defuddleOpts)
|
|
30
|
+
} catch (err) {
|
|
31
|
+
debug('Defuddle failed, fallback to next rule', { message: err.message })
|
|
32
|
+
return undefined
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
isSameExtraction
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const defuddleExtract = (url, html, options) => extractMemo(html, url, options)
|
|
39
|
+
|
|
40
|
+
module.exports = ({ preprocess, defuddleOpts } = {}) => {
|
|
41
|
+
const getDefuddle = composeRule(($, url) =>
|
|
42
|
+
defuddleExtract(url, getHtml($), { preprocess, defuddleOpts })
|
|
43
|
+
)
|
|
22
44
|
|
|
23
45
|
const rules = {
|
|
24
46
|
author: getDefuddle({ from: 'author' }),
|