NSTS
NSTS determines the validity timestamp of a webpage: the date that best represents its current primary content. It combines a symbolic date extractor (CPE), an OpenAI semantic reasoner (PSR), and confidence-based selection between their candidates.
Installation
python -m pip install NSTS
Python 3.10 or newer is required. Runtime dependencies install automatically. Import the NSTS class from the lowercase nsts package.
Determine a timestamp from a URL
Set OPENAI_API_KEY and APIFY_API_TOKEN in the environment of your Python process, then run:
from nsts import NSTS
nsts = NSTS()
result = nsts.get_timestamps(url="https://www.example.com/article")
print(result)
With only a URL, NSTS fetches the page text through Apify, fetches HTML metadata over HTTP, and uses OpenAI for semantic reasoning. Live service calls use your accounts and may incur their normal charges.
Use text and HTML you already have
If you already have the URL and page text, pass both to get_timestamps:
from nsts import NSTS
url = "https://example.com/article"
text = " Published on 2020-05-17. "
nsts = NSTS()
result = nsts.get_timestamps(url=url, text=text)
print(result)
Supplying nonempty text avoids Apify crawling, so APIFY_API_TOKEN is not needed. NSTS still fetches HTML metadata from the URL, and the default mode still requires OPENAI_API_KEY.
If you also have the HTML, supply it to avoid fetching page content entirely:
from nsts import NSTS
url = "https://example.com/article"
text = " Published on 2020-05-17. "
html = '<html><head><title>Example</title></head><body>Published on 2020-05-17.</body></html>'
nsts = NSTS()
result = nsts.get_timestamps(url=url, text=text, html=html)
print(result)
The default mode still calls OpenAI even when text and HTML are supplied. Omitted or empty text triggers Apify fetching; omitted or empty HTML triggers HTTP metadata fetching.
Choose a mode and LLM model
extraction_method |
Behavior | Credentials |
|---|---|---|
"nsts" (default) |
Combines symbolic and semantic candidates using confidence. | OpenAI; Apify if fetching text. |
"psr" |
Uses semantic reasoning. | OpenAI; Apify if fetching text. |
"cpe" |
Uses deterministic date patterns. | Apify only if fetching text. |
Specify the mode and model when constructing NSTS:
from nsts import NSTS
nsts = NSTS(
extraction_method="nsts",
llm_model="gpt-4o-mini",
)
gpt-4o-mini is the default. To use another model, set llm_model to a model your OpenAI account supports for Chat Completions with function tools. The model setting applies to nsts and psr; cpe does not use an LLM.
Read the result
Every determination example above assigns the returned dictionary to result. To access the selected timestamp:
print(result["timestamp"])
The dictionary also includes the candidates and confidence:
| Field | Meaning |
|---|---|
url |
Input webpage URL. |
timestamp |
Selected validity timestamp, possibly a partial date or empty string. |
cpe_timestamp |
Symbolic candidate when CPE ran. |
psr_timestamp |
Semantic candidate when PSR ran. |
system_confidence |
PSR confidence, including high, low, conflicted, or none; may be empty if PSR did not run or failed. |
These are estimates. A date mentioned in content can differ from the page's validity date; semantic modes help distinguish those cases.
Corpus-Level Use
IGD is a corpus-level denoising step. Use it when processing multiple pages from the same source or domain, then determine a timestamp for each cleaned item. Call remove_boilerplate once on the corpus; get_timestamps does not automatically perform corpus denoising.
The following example illustrates the workflow with two records. Replace them with your corpus. The default neural mode requires OPENAI_API_KEY; supplied nonempty text and HTML avoid page fetching.
from nsts import NSTS
nsts = NSTS(
min_len=30,
min_repeatance_ratio=0.01,
min_docs=30,
)
corpus_data = [
{"url": "...", "text": "Header... Unique 1... Footer...", "html": "<html>...</html>"},
{"url": "...", "text": "Header... Unique 2... Footer...", "html": "<html>...</html>"},
]
cleaned_corpus = nsts.remove_boilerplate(corpus_data)
for item in cleaned_corpus:
result = nsts.get_timestamps(
url=item["url"],
text=item["text"],
html=item["html"],
)
print(f"{item['url']}: {result['timestamp']}")
The following settings control corpus denoising:
| Parameter | Default | Meaning |
|---|---|---|
min_len |
30 |
Minimum text-pattern length in characters considered for removal. Increase it to focus on longer repeated blocks. |
min_repeatance_ratio |
0.01 |
Fraction of corpus documents in which a pattern must occur, subject to the min_docs minimum. Increase it to require broader repetition in larger corpora. |
min_docs |
30 |
Minimum number of documents containing a pattern before removal. Set a smaller positive integer for a smaller corpus. |
The document threshold is max(min_docs, int(len(corpus_data) * min_repeatance_ratio)). With the default min_docs=30, a pattern must occur in at least 30 documents, so the two-item illustration above does not remove repeated patterns. With 10,000 documents and the default ratio, the threshold is 100 documents. For a smaller corpus, set min_docs=2 to allow removal of patterns shared by at least two documents. The ratio can still require a higher document count; lowering the ratio alone does not lower min_docs. The above example illustrates the workflow; removal also requires a shared pattern that meets min_len.
Supply dictionaries with url and string text fields and, optionally, html. The returned list contains cleaned text with the original URL and supplied HTML; additional custom fields are not retained. Keep your original records separately if you need those fields.
Research and examples
The repository contains runnable examples, benchmark datasets/results, reproduction instructions, and the paper's citation. Benchmark tools and data are not installed with this library. See issues for problem reports.
Released under the MIT license.
Metadata
Release files for NSTS 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nsts-0.1.0.tar.gz | 18.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nsts-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.3 kB
Release files / nsts-0.1.0.tar.gz
| Download URL | nsts-0.1.0.tar.gz |
|---|---|
| Size | 18.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
10bb880bac69deb539407bd3872523bf75c8a1078cf27fe7bf79b01f4af1972b
|
|
BLAKE2b-256 checksum How to use checksums |
f73d02b0acafb6935bbd2a04beac11b3224dc407c39e002d813d88a71a219722
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / nsts-0.1.0-py3-none-any.whl
| Download URL | nsts-0.1.0-py3-none-any.whl |
|---|---|
| Size | 20.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6e51e367d867ac129bfa717585304982642fce8a101d66f42932e03d62b2b638
|
|
BLAKE2b-256 checksum How to use checksums |
80a3ef5a5759bbe12dd2a5f6d50ae9c2157a813061a60876377436b4809ec65f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|