watchdiff-core 0.1.3__tar.gz → 0.1.4__tar.gz
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.
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/PKG-INFO +28 -28
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/README.md +27 -27
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/pyproject.toml +1 -1
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/uv.lock +1 -1
- watchdiff_core-0.1.4/watchdiff/cli/main.py +491 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/core.py +147 -55
- watchdiff_core-0.1.4/watchdiff/diff/engine.py +239 -0
- watchdiff_core-0.1.4/watchdiff/fetcher/fetcher.py +161 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/models.py +77 -19
- watchdiff_core-0.1.4/watchdiff/notifier/notifier.py +122 -0
- watchdiff_core-0.1.4/watchdiff/scheduler/scheduler.py +523 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/store/sqlite_store.py +10 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/store/store.py +7 -0
- watchdiff_core-0.1.3/watchdiff/cli/main.py +0 -362
- watchdiff_core-0.1.3/watchdiff/diff/engine.py +0 -149
- watchdiff_core-0.1.3/watchdiff/fetcher/fetcher.py +0 -122
- watchdiff_core-0.1.3/watchdiff/notifier/notifier.py +0 -81
- watchdiff_core-0.1.3/watchdiff/scheduler/scheduler.py +0 -267
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/.github/workflows/ci.yml +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/.github/workflows/release-testpypi.yml +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/.github/workflows/release.yml +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/.gitignore +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/.python-version +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/LICENSE +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/main.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/tests/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/tests/test_watchdiff.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/cleaner/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/cleaner/cleaner.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/cli/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/diff/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/exporter/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/exporter/exporter.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/fetcher/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/fetcher/browser.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/notifier/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/parser/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/parser/parser.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/scheduler/__init__.py +0 -0
- {watchdiff_core-0.1.3 → watchdiff_core-0.1.4}/watchdiff/store/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: watchdiff-core
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Lightweight web change monitoring library - clean diffs, structured alerts, no AI required.
|
|
5
5
|
Author: WatchDiff Contributors
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -85,13 +85,13 @@ No noisy HTML diffs. No external services. No AI black boxes.
|
|
|
85
85
|
|
|
86
86
|
## Why WatchDiff?
|
|
87
87
|
|
|
88
|
-
Most change detection tools compare raw HTML
|
|
88
|
+
Most change detection tools compare raw HTML - which means every minor script reload or ad rotation triggers a false positive. WatchDiff strips the noise first, then diffs only the content that matters.
|
|
89
89
|
|
|
90
|
-
- **Deterministic**
|
|
91
|
-
- **Human-readable diffs**
|
|
92
|
-
- **Zero external services**
|
|
93
|
-
- **Async-ready**
|
|
94
|
-
- **Python 3.9+**
|
|
90
|
+
- **Deterministic** - same input always produces the same output
|
|
91
|
+
- **Human-readable diffs** - "Price changed: $19 → $24", not a wall of HTML
|
|
92
|
+
- **Zero external services** - snapshots stored locally (JSON or SQLite)
|
|
93
|
+
- **Async-ready** - sync and async schedulers included
|
|
94
|
+
- **Python 3.9+** - works on Debian Bullseye, Bookworm, and Trixie
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
## Install
|
|
@@ -194,9 +194,9 @@ wd.start()
|
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
`wait_for` accepts:
|
|
197
|
-
- `"load"`
|
|
198
|
-
- `"domcontentloaded"`
|
|
199
|
-
- `"networkidle"`
|
|
197
|
+
- `"load"` - default, waits for the `load` event
|
|
198
|
+
- `"domcontentloaded"` - faster, waits for DOM only
|
|
199
|
+
- `"networkidle"` - waits until no network requests for 500ms
|
|
200
200
|
|
|
201
201
|
|
|
202
202
|
### Proxy rotation and User-Agent rotation
|
|
@@ -220,12 +220,12 @@ wd.watch(
|
|
|
220
220
|
|
|
221
221
|
If `user_agents` is empty, WatchDiff rotates automatically among **4 built-in modern UA strings** (Chrome, Safari, Firefox, Chrome Linux). No configuration required.
|
|
222
222
|
|
|
223
|
-
Proxies also work in browser mode
|
|
223
|
+
Proxies also work in browser mode - Playwright passes the selected proxy to Chromium.
|
|
224
224
|
|
|
225
225
|
|
|
226
226
|
### Semantic diff mode
|
|
227
227
|
|
|
228
|
-
By default, WatchDiff diffs line by line. In **semantic mode**, it extracts meaningful HTML blocks
|
|
228
|
+
By default, WatchDiff diffs line by line. In **semantic mode**, it extracts meaningful HTML blocks - `<p>`, `<h1>`-`<h6>`, `<li>`, `<td>`, `<th>`, `<blockquote>` - and diffs those instead. This gives cleaner results on content-heavy pages where a single paragraph change doesn't shift dozens of lines.
|
|
229
229
|
|
|
230
230
|
```python
|
|
231
231
|
wd.watch(
|
|
@@ -260,12 +260,12 @@ wd.watch("https://example.com", target="(//h2)[1]") # first <h2> only
|
|
|
260
260
|
wd.watch("https://example.com", target="//p[contains(@class,'intro')]")
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
-
XPath is implemented via `lxml` (already a dependency
|
|
263
|
+
XPath is implemented via `lxml` (already a dependency - no extra install needed).
|
|
264
264
|
|
|
265
265
|
|
|
266
266
|
### SQLite storage backend
|
|
267
267
|
|
|
268
|
-
By default, WatchDiff stores data as JSON files. For larger datasets or concurrent access, switch to the built-in SQLite backend
|
|
268
|
+
By default, WatchDiff stores data as JSON files. For larger datasets or concurrent access, switch to the built-in SQLite backend - no extra dependencies required:
|
|
269
269
|
|
|
270
270
|
```python
|
|
271
271
|
from watchdiff import WatchDiff
|
|
@@ -275,7 +275,7 @@ wd = WatchDiff(store=SqliteStore(".watchdiff.db"))
|
|
|
275
275
|
wd.watch("https://example.com").start()
|
|
276
276
|
```
|
|
277
277
|
|
|
278
|
-
`SqliteStore` is a **drop-in replacement** for the default `Store`
|
|
278
|
+
`SqliteStore` is a **drop-in replacement** for the default `Store` - same interface, same behaviour. It runs in WAL mode for concurrent-read safety.
|
|
279
279
|
|
|
280
280
|
|
|
281
281
|
### CSV and XLSX export
|
|
@@ -298,10 +298,10 @@ path = wd.export_snapshots_xlsx("https://example.com", dest="snapshots.xlsx")
|
|
|
298
298
|
```
|
|
299
299
|
|
|
300
300
|
All export methods accept:
|
|
301
|
-
- `url`
|
|
302
|
-
- `target`
|
|
303
|
-
- `limit`
|
|
304
|
-
- `dest`
|
|
301
|
+
- `url` - the watched URL
|
|
302
|
+
- `target` - CSS/XPath filter (optional, `None` = full page)
|
|
303
|
+
- `limit` - max rows to include (default 500)
|
|
304
|
+
- `dest` - file path to write (optional for CSV, required for XLSX)
|
|
305
305
|
|
|
306
306
|
|
|
307
307
|
### Cooldown anti-spam
|
|
@@ -320,7 +320,7 @@ wd.watch(
|
|
|
320
320
|
|
|
321
321
|
Important: **changes are still detected and stored** during the cooldown period. Only the alerts (callbacks, webhooks) are suppressed. The full history remains available via `.history()` and `.reports()`.
|
|
322
322
|
|
|
323
|
-
`cooldown=0` (default) disables the feature
|
|
323
|
+
`cooldown=0` (default) disables the feature - every change triggers an alert immediately.
|
|
324
324
|
|
|
325
325
|
In the CLI:
|
|
326
326
|
|
|
@@ -459,7 +459,7 @@ Pass `block=False` to run in the background (daemon threads).
|
|
|
459
459
|
|
|
460
460
|
#### `await .start_async()`
|
|
461
461
|
|
|
462
|
-
Async variant
|
|
462
|
+
Async variant - use inside an existing event loop (FastAPI, aiohttp, etc.):
|
|
463
463
|
|
|
464
464
|
```python
|
|
465
465
|
import asyncio
|
|
@@ -578,13 +578,13 @@ Options for check:
|
|
|
578
578
|
|
|
579
579
|
## Use cases
|
|
580
580
|
|
|
581
|
-
- **E-commerce**
|
|
582
|
-
- **News monitoring**
|
|
583
|
-
- **Documentation**
|
|
584
|
-
- **Public APIs**
|
|
585
|
-
- **SPA / React apps**
|
|
586
|
-
- **Compliance**
|
|
587
|
-
- **Research**
|
|
581
|
+
- **E-commerce** - track product prices and stock availability
|
|
582
|
+
- **News monitoring** - detect article updates or new publications
|
|
583
|
+
- **Documentation** - alert when API docs or changelogs change
|
|
584
|
+
- **Public APIs** - watch JSON endpoints for schema or value changes
|
|
585
|
+
- **SPA / React apps** - monitor JS-rendered content with `browser=True`
|
|
586
|
+
- **Compliance** - audit changes on public-facing pages over time
|
|
587
|
+
- **Research** - collect snapshots for longitudinal content analysis
|
|
588
588
|
|
|
589
589
|
|
|
590
590
|
## Contributing
|
|
@@ -46,13 +46,13 @@ No noisy HTML diffs. No external services. No AI black boxes.
|
|
|
46
46
|
|
|
47
47
|
## Why WatchDiff?
|
|
48
48
|
|
|
49
|
-
Most change detection tools compare raw HTML
|
|
49
|
+
Most change detection tools compare raw HTML - which means every minor script reload or ad rotation triggers a false positive. WatchDiff strips the noise first, then diffs only the content that matters.
|
|
50
50
|
|
|
51
|
-
- **Deterministic**
|
|
52
|
-
- **Human-readable diffs**
|
|
53
|
-
- **Zero external services**
|
|
54
|
-
- **Async-ready**
|
|
55
|
-
- **Python 3.9+**
|
|
51
|
+
- **Deterministic** - same input always produces the same output
|
|
52
|
+
- **Human-readable diffs** - "Price changed: $19 → $24", not a wall of HTML
|
|
53
|
+
- **Zero external services** - snapshots stored locally (JSON or SQLite)
|
|
54
|
+
- **Async-ready** - sync and async schedulers included
|
|
55
|
+
- **Python 3.9+** - works on Debian Bullseye, Bookworm, and Trixie
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
## Install
|
|
@@ -155,9 +155,9 @@ wd.start()
|
|
|
155
155
|
```
|
|
156
156
|
|
|
157
157
|
`wait_for` accepts:
|
|
158
|
-
- `"load"`
|
|
159
|
-
- `"domcontentloaded"`
|
|
160
|
-
- `"networkidle"`
|
|
158
|
+
- `"load"` - default, waits for the `load` event
|
|
159
|
+
- `"domcontentloaded"` - faster, waits for DOM only
|
|
160
|
+
- `"networkidle"` - waits until no network requests for 500ms
|
|
161
161
|
|
|
162
162
|
|
|
163
163
|
### Proxy rotation and User-Agent rotation
|
|
@@ -181,12 +181,12 @@ wd.watch(
|
|
|
181
181
|
|
|
182
182
|
If `user_agents` is empty, WatchDiff rotates automatically among **4 built-in modern UA strings** (Chrome, Safari, Firefox, Chrome Linux). No configuration required.
|
|
183
183
|
|
|
184
|
-
Proxies also work in browser mode
|
|
184
|
+
Proxies also work in browser mode - Playwright passes the selected proxy to Chromium.
|
|
185
185
|
|
|
186
186
|
|
|
187
187
|
### Semantic diff mode
|
|
188
188
|
|
|
189
|
-
By default, WatchDiff diffs line by line. In **semantic mode**, it extracts meaningful HTML blocks
|
|
189
|
+
By default, WatchDiff diffs line by line. In **semantic mode**, it extracts meaningful HTML blocks - `<p>`, `<h1>`-`<h6>`, `<li>`, `<td>`, `<th>`, `<blockquote>` - and diffs those instead. This gives cleaner results on content-heavy pages where a single paragraph change doesn't shift dozens of lines.
|
|
190
190
|
|
|
191
191
|
```python
|
|
192
192
|
wd.watch(
|
|
@@ -221,12 +221,12 @@ wd.watch("https://example.com", target="(//h2)[1]") # first <h2> only
|
|
|
221
221
|
wd.watch("https://example.com", target="//p[contains(@class,'intro')]")
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
XPath is implemented via `lxml` (already a dependency
|
|
224
|
+
XPath is implemented via `lxml` (already a dependency - no extra install needed).
|
|
225
225
|
|
|
226
226
|
|
|
227
227
|
### SQLite storage backend
|
|
228
228
|
|
|
229
|
-
By default, WatchDiff stores data as JSON files. For larger datasets or concurrent access, switch to the built-in SQLite backend
|
|
229
|
+
By default, WatchDiff stores data as JSON files. For larger datasets or concurrent access, switch to the built-in SQLite backend - no extra dependencies required:
|
|
230
230
|
|
|
231
231
|
```python
|
|
232
232
|
from watchdiff import WatchDiff
|
|
@@ -236,7 +236,7 @@ wd = WatchDiff(store=SqliteStore(".watchdiff.db"))
|
|
|
236
236
|
wd.watch("https://example.com").start()
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
-
`SqliteStore` is a **drop-in replacement** for the default `Store`
|
|
239
|
+
`SqliteStore` is a **drop-in replacement** for the default `Store` - same interface, same behaviour. It runs in WAL mode for concurrent-read safety.
|
|
240
240
|
|
|
241
241
|
|
|
242
242
|
### CSV and XLSX export
|
|
@@ -259,10 +259,10 @@ path = wd.export_snapshots_xlsx("https://example.com", dest="snapshots.xlsx")
|
|
|
259
259
|
```
|
|
260
260
|
|
|
261
261
|
All export methods accept:
|
|
262
|
-
- `url`
|
|
263
|
-
- `target`
|
|
264
|
-
- `limit`
|
|
265
|
-
- `dest`
|
|
262
|
+
- `url` - the watched URL
|
|
263
|
+
- `target` - CSS/XPath filter (optional, `None` = full page)
|
|
264
|
+
- `limit` - max rows to include (default 500)
|
|
265
|
+
- `dest` - file path to write (optional for CSV, required for XLSX)
|
|
266
266
|
|
|
267
267
|
|
|
268
268
|
### Cooldown anti-spam
|
|
@@ -281,7 +281,7 @@ wd.watch(
|
|
|
281
281
|
|
|
282
282
|
Important: **changes are still detected and stored** during the cooldown period. Only the alerts (callbacks, webhooks) are suppressed. The full history remains available via `.history()` and `.reports()`.
|
|
283
283
|
|
|
284
|
-
`cooldown=0` (default) disables the feature
|
|
284
|
+
`cooldown=0` (default) disables the feature - every change triggers an alert immediately.
|
|
285
285
|
|
|
286
286
|
In the CLI:
|
|
287
287
|
|
|
@@ -420,7 +420,7 @@ Pass `block=False` to run in the background (daemon threads).
|
|
|
420
420
|
|
|
421
421
|
#### `await .start_async()`
|
|
422
422
|
|
|
423
|
-
Async variant
|
|
423
|
+
Async variant - use inside an existing event loop (FastAPI, aiohttp, etc.):
|
|
424
424
|
|
|
425
425
|
```python
|
|
426
426
|
import asyncio
|
|
@@ -539,13 +539,13 @@ Options for check:
|
|
|
539
539
|
|
|
540
540
|
## Use cases
|
|
541
541
|
|
|
542
|
-
- **E-commerce**
|
|
543
|
-
- **News monitoring**
|
|
544
|
-
- **Documentation**
|
|
545
|
-
- **Public APIs**
|
|
546
|
-
- **SPA / React apps**
|
|
547
|
-
- **Compliance**
|
|
548
|
-
- **Research**
|
|
542
|
+
- **E-commerce** - track product prices and stock availability
|
|
543
|
+
- **News monitoring** - detect article updates or new publications
|
|
544
|
+
- **Documentation** - alert when API docs or changelogs change
|
|
545
|
+
- **Public APIs** - watch JSON endpoints for schema or value changes
|
|
546
|
+
- **SPA / React apps** - monitor JS-rendered content with `browser=True`
|
|
547
|
+
- **Compliance** - audit changes on public-facing pages over time
|
|
548
|
+
- **Research** - collect snapshots for longitudinal content analysis
|
|
549
549
|
|
|
550
550
|
|
|
551
551
|
## Contributing
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "watchdiff-core"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "Lightweight web change monitoring library - clean diffs, structured alerts, no AI required."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|