extract-webpage 1.2.419 → 1.2.421
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 +7 -1
- package/dist/extract-webpage.cjs.js +1 -1
- package/dist/extract-webpage.cjs.js.map +1 -1
- package/dist/extract-webpage.es.js +2 -4
- package/dist/extract-webpage.es.js.map +1 -1
- package/dist/html-to-content/highlight.d.ts +8 -0
- package/dist/index.d.ts +3 -2
- package/package.json +6 -6
- package/src/html-to-content/highlight.ts +59 -0
- package/src/html-to-content/html-to-content.ts +1 -2
- package/src/html-to-content/html-utils.ts +11 -20
- package/src/index.ts +5 -2
- package/src/url-to-content/docx-to-content.ts +9 -5
- package/dist/html-to-cite/extract-author.d.ts +0 -11
- package/dist/html-to-cite/extract-cite.d.ts +0 -33
- package/dist/html-to-cite/extract-date/date-extractors.d.ts +0 -40
- package/dist/html-to-cite/extract-date/date-validators.d.ts +0 -15
- package/dist/html-to-cite/extract-date/extract-date-quick.d.ts +0 -8
- package/dist/html-to-cite/extract-date/extract-date.d.ts +0 -26
- package/dist/html-to-cite/extract-source.d.ts +0 -11
- package/dist/html-to-cite/extract-title.d.ts +0 -11
- package/dist/html-to-cite/human-names-recognize.d.ts +0 -16
- package/dist/html-to-cite/metadata-to-cite.d.ts +0 -16
- package/dist/html-to-cite/url-to-domain.d.ts +0 -20
- package/dist/html-to-content/prism-global.d.ts +0 -12
- package/src/html-to-cite/extract-author.ts +0 -125
- package/src/html-to-cite/extract-cite.ts +0 -97
- package/src/html-to-cite/extract-date/date-extractors.ts +0 -484
- package/src/html-to-cite/extract-date/date-validators.ts +0 -191
- package/src/html-to-cite/extract-date/extract-date-quick.ts +0 -184
- package/src/html-to-cite/extract-date/extract-date.ts +0 -1049
- package/src/html-to-cite/extract-source.ts +0 -35
- package/src/html-to-cite/extract-title.ts +0 -78
- package/src/html-to-cite/human-names-92k.json +0 -1
- package/src/html-to-cite/human-names-recognize.ts +0 -396
- package/src/html-to-cite/metadata-to-cite.ts +0 -77
- package/src/html-to-cite/url-to-domain.ts +0 -50
- package/src/html-to-content/prism-global.d.ts +0 -40
- package/src/html-to-content/prism-global.ts +0 -82
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Validation and filtering logic for extracted date candidates.
|
|
3
|
-
* Ensures dates fall within plausible ranges and meet format requirements.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// Function cache
|
|
7
|
-
var CACHE_SIZE = 8192;
|
|
8
|
-
// earliest possible date to take into account (inclusive)
|
|
9
|
-
var MIN_DATE = new Date(2002, 0, 0, 1); // Note: month is 0-indexed in JavaScript
|
|
10
|
-
|
|
11
|
-
var DEBUG = 0;
|
|
12
|
-
|
|
13
|
-
const LOGGER = DEBUG ? console : { debug: () => { }, log: () => { }, error: () => { }, warn: () => { } };
|
|
14
|
-
|
|
15
|
-
function is_valid_date(date_input, outputformat, earliest, latest) {
|
|
16
|
-
// safety check
|
|
17
|
-
if (date_input === null) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
if (!earliest || !earliest.getFullYear)
|
|
21
|
-
earliest = new Date(2002, 0, 0, 1);
|
|
22
|
-
if (!latest)
|
|
23
|
-
latest = new Date();
|
|
24
|
-
|
|
25
|
-
let dateobject;
|
|
26
|
-
if (date_input instanceof Date) {
|
|
27
|
-
dateobject = date_input;
|
|
28
|
-
} else {
|
|
29
|
-
try {
|
|
30
|
-
if (outputformat === 'YYYY-MM-DD') {
|
|
31
|
-
dateobject = new Date(date_input.slice(0, 4), date_input.slice(5, 7) - 1, date_input.slice(8, 10));
|
|
32
|
-
} else {
|
|
33
|
-
dateobject = new Date(date_input);
|
|
34
|
-
}
|
|
35
|
-
} catch (error) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
earliest.getFullYear() <= dateobject.getFullYear() &&
|
|
42
|
-
dateobject.getFullYear() <= latest.getFullYear() &&
|
|
43
|
-
earliest.getTime() <= dateobject.getTime() &&
|
|
44
|
-
dateobject.getTime() <= latest.getTime()
|
|
45
|
-
) {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
LOGGER.debug(`date not valid: ${date_input}`);
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function is_valid_format(outputformat) {
|
|
53
|
-
// test with date object
|
|
54
|
-
var dateobject = new Date(2017, 8, 1, 0, 0);
|
|
55
|
-
try {
|
|
56
|
-
dateobject.toLocaleDateString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit' });
|
|
57
|
-
} catch (err) {
|
|
58
|
-
LOGGER.error(`wrong output format or type: ${outputformat} ${err}`);
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
// test in abstracto (could be the only test)
|
|
62
|
-
if (typeof outputformat !== 'string' || !outputformat.includes('%')) {
|
|
63
|
-
LOGGER.error(`malformed output format: ${outputformat}`);
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function plausible_year_filter(htmlstring, pattern, yearpat, earliest, latest, incomplete = false) {
|
|
70
|
-
var occurrences = new Map();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!htmlstring)
|
|
74
|
-
return occurrences;
|
|
75
|
-
|
|
76
|
-
var matches = htmlstring.match(new RegExp(pattern, 'g')) || [];
|
|
77
|
-
|
|
78
|
-
matches.forEach(item => {
|
|
79
|
-
var count = occurrences.get(item) || 0;
|
|
80
|
-
occurrences.set(item, count + 1);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
for (var [item, count] of occurrences) {
|
|
84
|
-
var year_match = item.match(yearpat);
|
|
85
|
-
if (!year_match) {
|
|
86
|
-
LOGGER.debug(`not a year pattern: ${item}`);
|
|
87
|
-
occurrences.delete(item);
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
var lastdigits = year_match[1];
|
|
92
|
-
let potential_year;
|
|
93
|
-
if (!incomplete) {
|
|
94
|
-
potential_year = parseInt(lastdigits);
|
|
95
|
-
} else {
|
|
96
|
-
var century = lastdigits[0] === '9' ? '19' : '20';
|
|
97
|
-
potential_year = parseInt(century + lastdigits);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (!(earliest.getFullYear() <= potential_year && potential_year <= latest.getFullYear())) {
|
|
101
|
-
LOGGER.debug(`no potential year: ${item}`);
|
|
102
|
-
occurrences.delete(item);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return occurrences;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function compare_values(reference, attempt, options) {
|
|
110
|
-
try {
|
|
111
|
-
var timestamp = new Date(attempt).getTime() / 1000;
|
|
112
|
-
if (options.original) {
|
|
113
|
-
reference = reference ? Math.min(reference, timestamp) : timestamp;
|
|
114
|
-
} else {
|
|
115
|
-
reference = Math.max(reference, timestamp);
|
|
116
|
-
}
|
|
117
|
-
} catch (err) {
|
|
118
|
-
LOGGER.debug(`Date parsing exception: ${err} for string ${attempt}`);
|
|
119
|
-
}
|
|
120
|
-
return reference;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function filter_ymd_candidate(bestmatch, pattern, original_date, copyear, outputformat, min_date, max_date) {
|
|
124
|
-
if (bestmatch) {
|
|
125
|
-
var pagedate = `${bestmatch[1]}-${bestmatch[2]}-${bestmatch[3]}`;
|
|
126
|
-
if (is_valid_date(pagedate, 'YYYY-MM-DD', min_date, max_date) &&
|
|
127
|
-
(copyear === 0 || parseInt(bestmatch[1]) >= copyear)) {
|
|
128
|
-
LOGGER.debug(`date found for pattern "${pattern}": ${pagedate}`);
|
|
129
|
-
return convert_date(pagedate, 'YYYY-MM-DD', outputformat);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function convert_date(datestring, inputformat, outputformat) {
|
|
136
|
-
if (inputformat === outputformat) {
|
|
137
|
-
return datestring;
|
|
138
|
-
}
|
|
139
|
-
if (datestring instanceof Date) {
|
|
140
|
-
return datestring.toLocaleDateString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit' });
|
|
141
|
-
}
|
|
142
|
-
var dateobject = new Date(datestring);
|
|
143
|
-
return dateobject.toLocaleDateString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit' });
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function check_extracted_reference(reference, options) {
|
|
147
|
-
if (reference > 0) {
|
|
148
|
-
var dateobject = new Date(reference * 1000);
|
|
149
|
-
var converted = dateobject.toLocaleDateString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit' });
|
|
150
|
-
if (is_valid_date(converted, options.format, options.min, options.max)) {
|
|
151
|
-
return converted;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function check_date_input(date_object, default_date) {
|
|
158
|
-
if (date_object instanceof Date) {
|
|
159
|
-
return date_object;
|
|
160
|
-
}
|
|
161
|
-
if (typeof date_object === 'string') {
|
|
162
|
-
try {
|
|
163
|
-
return new Date(date_object);
|
|
164
|
-
} catch (error) {
|
|
165
|
-
LOGGER.warn(`invalid datetime string: ${date_object}`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return default_date;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function get_min_date(min_date) {
|
|
172
|
-
return check_date_input(min_date, MIN_DATE);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function get_max_date(max_date) {
|
|
176
|
-
return check_date_input(max_date, new Date());
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
export {
|
|
181
|
-
is_valid_date,
|
|
182
|
-
is_valid_format,
|
|
183
|
-
plausible_year_filter,
|
|
184
|
-
compare_values,
|
|
185
|
-
filter_ymd_candidate,
|
|
186
|
-
convert_date,
|
|
187
|
-
check_extracted_reference,
|
|
188
|
-
check_date_input,
|
|
189
|
-
get_min_date,
|
|
190
|
-
get_max_date
|
|
191
|
-
};
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview High-performance date extraction utility for HTML documents.
|
|
3
|
-
* Prioritizes meta tags, specific selectors, and URL patterns for fast results.
|
|
4
|
-
*/
|
|
5
|
-
// Constants
|
|
6
|
-
const DATE_PUBLISHED_META_TAGS = [
|
|
7
|
-
'article:published_time',
|
|
8
|
-
'displaydate',
|
|
9
|
-
'dc.date',
|
|
10
|
-
'dc.date.issued',
|
|
11
|
-
'rbpubdate',
|
|
12
|
-
'publish_date',
|
|
13
|
-
'pub_date',
|
|
14
|
-
'pagedate',
|
|
15
|
-
'pubdate',
|
|
16
|
-
'revision_date',
|
|
17
|
-
'doc_date',
|
|
18
|
-
'date_created',
|
|
19
|
-
'content_create_date',
|
|
20
|
-
'lastmodified',
|
|
21
|
-
'created',
|
|
22
|
-
'date',
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
const DATE_PUBLISHED_SELECTORS = [
|
|
26
|
-
'.hentry .dtstamp.published',
|
|
27
|
-
'.hentry .published',
|
|
28
|
-
'.hentry .dtstamp.updated',
|
|
29
|
-
'.hentry .updated',
|
|
30
|
-
'.single .published',
|
|
31
|
-
'.meta .published',
|
|
32
|
-
'.meta .postDate',
|
|
33
|
-
'.entry-date',
|
|
34
|
-
'.byline .date',
|
|
35
|
-
'.postmetadata .date',
|
|
36
|
-
'.article_datetime',
|
|
37
|
-
'.date-header',
|
|
38
|
-
'.story-date',
|
|
39
|
-
'.dateStamp',
|
|
40
|
-
'#story .datetime',
|
|
41
|
-
'.dateline',
|
|
42
|
-
'.pubdate',
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
const abbrevMonthsStr = '(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)';
|
|
46
|
-
const DATE_PUBLISHED_URL_RES = [
|
|
47
|
-
new RegExp('/(20\\d{2}/\\d{2}/\\d{2})/', 'i'),
|
|
48
|
-
new RegExp('(20\\d{2}-[01]\\d-[0-3]\\d)', 'i'),
|
|
49
|
-
new RegExp(`/(20\\d{2}/${abbrevMonthsStr}/[0-3]\\d)/`, 'i'),
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
const MS_DATE_STRING = /^\d{13}$/;
|
|
53
|
-
const SEC_DATE_STRING = /^\d{10}$/;
|
|
54
|
-
const CLEAN_DATE_STRING_RE = /^(.+?)(?: [a-z]{3})?(?::\d{2}| \d{4})? (?:[a-z]{3} )?[a-z]{3}$/i;
|
|
55
|
-
const TIME_AGO_STRING = /(\d+)\s+(.*?)\s+ago/;
|
|
56
|
-
const TIME_NOW_STRING = /^\s*(now|\d{1,2}\s*min(ute)?s?)\s*$/i;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Extract date from document using various methods
|
|
60
|
-
*
|
|
61
|
-
* @param {Document} document - DOM object with article content
|
|
62
|
-
* @param {string} url - URL of the page
|
|
63
|
-
* @returns {string|null} Extracted date or null if not found
|
|
64
|
-
*/
|
|
65
|
-
export function extractDateQuick(document, url) {
|
|
66
|
-
let datePublished;
|
|
67
|
-
|
|
68
|
-
// Helper function to extract content from meta tags
|
|
69
|
-
function extractFromMeta(tags) {
|
|
70
|
-
for (const tag of tags) {
|
|
71
|
-
const metaTag = document.querySelector(`meta[name="${tag}"], meta[property="${tag}"]`);
|
|
72
|
-
if (metaTag) {
|
|
73
|
-
return metaTag.getAttribute('content');
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Helper function to extract content from selectors
|
|
80
|
-
function extractFromSelectors(selectors) {
|
|
81
|
-
for (const selector of selectors) {
|
|
82
|
-
const element = document.querySelector(selector);
|
|
83
|
-
if (element) {
|
|
84
|
-
return element.textContent.trim();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// 1. Check meta tags
|
|
91
|
-
datePublished = extractFromMeta(DATE_PUBLISHED_META_TAGS);
|
|
92
|
-
if (datePublished) return cleanDatePublished(datePublished);
|
|
93
|
-
|
|
94
|
-
// 2. Check selectors
|
|
95
|
-
datePublished = extractFromSelectors(DATE_PUBLISHED_SELECTORS);
|
|
96
|
-
if (datePublished) return cleanDatePublished(datePublished);
|
|
97
|
-
|
|
98
|
-
// 3. Check URL
|
|
99
|
-
for (const regex of DATE_PUBLISHED_URL_RES) {
|
|
100
|
-
const match = url.match(regex);
|
|
101
|
-
if (match) {
|
|
102
|
-
return cleanDatePublished(match[1]);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// 4. Check for specific text patterns
|
|
107
|
-
const textPatterns = ['Published', 'Updated', 'Posted'];
|
|
108
|
-
for (const pattern of textPatterns) {
|
|
109
|
-
for (const tag of ['time', 'span', 'div', 'p']) {
|
|
110
|
-
const elements = document.getElementsByTagName(tag);
|
|
111
|
-
for (const el of elements) {
|
|
112
|
-
if (el.textContent.includes(pattern)) {
|
|
113
|
-
datePublished = el.textContent.split(pattern)[1].trim();
|
|
114
|
-
if (datePublished) return cleanDatePublished(datePublished);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function cleanDatePublished(dateString) {
|
|
124
|
-
// If string is in milliseconds or seconds, convert to int and return
|
|
125
|
-
if (MS_DATE_STRING.test(dateString)) {
|
|
126
|
-
return new Date(parseInt(dateString, 10)).toISOString();
|
|
127
|
-
}
|
|
128
|
-
if (SEC_DATE_STRING.test(dateString)) {
|
|
129
|
-
return new Date(parseInt(dateString, 10) * 1000).toISOString();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
dateString = cleanDateString(dateString);
|
|
133
|
-
|
|
134
|
-
return dateString
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function cleanDateString(dateString) {
|
|
138
|
-
return dateString?.replace(CLEAN_DATE_STRING_RE, '$1').trim();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function createDate(dateString) {
|
|
142
|
-
if (TIME_AGO_STRING.test(dateString)) {
|
|
143
|
-
const [, amount, unit] = TIME_AGO_STRING.exec(dateString);
|
|
144
|
-
const now = new Date();
|
|
145
|
-
now.setMilliseconds(0);
|
|
146
|
-
switch (unit?.toLowerCase()) {
|
|
147
|
-
case 'second':
|
|
148
|
-
case 'seconds':
|
|
149
|
-
now.setSeconds(now.getSeconds() - parseInt(amount));
|
|
150
|
-
break;
|
|
151
|
-
case 'minute':
|
|
152
|
-
case 'minutes':
|
|
153
|
-
now.setMinutes(now.getMinutes() - parseInt(amount));
|
|
154
|
-
break;
|
|
155
|
-
case 'hour':
|
|
156
|
-
case 'hours':
|
|
157
|
-
now.setHours(now.getHours() - parseInt(amount));
|
|
158
|
-
break;
|
|
159
|
-
case 'day':
|
|
160
|
-
case 'days':
|
|
161
|
-
now.setDate(now.getDate() - parseInt(amount));
|
|
162
|
-
break;
|
|
163
|
-
case 'week':
|
|
164
|
-
case 'weeks':
|
|
165
|
-
now.setDate(now.getDate() - parseInt(amount) * 7);
|
|
166
|
-
break;
|
|
167
|
-
case 'month':
|
|
168
|
-
case 'months':
|
|
169
|
-
now.setMonth(now.getMonth() - parseInt(amount));
|
|
170
|
-
break;
|
|
171
|
-
case 'year':
|
|
172
|
-
case 'years':
|
|
173
|
-
now.setFullYear(now.getFullYear() - parseInt(amount));
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
return now;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (TIME_NOW_STRING.test(dateString)) {
|
|
180
|
-
return new Date();
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return new Date(dateString);
|
|
184
|
-
}
|