scraper-rust 0.5.1__tar.gz → 0.7.0__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.
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/.github/workflows/release.yml +43 -72
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/.github/workflows/tests.yml +3 -6
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/AGENTS.md +13 -14
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/Cargo.lock +345 -389
- scraper_rust-0.7.0/Cargo.toml +48 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/PKG-INFO +20 -8
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/README.md +18 -6
- scraper_rust-0.7.0/benchmarks/README.md +54 -0
- scraper_rust-0.7.0/benchmarks/common.rs +122 -0
- scraper_rust-0.7.0/benchmarks/parse_scaling.rs +38 -0
- scraper_rust-0.7.0/benchmarks/sync_async.rs +128 -0
- scraper_rust-0.7.0/build.rs +3 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/docs/api.md +1 -1
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/docs/async-api.md +2 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/justfile +3 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/pyproject.toml +2 -2
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/scraper_rs/__init__.py +1 -1
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/scraper_rs/asyncio.py +11 -11
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/async_core.rs +9 -5
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/document.rs +26 -35
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/element.rs +0 -18
- scraper_rust-0.7.0/src/html5_dict.rs +151 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/lib.rs +4 -2
- scraper_rust-0.7.0/src/prettify.rs +279 -0
- scraper_rust-0.7.0/src/selectors.rs +47 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/tests.rs +11 -12
- scraper_rust-0.7.0/src/text.rs +89 -0
- scraper_rust-0.7.0/src/tl_dom.rs +313 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/xpath.rs +10 -14
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/tests/test_scraper.py +56 -2
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/uv.lock +44 -44
- scraper_rust-0.5.1/Cargo.toml +0 -31
- scraper_rust-0.5.1/benchmarks/README.md +0 -440
- scraper_rust-0.5.1/benchmarks/bench_parse_memory.py +0 -523
- scraper_rust-0.5.1/benchmarks/bench_sync_async.py +0 -309
- scraper_rust-0.5.1/benchmarks/bench_vs_markupever.py +0 -273
- scraper_rust-0.5.1/src/html5_dict.rs +0 -493
- scraper_rust-0.5.1/src/prettify.rs +0 -207
- scraper_rust-0.5.1/src/selectors.rs +0 -85
- scraper_rust-0.5.1/src/text.rs +0 -76
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/.github/workflows/benchmark.yml +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/.github/workflows/bump-version.yml +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/.gitignore +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/LICENSE +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/docs/README.md +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/docs/architecture.md +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/docs/development.md +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/docs/limits-and-errors.md +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/examples/demo.py +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/examples/demo_async_document.py +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/examples/demo_asyncio.py +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/examples/demo_prettify_url.py +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/py.typed +0 -0
- /scraper_rust-0.5.1/scraper_rs.pyi → /scraper_rust-0.7.0/scraper_rs/__init__.pyi +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/scraper_rs/asyncio.pyi +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/cache.rs +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/functions.rs +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/limits.rs +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/src/runtime.rs +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/tests/test_asyncio.py +0 -0
- {scraper_rust-0.5.1 → scraper_rust-0.7.0}/tests/test_performance.py +0 -0
|
@@ -82,6 +82,7 @@ jobs:
|
|
|
82
82
|
with:
|
|
83
83
|
name: wheels-${{ matrix.name }}
|
|
84
84
|
path: dist/*.whl
|
|
85
|
+
if-no-files-found: error
|
|
85
86
|
|
|
86
87
|
- name: Upload wheels to release
|
|
87
88
|
uses: softprops/action-gh-release@v2
|
|
@@ -91,71 +92,6 @@ jobs:
|
|
|
91
92
|
env:
|
|
92
93
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
93
94
|
|
|
94
|
-
build-free-threaded-wheels:
|
|
95
|
-
name: Build free-threaded wheels on ${{ matrix.name }} (Python ${{ matrix.python-version }})
|
|
96
|
-
runs-on: ${{ matrix.runner }}
|
|
97
|
-
strategy:
|
|
98
|
-
fail-fast: false
|
|
99
|
-
matrix:
|
|
100
|
-
python-version: ['3.13t', '3.14t']
|
|
101
|
-
include:
|
|
102
|
-
- name: linux-x86_64
|
|
103
|
-
runner: ubuntu-latest
|
|
104
|
-
platform: linux
|
|
105
|
-
- name: linux-arm64
|
|
106
|
-
runner: ubuntu-24.04-arm
|
|
107
|
-
platform: linux
|
|
108
|
-
- name: windows
|
|
109
|
-
runner: windows-latest
|
|
110
|
-
platform: windows
|
|
111
|
-
- name: macos
|
|
112
|
-
runner: macos-latest
|
|
113
|
-
platform: macos
|
|
114
|
-
|
|
115
|
-
steps:
|
|
116
|
-
- name: Checkout repository
|
|
117
|
-
uses: actions/checkout@v6
|
|
118
|
-
|
|
119
|
-
- name: Set up Python
|
|
120
|
-
uses: actions/setup-python@v6
|
|
121
|
-
with:
|
|
122
|
-
python-version: ${{ matrix.python-version }}
|
|
123
|
-
allow-prereleases: true
|
|
124
|
-
|
|
125
|
-
- name: Install zig (for Linux manylinux builds)
|
|
126
|
-
if: matrix.platform == 'linux'
|
|
127
|
-
uses: goto-bus-stop/setup-zig@v2
|
|
128
|
-
with:
|
|
129
|
-
version: 0.11.0
|
|
130
|
-
|
|
131
|
-
- name: Build free-threaded wheels (Linux - using zig for manylinux)
|
|
132
|
-
if: matrix.platform == 'linux'
|
|
133
|
-
uses: PyO3/maturin-action@v1
|
|
134
|
-
with:
|
|
135
|
-
command: build
|
|
136
|
-
args: --release --strip --out dist --zig --interpreter python
|
|
137
|
-
|
|
138
|
-
- name: Build free-threaded wheels (Windows/macOS)
|
|
139
|
-
if: matrix.platform == 'windows' || matrix.platform == 'macos'
|
|
140
|
-
uses: PyO3/maturin-action@v1
|
|
141
|
-
with:
|
|
142
|
-
command: build
|
|
143
|
-
args: --release --strip --out dist
|
|
144
|
-
|
|
145
|
-
- name: Upload free-threaded wheels as artifacts
|
|
146
|
-
uses: actions/upload-artifact@v7
|
|
147
|
-
with:
|
|
148
|
-
name: wheels-free-threaded-${{ matrix.name }}-${{ matrix.python-version }}
|
|
149
|
-
path: dist/*.whl
|
|
150
|
-
|
|
151
|
-
- name: Upload free-threaded wheels to release
|
|
152
|
-
uses: softprops/action-gh-release@v2
|
|
153
|
-
with:
|
|
154
|
-
files: dist/*.whl
|
|
155
|
-
fail_on_unmatched_files: true
|
|
156
|
-
env:
|
|
157
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
158
|
-
|
|
159
95
|
build-sdist:
|
|
160
96
|
name: Build source distribution
|
|
161
97
|
runs-on: ubuntu-latest
|
|
@@ -174,6 +110,7 @@ jobs:
|
|
|
174
110
|
with:
|
|
175
111
|
name: sdist
|
|
176
112
|
path: dist/*.tar.gz
|
|
113
|
+
if-no-files-found: error
|
|
177
114
|
|
|
178
115
|
- name: Upload sdist to release
|
|
179
116
|
uses: softprops/action-gh-release@v2
|
|
@@ -186,7 +123,7 @@ jobs:
|
|
|
186
123
|
publish-to-pypi:
|
|
187
124
|
name: Publish to PyPI
|
|
188
125
|
runs-on: ubuntu-latest
|
|
189
|
-
needs: [build-wheels, build-
|
|
126
|
+
needs: [build-wheels, build-sdist]
|
|
190
127
|
environment:
|
|
191
128
|
name: pypi
|
|
192
129
|
url: https://pypi.org/p/scraper-rust
|
|
@@ -208,14 +145,48 @@ jobs:
|
|
|
208
145
|
pattern: 'sdist'
|
|
209
146
|
merge-multiple: true
|
|
210
147
|
|
|
211
|
-
- name:
|
|
148
|
+
- name: Validate distributions
|
|
212
149
|
run: |
|
|
213
|
-
echo "Files in dist directory:"
|
|
214
150
|
ls -lah dist/
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
151
|
+
python - <<'PY'
|
|
152
|
+
from pathlib import Path
|
|
153
|
+
import struct
|
|
154
|
+
import tarfile
|
|
155
|
+
import zipfile
|
|
156
|
+
|
|
157
|
+
dist = Path("dist")
|
|
158
|
+
wheels = sorted(dist.glob("*.whl"))
|
|
159
|
+
sdists = sorted(dist.glob("*.tar.gz"))
|
|
160
|
+
|
|
161
|
+
if len(wheels) != 4:
|
|
162
|
+
raise SystemExit(f"Expected 4 platform wheels, found {len(wheels)}")
|
|
163
|
+
if len(sdists) != 1:
|
|
164
|
+
raise SystemExit(f"Expected 1 source distribution, found {len(sdists)}")
|
|
165
|
+
|
|
166
|
+
for wheel in wheels:
|
|
167
|
+
data = wheel.read_bytes()
|
|
168
|
+
eocd = data.rfind(b"PK\x05\x06")
|
|
169
|
+
if eocd < 0 or eocd + 22 > len(data):
|
|
170
|
+
raise SystemExit(f"{wheel}: missing or truncated ZIP end record")
|
|
171
|
+
|
|
172
|
+
comment_length = struct.unpack_from("<H", data, eocd + 20)[0]
|
|
173
|
+
archive_end = eocd + 22 + comment_length
|
|
174
|
+
if comment_length:
|
|
175
|
+
raise SystemExit(f"{wheel}: ZIP comments are not accepted by PyPI")
|
|
176
|
+
if archive_end != len(data):
|
|
177
|
+
trailing = len(data) - archive_end
|
|
178
|
+
raise SystemExit(f"{wheel}: contains {trailing} trailing bytes")
|
|
179
|
+
|
|
180
|
+
with zipfile.ZipFile(wheel) as archive:
|
|
181
|
+
bad_member = archive.testzip()
|
|
182
|
+
if bad_member is not None:
|
|
183
|
+
raise SystemExit(f"{wheel}: corrupt member {bad_member}")
|
|
184
|
+
|
|
185
|
+
with tarfile.open(sdists[0], mode="r:gz") as archive:
|
|
186
|
+
archive.getmembers()
|
|
187
|
+
|
|
188
|
+
print(f"Validated {len(wheels)} wheels and {len(sdists)} source distribution")
|
|
189
|
+
PY
|
|
219
190
|
|
|
220
191
|
- name: Publish to PyPI
|
|
221
192
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -16,7 +16,8 @@ jobs:
|
|
|
16
16
|
strategy:
|
|
17
17
|
fail-fast: false
|
|
18
18
|
matrix:
|
|
19
|
-
|
|
19
|
+
# PyO3 0.29 supports free-threaded CPython starting with 3.14.
|
|
20
|
+
python-version: ${{ fromJSON(github.event.inputs.python-version != '' && format('["{0}"]', github.event.inputs.python-version) || '["3.10","3.11","3.12","3.13","3.14","3.14t"]') }}
|
|
20
21
|
|
|
21
22
|
steps:
|
|
22
23
|
- name: Checkout repository
|
|
@@ -44,11 +45,7 @@ jobs:
|
|
|
44
45
|
- name: Build extension module
|
|
45
46
|
run: |
|
|
46
47
|
source .venv/bin/activate
|
|
47
|
-
|
|
48
|
-
maturin develop --release --locked
|
|
49
|
-
else
|
|
50
|
-
maturin develop --release --locked
|
|
51
|
-
fi
|
|
48
|
+
maturin develop --release --locked
|
|
52
49
|
|
|
53
50
|
- name: Run tests
|
|
54
51
|
run: |
|
|
@@ -4,7 +4,7 @@ This file contains essential information for Large Language Models (LLMs) to eff
|
|
|
4
4
|
|
|
5
5
|
## Project Overview
|
|
6
6
|
|
|
7
|
-
**scraper-rs** is a Python library providing high-performance HTML parsing and CSS/XPath selection through Python bindings to
|
|
7
|
+
**scraper-rs** is a Python library providing high-performance HTML parsing and CSS/XPath selection through Python bindings to Rust HTML tooling. It uses PyO3 to create Python bindings for Rust code, offering zero Python-side parsing overhead.
|
|
8
8
|
|
|
9
9
|
- **Repository**: https://github.com/RustedBytes/scraper-rs
|
|
10
10
|
- **Package Name**: `scraper-rust` (PyPI)
|
|
@@ -17,14 +17,13 @@ This file contains essential information for Large Language Models (LLMs) to eff
|
|
|
17
17
|
|
|
18
18
|
### Core Technologies
|
|
19
19
|
- **Rust** (2024 edition)
|
|
20
|
-
- `scraper` v0.27.0 from `rust-scraper/scraper` with the `atomic` feature - HTML parsing and CSS selection
|
|
21
20
|
- `xee-xpath` v0.1.5 - XPath support
|
|
22
|
-
- `
|
|
21
|
+
- `rustedbytes-tl` v0.2.0 with `std` feature - HTML parsing, CSS selection, and parse-tree dictionary helpers
|
|
23
22
|
- `pyo3` v0.28.3 with `abi3-py310`; the `extension-module` feature is enabled through the package build config
|
|
24
23
|
- `pyo3-async-runtimes` v0.28 with `tokio-runtime` feature
|
|
25
24
|
- `tokio` v1 (rt, macros) for async wrappers
|
|
26
25
|
- **Python** 3.10+
|
|
27
|
-
- Type annotations and stub files (`
|
|
26
|
+
- Type annotations and stub files (`__init__.pyi`, `py.typed`)
|
|
28
27
|
- Async support via `asyncio` module
|
|
29
28
|
- **Build System**
|
|
30
29
|
- `maturin` - Build PyO3 wheels
|
|
@@ -60,8 +59,9 @@ scraper-rs/
|
|
|
60
59
|
│ ├── demo_asyncio.py # Async usage examples
|
|
61
60
|
│ ├── demo_async_document.py
|
|
62
61
|
│ └── demo_prettify_url.py
|
|
63
|
-
├── scraper_rs/
|
|
64
|
-
├──
|
|
62
|
+
├── scraper_rs/
|
|
63
|
+
│ ├── __init__.pyi # Type stub file
|
|
64
|
+
│ └── asyncio.pyi # async wrappers
|
|
65
65
|
├── py.typed # PEP 561 marker for type support
|
|
66
66
|
├── Cargo.toml # Rust package configuration
|
|
67
67
|
├── pyproject.toml # Python package configuration
|
|
@@ -78,7 +78,7 @@ scraper-rs/
|
|
|
78
78
|
- **`src/functions.rs`**: Top-level helpers: `parse()`, `parse_document()`, `parse_fragment()`, `prettify()`, `select()`, `select_first()`, `first()`, `xpath()`, `xpath_first()`
|
|
79
79
|
- **`src/selectors.rs`**, **`src/xpath.rs`**, **`src/limits.rs`**: selector caches, XPath conversion, size limit enforcement, and UTF-8-safe truncation
|
|
80
80
|
|
|
81
|
-
- **`
|
|
81
|
+
- **`__init__.pyi`**: Type stubs for IDE support and type checking
|
|
82
82
|
|
|
83
83
|
- **`Cargo.toml`**: Rust crate configuration
|
|
84
84
|
- `crate-type = ["cdylib"]` for Python extension module
|
|
@@ -241,7 +241,7 @@ Module: `scraper_rs.asyncio`
|
|
|
241
241
|
- Release build optimizations in `Cargo.toml` for smaller binary size
|
|
242
242
|
|
|
243
243
|
### Python Code
|
|
244
|
-
- Type annotations required (uses `
|
|
244
|
+
- Type annotations required (uses `__init__.pyi` stub file)
|
|
245
245
|
- Follow PEP 8 style guidelines
|
|
246
246
|
- Use descriptive variable names
|
|
247
247
|
- Include docstrings for public functions
|
|
@@ -259,7 +259,7 @@ Module: `scraper_rs.asyncio`
|
|
|
259
259
|
### Adding a New Method to Document
|
|
260
260
|
1. Add Rust implementation in `src/document.rs` under `impl Document`
|
|
261
261
|
2. Add `#[pymethods]` annotation if needed
|
|
262
|
-
3. Update `
|
|
262
|
+
3. Update `__init__.pyi` with type signature
|
|
263
263
|
4. Add tests in `tests/test_scraper.py`
|
|
264
264
|
5. If it should be async, update `scraper_rs/asyncio.py` and `scraper_rs/asyncio.pyi`
|
|
265
265
|
6. Update README.md with usage example
|
|
@@ -270,7 +270,7 @@ Module: `scraper_rs.asyncio`
|
|
|
270
270
|
1. Add Rust function in `src/functions.rs` with `#[pyfunction]`
|
|
271
271
|
2. Register with `m.add_function(wrap_pyfunction!(function_name, m)?)?` in `scraper_rs` module
|
|
272
272
|
3. Export in `__all__` if applicable
|
|
273
|
-
4. Update `
|
|
273
|
+
4. Update `__init__.pyi`
|
|
274
274
|
5. Add async wrapper in `scraper_rs/asyncio.py` and `scraper_rs/asyncio.pyi` if needed
|
|
275
275
|
6. Add tests
|
|
276
276
|
7. Update documentation
|
|
@@ -297,7 +297,7 @@ See `.github/workflows/release.yml` and `.github/workflows/bump-version.yml`
|
|
|
297
297
|
4. **UTF-8 safety**: Truncation must respect character boundaries
|
|
298
298
|
5. **Async wrappers**: Public async wrappers live in `scraper_rs/asyncio.py`; keep their stubs and tests aligned with sync behavior
|
|
299
299
|
6. **Rust modules are split by responsibility**: `src/lib.rs` is the module entry point; add core behavior to the focused module that owns it
|
|
300
|
-
7. **Type stubs are critical**: Always update `
|
|
300
|
+
7. **Type stubs are critical**: Always update `__init__.pyi` when changing APIs
|
|
301
301
|
8. **Tests are comprehensive**: Follow existing test patterns in `tests/`
|
|
302
302
|
9. **Build artifacts**: `target/` and `.venv/` should not be committed
|
|
303
303
|
10. **Use `just` commands**: They handle common workflows correctly
|
|
@@ -308,9 +308,8 @@ See `.github/workflows/release.yml` and `.github/workflows/bump-version.yml`
|
|
|
308
308
|
- `pyo3 = { version = "0.28.3", features = ["abi3-py310"] }`
|
|
309
309
|
- `pyo3-async-runtimes = { version = "0.28", features = ["tokio-runtime"] }`
|
|
310
310
|
- `tokio = { version = "1", features = ["rt", "macros"] }`
|
|
311
|
-
- `scraper = { version = "0.27.0", git = "https://github.com/rust-scraper/scraper", features = ["atomic"], rev = "9c1eff304e45a8bccb463968268ce6758703e821" }`
|
|
312
311
|
- `xee-xpath = "0.1.5"`
|
|
313
|
-
- `
|
|
312
|
+
- `rustedbytes-tl = { version = "0.2.0", features = ["std"] }`
|
|
314
313
|
|
|
315
314
|
### Python Dependencies (pyproject.toml)
|
|
316
315
|
**Runtime**: None (self-contained binary wheel)
|
|
@@ -339,7 +338,7 @@ See `.github/workflows/release.yml` and `.github/workflows/bump-version.yml`
|
|
|
339
338
|
## Resources
|
|
340
339
|
|
|
341
340
|
- **PyO3 Documentation**: https://pyo3.rs/
|
|
342
|
-
- **
|
|
341
|
+
- **rustedbytes-tl crate**: https://crates.io/crates/rustedbytes-tl
|
|
343
342
|
- **Maturin Guide**: https://www.maturin.rs/
|
|
344
343
|
- **Project README**: `README.md`
|
|
345
344
|
- **Example Usage**: `examples/demo.py`
|