scraper-rust 0.6.0__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.6.0 → scraper_rust-0.7.0}/.github/workflows/release.yml +43 -72
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/.github/workflows/tests.yml +3 -6
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/AGENTS.md +9 -8
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/Cargo.lock +53 -54
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/Cargo.toml +4 -4
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/PKG-INFO +16 -5
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/README.md +15 -4
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/docs/api.md +1 -1
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/docs/async-api.md +2 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/pyproject.toml +1 -1
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/scraper_rs/asyncio.py +11 -11
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/prettify.rs +42 -4
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/tests.rs +4 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/xpath.rs +8 -12
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/uv.lock +41 -41
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/.github/workflows/benchmark.yml +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/.github/workflows/bump-version.yml +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/.gitignore +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/LICENSE +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/benchmarks/README.md +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/benchmarks/common.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/benchmarks/parse_scaling.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/benchmarks/sync_async.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/build.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/docs/README.md +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/docs/architecture.md +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/docs/development.md +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/docs/limits-and-errors.md +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/examples/demo.py +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/examples/demo_async_document.py +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/examples/demo_asyncio.py +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/examples/demo_prettify_url.py +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/justfile +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/py.typed +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/scraper_rs/__init__.py +0 -0
- /scraper_rust-0.6.0/scraper_rs.pyi → /scraper_rust-0.7.0/scraper_rs/__init__.pyi +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/scraper_rs/asyncio.pyi +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/async_core.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/cache.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/document.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/element.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/functions.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/html5_dict.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/lib.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/limits.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/runtime.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/selectors.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/text.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/src/tl_dom.rs +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/tests/test_asyncio.py +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/tests/test_performance.py +0 -0
- {scraper_rust-0.6.0 → scraper_rust-0.7.0}/tests/test_scraper.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: |
|
|
@@ -23,7 +23,7 @@ This file contains essential information for Large Language Models (LLMs) to eff
|
|
|
23
23
|
- `pyo3-async-runtimes` v0.28 with `tokio-runtime` feature
|
|
24
24
|
- `tokio` v1 (rt, macros) for async wrappers
|
|
25
25
|
- **Python** 3.10+
|
|
26
|
-
- Type annotations and stub files (`
|
|
26
|
+
- Type annotations and stub files (`__init__.pyi`, `py.typed`)
|
|
27
27
|
- Async support via `asyncio` module
|
|
28
28
|
- **Build System**
|
|
29
29
|
- `maturin` - Build PyO3 wheels
|
|
@@ -59,8 +59,9 @@ scraper-rs/
|
|
|
59
59
|
│ ├── demo_asyncio.py # Async usage examples
|
|
60
60
|
│ ├── demo_async_document.py
|
|
61
61
|
│ └── demo_prettify_url.py
|
|
62
|
-
├── scraper_rs/
|
|
63
|
-
├──
|
|
62
|
+
├── scraper_rs/
|
|
63
|
+
│ ├── __init__.pyi # Type stub file
|
|
64
|
+
│ └── asyncio.pyi # async wrappers
|
|
64
65
|
├── py.typed # PEP 561 marker for type support
|
|
65
66
|
├── Cargo.toml # Rust package configuration
|
|
66
67
|
├── pyproject.toml # Python package configuration
|
|
@@ -77,7 +78,7 @@ scraper-rs/
|
|
|
77
78
|
- **`src/functions.rs`**: Top-level helpers: `parse()`, `parse_document()`, `parse_fragment()`, `prettify()`, `select()`, `select_first()`, `first()`, `xpath()`, `xpath_first()`
|
|
78
79
|
- **`src/selectors.rs`**, **`src/xpath.rs`**, **`src/limits.rs`**: selector caches, XPath conversion, size limit enforcement, and UTF-8-safe truncation
|
|
79
80
|
|
|
80
|
-
- **`
|
|
81
|
+
- **`__init__.pyi`**: Type stubs for IDE support and type checking
|
|
81
82
|
|
|
82
83
|
- **`Cargo.toml`**: Rust crate configuration
|
|
83
84
|
- `crate-type = ["cdylib"]` for Python extension module
|
|
@@ -240,7 +241,7 @@ Module: `scraper_rs.asyncio`
|
|
|
240
241
|
- Release build optimizations in `Cargo.toml` for smaller binary size
|
|
241
242
|
|
|
242
243
|
### Python Code
|
|
243
|
-
- Type annotations required (uses `
|
|
244
|
+
- Type annotations required (uses `__init__.pyi` stub file)
|
|
244
245
|
- Follow PEP 8 style guidelines
|
|
245
246
|
- Use descriptive variable names
|
|
246
247
|
- Include docstrings for public functions
|
|
@@ -258,7 +259,7 @@ Module: `scraper_rs.asyncio`
|
|
|
258
259
|
### Adding a New Method to Document
|
|
259
260
|
1. Add Rust implementation in `src/document.rs` under `impl Document`
|
|
260
261
|
2. Add `#[pymethods]` annotation if needed
|
|
261
|
-
3. Update `
|
|
262
|
+
3. Update `__init__.pyi` with type signature
|
|
262
263
|
4. Add tests in `tests/test_scraper.py`
|
|
263
264
|
5. If it should be async, update `scraper_rs/asyncio.py` and `scraper_rs/asyncio.pyi`
|
|
264
265
|
6. Update README.md with usage example
|
|
@@ -269,7 +270,7 @@ Module: `scraper_rs.asyncio`
|
|
|
269
270
|
1. Add Rust function in `src/functions.rs` with `#[pyfunction]`
|
|
270
271
|
2. Register with `m.add_function(wrap_pyfunction!(function_name, m)?)?` in `scraper_rs` module
|
|
271
272
|
3. Export in `__all__` if applicable
|
|
272
|
-
4. Update `
|
|
273
|
+
4. Update `__init__.pyi`
|
|
273
274
|
5. Add async wrapper in `scraper_rs/asyncio.py` and `scraper_rs/asyncio.pyi` if needed
|
|
274
275
|
6. Add tests
|
|
275
276
|
7. Update documentation
|
|
@@ -296,7 +297,7 @@ See `.github/workflows/release.yml` and `.github/workflows/bump-version.yml`
|
|
|
296
297
|
4. **UTF-8 safety**: Truncation must respect character boundaries
|
|
297
298
|
5. **Async wrappers**: Public async wrappers live in `scraper_rs/asyncio.py`; keep their stubs and tests aligned with sync behavior
|
|
298
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
|
|
299
|
-
7. **Type stubs are critical**: Always update `
|
|
300
|
+
7. **Type stubs are critical**: Always update `__init__.pyi` when changing APIs
|
|
300
301
|
8. **Tests are comprehensive**: Follow existing test patterns in `tests/`
|
|
301
302
|
9. **Build artifacts**: `target/` and `.venv/` should not be committed
|
|
302
303
|
10. **Use `just` commands**: They handle common workflows correctly
|
|
@@ -124,7 +124,7 @@ checksum = "56791e4bd64c99fc361e01008f45c984baa93f12a0957d1b3c51dd2c6baab453"
|
|
|
124
124
|
dependencies = [
|
|
125
125
|
"proc-macro2",
|
|
126
126
|
"quote",
|
|
127
|
-
"syn 2.0.
|
|
127
|
+
"syn 2.0.119",
|
|
128
128
|
]
|
|
129
129
|
|
|
130
130
|
[[package]]
|
|
@@ -148,7 +148,7 @@ dependencies = [
|
|
|
148
148
|
"proc-macro-crate",
|
|
149
149
|
"proc-macro2",
|
|
150
150
|
"quote",
|
|
151
|
-
"syn 2.0.
|
|
151
|
+
"syn 2.0.119",
|
|
152
152
|
]
|
|
153
153
|
|
|
154
154
|
[[package]]
|
|
@@ -392,7 +392,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
|
|
|
392
392
|
dependencies = [
|
|
393
393
|
"proc-macro2",
|
|
394
394
|
"quote",
|
|
395
|
-
"syn 2.0.
|
|
395
|
+
"syn 2.0.119",
|
|
396
396
|
]
|
|
397
397
|
|
|
398
398
|
[[package]]
|
|
@@ -419,7 +419,7 @@ dependencies = [
|
|
|
419
419
|
"once_cell",
|
|
420
420
|
"proc-macro2",
|
|
421
421
|
"quote",
|
|
422
|
-
"syn 2.0.
|
|
422
|
+
"syn 2.0.119",
|
|
423
423
|
]
|
|
424
424
|
|
|
425
425
|
[[package]]
|
|
@@ -465,41 +465,41 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
|
|
465
465
|
|
|
466
466
|
[[package]]
|
|
467
467
|
name = "futures-channel"
|
|
468
|
-
version = "0.3.
|
|
468
|
+
version = "0.3.33"
|
|
469
469
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
470
|
-
checksum = "
|
|
470
|
+
checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
|
|
471
471
|
dependencies = [
|
|
472
472
|
"futures-core",
|
|
473
473
|
]
|
|
474
474
|
|
|
475
475
|
[[package]]
|
|
476
476
|
name = "futures-core"
|
|
477
|
-
version = "0.3.
|
|
477
|
+
version = "0.3.33"
|
|
478
478
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
479
|
-
checksum = "
|
|
479
|
+
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
|
|
480
480
|
|
|
481
481
|
[[package]]
|
|
482
482
|
name = "futures-macro"
|
|
483
|
-
version = "0.3.
|
|
483
|
+
version = "0.3.33"
|
|
484
484
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
485
|
-
checksum = "
|
|
485
|
+
checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
|
|
486
486
|
dependencies = [
|
|
487
487
|
"proc-macro2",
|
|
488
488
|
"quote",
|
|
489
|
-
"syn 2.0.
|
|
489
|
+
"syn 2.0.119",
|
|
490
490
|
]
|
|
491
491
|
|
|
492
492
|
[[package]]
|
|
493
493
|
name = "futures-task"
|
|
494
|
-
version = "0.3.
|
|
494
|
+
version = "0.3.33"
|
|
495
495
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
496
|
-
checksum = "
|
|
496
|
+
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
|
|
497
497
|
|
|
498
498
|
[[package]]
|
|
499
499
|
name = "futures-util"
|
|
500
|
-
version = "0.3.
|
|
500
|
+
version = "0.3.33"
|
|
501
501
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
502
|
-
checksum = "
|
|
502
|
+
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
|
|
503
503
|
dependencies = [
|
|
504
504
|
"futures-core",
|
|
505
505
|
"futures-macro",
|
|
@@ -1006,7 +1006,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
|
|
1006
1006
|
dependencies = [
|
|
1007
1007
|
"proc-macro2",
|
|
1008
1008
|
"quote",
|
|
1009
|
-
"syn 2.0.
|
|
1009
|
+
"syn 2.0.119",
|
|
1010
1010
|
]
|
|
1011
1011
|
|
|
1012
1012
|
[[package]]
|
|
@@ -1082,7 +1082,7 @@ dependencies = [
|
|
|
1082
1082
|
"proc-macro2",
|
|
1083
1083
|
"quote",
|
|
1084
1084
|
"strum",
|
|
1085
|
-
"syn 2.0.
|
|
1085
|
+
"syn 2.0.119",
|
|
1086
1086
|
"thiserror",
|
|
1087
1087
|
]
|
|
1088
1088
|
|
|
@@ -1256,7 +1256,7 @@ dependencies = [
|
|
|
1256
1256
|
"quote",
|
|
1257
1257
|
"regex-syntax 0.8.10",
|
|
1258
1258
|
"rustc_version",
|
|
1259
|
-
"syn 2.0.
|
|
1259
|
+
"syn 2.0.119",
|
|
1260
1260
|
]
|
|
1261
1261
|
|
|
1262
1262
|
[[package]]
|
|
@@ -1336,7 +1336,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
|
|
1336
1336
|
dependencies = [
|
|
1337
1337
|
"proc-macro2",
|
|
1338
1338
|
"quote",
|
|
1339
|
-
"syn 2.0.
|
|
1339
|
+
"syn 2.0.119",
|
|
1340
1340
|
]
|
|
1341
1341
|
|
|
1342
1342
|
[[package]]
|
|
@@ -1556,9 +1556,9 @@ dependencies = [
|
|
|
1556
1556
|
|
|
1557
1557
|
[[package]]
|
|
1558
1558
|
name = "pyo3"
|
|
1559
|
-
version = "0.
|
|
1559
|
+
version = "0.29.0"
|
|
1560
1560
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1561
|
-
checksum = "
|
|
1561
|
+
checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
|
|
1562
1562
|
dependencies = [
|
|
1563
1563
|
"libc",
|
|
1564
1564
|
"once_cell",
|
|
@@ -1570,9 +1570,9 @@ dependencies = [
|
|
|
1570
1570
|
|
|
1571
1571
|
[[package]]
|
|
1572
1572
|
name = "pyo3-async-runtimes"
|
|
1573
|
-
version = "0.
|
|
1573
|
+
version = "0.29.0"
|
|
1574
1574
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1575
|
-
checksum = "
|
|
1575
|
+
checksum = "b3ef68daa7316a3fac65e5e18b2203f010346de1c1c53456811a2624673ab046"
|
|
1576
1576
|
dependencies = [
|
|
1577
1577
|
"futures-channel",
|
|
1578
1578
|
"futures-util",
|
|
@@ -1584,18 +1584,18 @@ dependencies = [
|
|
|
1584
1584
|
|
|
1585
1585
|
[[package]]
|
|
1586
1586
|
name = "pyo3-build-config"
|
|
1587
|
-
version = "0.
|
|
1587
|
+
version = "0.29.0"
|
|
1588
1588
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1589
|
-
checksum = "
|
|
1589
|
+
checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
|
|
1590
1590
|
dependencies = [
|
|
1591
1591
|
"target-lexicon",
|
|
1592
1592
|
]
|
|
1593
1593
|
|
|
1594
1594
|
[[package]]
|
|
1595
1595
|
name = "pyo3-ffi"
|
|
1596
|
-
version = "0.
|
|
1596
|
+
version = "0.29.0"
|
|
1597
1597
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1598
|
-
checksum = "
|
|
1598
|
+
checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
|
|
1599
1599
|
dependencies = [
|
|
1600
1600
|
"libc",
|
|
1601
1601
|
"pyo3-build-config",
|
|
@@ -1603,34 +1603,33 @@ dependencies = [
|
|
|
1603
1603
|
|
|
1604
1604
|
[[package]]
|
|
1605
1605
|
name = "pyo3-macros"
|
|
1606
|
-
version = "0.
|
|
1606
|
+
version = "0.29.0"
|
|
1607
1607
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1608
|
-
checksum = "
|
|
1608
|
+
checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
|
|
1609
1609
|
dependencies = [
|
|
1610
1610
|
"proc-macro2",
|
|
1611
1611
|
"pyo3-macros-backend",
|
|
1612
1612
|
"quote",
|
|
1613
|
-
"syn 2.0.
|
|
1613
|
+
"syn 2.0.119",
|
|
1614
1614
|
]
|
|
1615
1615
|
|
|
1616
1616
|
[[package]]
|
|
1617
1617
|
name = "pyo3-macros-backend"
|
|
1618
|
-
version = "0.
|
|
1618
|
+
version = "0.29.0"
|
|
1619
1619
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1620
|
-
checksum = "
|
|
1620
|
+
checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
|
|
1621
1621
|
dependencies = [
|
|
1622
1622
|
"heck",
|
|
1623
1623
|
"proc-macro2",
|
|
1624
|
-
"pyo3-build-config",
|
|
1625
1624
|
"quote",
|
|
1626
|
-
"syn 2.0.
|
|
1625
|
+
"syn 2.0.119",
|
|
1627
1626
|
]
|
|
1628
1627
|
|
|
1629
1628
|
[[package]]
|
|
1630
1629
|
name = "quote"
|
|
1631
|
-
version = "1.0.
|
|
1630
|
+
version = "1.0.46"
|
|
1632
1631
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1633
|
-
checksum = "
|
|
1632
|
+
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
|
1634
1633
|
dependencies = [
|
|
1635
1634
|
"proc-macro2",
|
|
1636
1635
|
]
|
|
@@ -1864,7 +1863,7 @@ dependencies = [
|
|
|
1864
1863
|
|
|
1865
1864
|
[[package]]
|
|
1866
1865
|
name = "scraper-rs"
|
|
1867
|
-
version = "0.
|
|
1866
|
+
version = "0.7.0"
|
|
1868
1867
|
dependencies = [
|
|
1869
1868
|
"criterion",
|
|
1870
1869
|
"pyo3",
|
|
@@ -1915,7 +1914,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
|
1915
1914
|
dependencies = [
|
|
1916
1915
|
"proc-macro2",
|
|
1917
1916
|
"quote",
|
|
1918
|
-
"syn 2.0.
|
|
1917
|
+
"syn 2.0.119",
|
|
1919
1918
|
]
|
|
1920
1919
|
|
|
1921
1920
|
[[package]]
|
|
@@ -1998,7 +1997,7 @@ dependencies = [
|
|
|
1998
1997
|
"heck",
|
|
1999
1998
|
"proc-macro2",
|
|
2000
1999
|
"quote",
|
|
2001
|
-
"syn 2.0.
|
|
2000
|
+
"syn 2.0.119",
|
|
2002
2001
|
]
|
|
2003
2002
|
|
|
2004
2003
|
[[package]]
|
|
@@ -2014,9 +2013,9 @@ dependencies = [
|
|
|
2014
2013
|
|
|
2015
2014
|
[[package]]
|
|
2016
2015
|
name = "syn"
|
|
2017
|
-
version = "2.0.
|
|
2016
|
+
version = "2.0.119"
|
|
2018
2017
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2019
|
-
checksum = "
|
|
2018
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
2020
2019
|
dependencies = [
|
|
2021
2020
|
"proc-macro2",
|
|
2022
2021
|
"quote",
|
|
@@ -2042,7 +2041,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
|
|
2042
2041
|
dependencies = [
|
|
2043
2042
|
"proc-macro2",
|
|
2044
2043
|
"quote",
|
|
2045
|
-
"syn 2.0.
|
|
2044
|
+
"syn 2.0.119",
|
|
2046
2045
|
]
|
|
2047
2046
|
|
|
2048
2047
|
[[package]]
|
|
@@ -2074,7 +2073,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
|
|
2074
2073
|
dependencies = [
|
|
2075
2074
|
"proc-macro2",
|
|
2076
2075
|
"quote",
|
|
2077
|
-
"syn 2.0.
|
|
2076
|
+
"syn 2.0.119",
|
|
2078
2077
|
]
|
|
2079
2078
|
|
|
2080
2079
|
[[package]]
|
|
@@ -2114,9 +2113,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|
|
2114
2113
|
|
|
2115
2114
|
[[package]]
|
|
2116
2115
|
name = "tokio"
|
|
2117
|
-
version = "1.
|
|
2116
|
+
version = "1.53.0"
|
|
2118
2117
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2119
|
-
checksum = "
|
|
2118
|
+
checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee"
|
|
2120
2119
|
dependencies = [
|
|
2121
2120
|
"pin-project-lite",
|
|
2122
2121
|
"tokio-macros",
|
|
@@ -2130,7 +2129,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
|
|
2130
2129
|
dependencies = [
|
|
2131
2130
|
"proc-macro2",
|
|
2132
2131
|
"quote",
|
|
2133
|
-
"syn 2.0.
|
|
2132
|
+
"syn 2.0.119",
|
|
2134
2133
|
]
|
|
2135
2134
|
|
|
2136
2135
|
[[package]]
|
|
@@ -2273,7 +2272,7 @@ dependencies = [
|
|
|
2273
2272
|
"bumpalo",
|
|
2274
2273
|
"proc-macro2",
|
|
2275
2274
|
"quote",
|
|
2276
|
-
"syn 2.0.
|
|
2275
|
+
"syn 2.0.119",
|
|
2277
2276
|
"wasm-bindgen-shared",
|
|
2278
2277
|
]
|
|
2279
2278
|
|
|
@@ -2348,7 +2347,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
|
|
2348
2347
|
dependencies = [
|
|
2349
2348
|
"proc-macro2",
|
|
2350
2349
|
"quote",
|
|
2351
|
-
"syn 2.0.
|
|
2350
|
+
"syn 2.0.119",
|
|
2352
2351
|
]
|
|
2353
2352
|
|
|
2354
2353
|
[[package]]
|
|
@@ -2359,7 +2358,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
|
|
2359
2358
|
dependencies = [
|
|
2360
2359
|
"proc-macro2",
|
|
2361
2360
|
"quote",
|
|
2362
|
-
"syn 2.0.
|
|
2361
|
+
"syn 2.0.119",
|
|
2363
2362
|
]
|
|
2364
2363
|
|
|
2365
2364
|
[[package]]
|
|
@@ -2589,7 +2588,7 @@ checksum = "30d233fc2ef8ab1a9f9bf7af9cc1c7f7e7d75eb9848f731a99d8f1d4c2259fe3"
|
|
|
2589
2588
|
dependencies = [
|
|
2590
2589
|
"proc-macro2",
|
|
2591
2590
|
"quote",
|
|
2592
|
-
"syn 2.0.
|
|
2591
|
+
"syn 2.0.119",
|
|
2593
2592
|
"xee-schema-type",
|
|
2594
2593
|
"xee-xpath-ast",
|
|
2595
2594
|
"xot",
|
|
@@ -2652,7 +2651,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
|
|
2652
2651
|
dependencies = [
|
|
2653
2652
|
"proc-macro2",
|
|
2654
2653
|
"quote",
|
|
2655
|
-
"syn 2.0.
|
|
2654
|
+
"syn 2.0.119",
|
|
2656
2655
|
"synstructure",
|
|
2657
2656
|
]
|
|
2658
2657
|
|
|
@@ -2673,7 +2672,7 @@ checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
|
|
|
2673
2672
|
dependencies = [
|
|
2674
2673
|
"proc-macro2",
|
|
2675
2674
|
"quote",
|
|
2676
|
-
"syn 2.0.
|
|
2675
|
+
"syn 2.0.119",
|
|
2677
2676
|
]
|
|
2678
2677
|
|
|
2679
2678
|
[[package]]
|
|
@@ -2693,7 +2692,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
|
|
|
2693
2692
|
dependencies = [
|
|
2694
2693
|
"proc-macro2",
|
|
2695
2694
|
"quote",
|
|
2696
|
-
"syn 2.0.
|
|
2695
|
+
"syn 2.0.119",
|
|
2697
2696
|
"synstructure",
|
|
2698
2697
|
]
|
|
2699
2698
|
|
|
@@ -2727,7 +2726,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
|
|
2727
2726
|
dependencies = [
|
|
2728
2727
|
"proc-macro2",
|
|
2729
2728
|
"quote",
|
|
2730
|
-
"syn 2.0.
|
|
2729
|
+
"syn 2.0.119",
|
|
2731
2730
|
]
|
|
2732
2731
|
|
|
2733
2732
|
[[package]]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "scraper-rs"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.0"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
|
|
@@ -15,8 +15,8 @@ default = []
|
|
|
15
15
|
extension-module = ["pyo3/extension-module"]
|
|
16
16
|
|
|
17
17
|
[dependencies]
|
|
18
|
-
pyo3 = { version = "0.
|
|
19
|
-
pyo3-async-runtimes = { version = "0.
|
|
18
|
+
pyo3 = { version = "0.29.0", features = ["abi3-py310"] }
|
|
19
|
+
pyo3-async-runtimes = { version = "0.29", features = ["tokio-runtime"] }
|
|
20
20
|
rayon = "1"
|
|
21
21
|
tokio = { version = "1", features = ["rt", "macros", "sync"] }
|
|
22
22
|
|
|
@@ -28,7 +28,7 @@ criterion = { version = "0.8", features = ["async_tokio"] }
|
|
|
28
28
|
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
|
29
29
|
|
|
30
30
|
[build-dependencies]
|
|
31
|
-
pyo3-build-config = { version = "0.
|
|
31
|
+
pyo3-build-config = { version = "0.29.0", features = ["resolve-config"] }
|
|
32
32
|
|
|
33
33
|
[[bench]]
|
|
34
34
|
name = "sync_async"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scraper-rust
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Classifier: Programming Language :: Python
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -33,6 +33,15 @@ Project-URL: Repository, https://github.com/RustedBytes/scraper-rs
|
|
|
33
33
|
|
|
34
34
|
Python bindings for high-performance Rust HTML parsing via PyO3. It provides a lightweight `Document`/`Element` API with CSS selectors backed by `rustedbytes-tl`, XPath (via `xee-xpath`), handy helpers, and zero Python-side parsing work.
|
|
35
35
|
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install scraper-rust
|
|
40
|
+
|
|
41
|
+
# or using uv
|
|
42
|
+
uv add scraper-rust
|
|
43
|
+
```
|
|
44
|
+
|
|
36
45
|
## Quick start
|
|
37
46
|
|
|
38
47
|
```py
|
|
@@ -50,7 +59,7 @@ print(doc.text) # "First Second"
|
|
|
50
59
|
|
|
51
60
|
items = doc.select(".item")
|
|
52
61
|
print(items[0].attr("data-id")) # "1"
|
|
53
|
-
print(items[0].to_dict())
|
|
62
|
+
print(items[0].to_dict()) # {"tag": "div", "text": "First", "html": "<a...>", ...}
|
|
54
63
|
|
|
55
64
|
first_link = doc.select_first("a[href]") # alias: doc.find(...)
|
|
56
65
|
print(first_link.text, first_link.attr("href")) # First /a
|
|
@@ -65,9 +74,11 @@ print(doc.xpath_first("//div[@data-id='1']/a").attr("href")) # "/a"
|
|
|
65
74
|
# Functional helpers
|
|
66
75
|
links = select(html, "a[href]")
|
|
67
76
|
print([link.attr("href") for link in links]) # ["/a", "/b"]
|
|
68
|
-
print(first(html, "a[href]").text)
|
|
69
|
-
print(select_first(html, "a[href]").text)
|
|
70
|
-
print(
|
|
77
|
+
print(first(html, "a[href]").text) # First
|
|
78
|
+
print(select_first(html, "a[href]").text) # First
|
|
79
|
+
print(
|
|
80
|
+
[link.text for link in xpath(html, "//div[@class='item']/a")]
|
|
81
|
+
) # ["First", "Second"]
|
|
71
82
|
print(prettify(html))
|
|
72
83
|
```
|
|
73
84
|
|
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
Python bindings for high-performance Rust HTML parsing via PyO3. It provides a lightweight `Document`/`Element` API with CSS selectors backed by `rustedbytes-tl`, XPath (via `xee-xpath`), handy helpers, and zero Python-side parsing work.
|
|
8
8
|
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install scraper-rust
|
|
13
|
+
|
|
14
|
+
# or using uv
|
|
15
|
+
uv add scraper-rust
|
|
16
|
+
```
|
|
17
|
+
|
|
9
18
|
## Quick start
|
|
10
19
|
|
|
11
20
|
```py
|
|
@@ -23,7 +32,7 @@ print(doc.text) # "First Second"
|
|
|
23
32
|
|
|
24
33
|
items = doc.select(".item")
|
|
25
34
|
print(items[0].attr("data-id")) # "1"
|
|
26
|
-
print(items[0].to_dict())
|
|
35
|
+
print(items[0].to_dict()) # {"tag": "div", "text": "First", "html": "<a...>", ...}
|
|
27
36
|
|
|
28
37
|
first_link = doc.select_first("a[href]") # alias: doc.find(...)
|
|
29
38
|
print(first_link.text, first_link.attr("href")) # First /a
|
|
@@ -38,9 +47,11 @@ print(doc.xpath_first("//div[@data-id='1']/a").attr("href")) # "/a"
|
|
|
38
47
|
# Functional helpers
|
|
39
48
|
links = select(html, "a[href]")
|
|
40
49
|
print([link.attr("href") for link in links]) # ["/a", "/b"]
|
|
41
|
-
print(first(html, "a[href]").text)
|
|
42
|
-
print(select_first(html, "a[href]").text)
|
|
43
|
-
print(
|
|
50
|
+
print(first(html, "a[href]").text) # First
|
|
51
|
+
print(select_first(html, "a[href]").text) # First
|
|
52
|
+
print(
|
|
53
|
+
[link.text for link in xpath(html, "//div[@class='item']/a")]
|
|
54
|
+
) # ["First", "Second"]
|
|
44
55
|
print(prettify(html))
|
|
45
56
|
```
|
|
46
57
|
|
|
@@ -31,12 +31,14 @@ from scraper_rs import asyncio as async_scraper
|
|
|
31
31
|
|
|
32
32
|
html = "<div class='item'><a href='/a'>First</a></div>"
|
|
33
33
|
|
|
34
|
+
|
|
34
35
|
async def main():
|
|
35
36
|
async with await async_scraper.parse(html) as doc:
|
|
36
37
|
items = await doc.select(".item")
|
|
37
38
|
first_link = await items[0].select_first("a[href]")
|
|
38
39
|
print(first_link.text, first_link.attr("href"))
|
|
39
40
|
|
|
41
|
+
|
|
40
42
|
asyncio.run(main())
|
|
41
43
|
```
|
|
42
44
|
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "scraper-rust"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.7.0"
|
|
8
8
|
description = "Python bindings for high-performance HTML parsing and selection with PyO3"
|
|
9
9
|
authors = [{ name = "Yehor Smoliakov", email = "egorsmkv@gmail.com" }]
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from .
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
from . import scraper_rs as _core
|
|
6
|
+
|
|
7
|
+
_AsyncDocumentCore = _core._AsyncDocumentCore
|
|
8
|
+
_AsyncElementCore = _core._AsyncElementCore
|
|
9
|
+
_first_async = _core.first_async
|
|
10
|
+
_parse_async = _core.parse_async
|
|
11
|
+
_prettify_async = _core.prettify_async
|
|
12
|
+
_select_async = _core.select_async
|
|
13
|
+
_select_first_async = _core.select_first_async
|
|
14
|
+
_xpath_async = _core.xpath_async
|
|
15
|
+
_xpath_first_async = _core.xpath_first_async
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def _wrap_element(element: _AsyncElementCore | None) -> "AsyncElement | None":
|
|
@@ -19,6 +19,44 @@ pub(crate) fn escape_html(value: &str) -> String {
|
|
|
19
19
|
escaped
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
#[inline]
|
|
23
|
+
fn entity_reference_len(value: &str) -> Option<usize> {
|
|
24
|
+
let semicolon = value.find(';')?;
|
|
25
|
+
let reference = &value[..semicolon];
|
|
26
|
+
let valid = if let Some(numeric) = reference.strip_prefix('#') {
|
|
27
|
+
if let Some(hex) = numeric
|
|
28
|
+
.strip_prefix('x')
|
|
29
|
+
.or_else(|| numeric.strip_prefix('X'))
|
|
30
|
+
{
|
|
31
|
+
!hex.is_empty() && hex.bytes().all(|byte| byte.is_ascii_hexdigit())
|
|
32
|
+
} else {
|
|
33
|
+
!numeric.is_empty() && numeric.bytes().all(|byte| byte.is_ascii_digit())
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
!reference.is_empty() && reference.bytes().all(|byte| byte.is_ascii_alphanumeric())
|
|
37
|
+
};
|
|
38
|
+
valid.then_some(semicolon + 1)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#[inline]
|
|
42
|
+
fn escape_html_text(value: &str) -> String {
|
|
43
|
+
let mut escaped = String::with_capacity(value.len());
|
|
44
|
+
let mut rest = value;
|
|
45
|
+
while let Some((before, after_ampersand)) = rest.split_once('&') {
|
|
46
|
+
escaped.push_str(&escape_html(before));
|
|
47
|
+
rest = &rest[before.len() + 1..];
|
|
48
|
+
if let Some(reference_len) = entity_reference_len(after_ampersand) {
|
|
49
|
+
escaped.push('&');
|
|
50
|
+
escaped.push_str(&after_ampersand[..reference_len]);
|
|
51
|
+
rest = &after_ampersand[reference_len..];
|
|
52
|
+
} else {
|
|
53
|
+
escaped.push_str("&");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
escaped.push_str(&escape_html(rest));
|
|
57
|
+
escaped
|
|
58
|
+
}
|
|
59
|
+
|
|
22
60
|
#[inline]
|
|
23
61
|
fn push_indent(out: &mut String, level: usize, indent_size: usize) {
|
|
24
62
|
let spaces = level.saturating_mul(indent_size);
|
|
@@ -135,7 +173,7 @@ fn serialize_pretty_element_into(
|
|
|
135
173
|
&& let PrettyChild::Text(text) = &children[0]
|
|
136
174
|
{
|
|
137
175
|
out.push('>');
|
|
138
|
-
out.push_str(&
|
|
176
|
+
out.push_str(&escape_html_text(text));
|
|
139
177
|
out.push_str("</");
|
|
140
178
|
out.push_str(&name);
|
|
141
179
|
out.push_str(">\n");
|
|
@@ -160,7 +198,7 @@ fn serialize_pretty_element_into(
|
|
|
160
198
|
}
|
|
161
199
|
PrettyChild::Text(text) => {
|
|
162
200
|
push_indent(out, level + 1, indent_size);
|
|
163
|
-
out.push_str(&
|
|
201
|
+
out.push_str(&escape_html_text(&text));
|
|
164
202
|
out.push('\n');
|
|
165
203
|
}
|
|
166
204
|
}
|
|
@@ -191,7 +229,7 @@ pub(crate) fn prettify_document_html(html: &str) -> String {
|
|
|
191
229
|
tl::Node::Raw(text) => {
|
|
192
230
|
let normalized = normalized_text(&bytes_to_string(text));
|
|
193
231
|
if !normalized.is_empty() {
|
|
194
|
-
out.push_str(&
|
|
232
|
+
out.push_str(&escape_html_text(&normalized));
|
|
195
233
|
out.push('\n');
|
|
196
234
|
}
|
|
197
235
|
}
|
|
@@ -226,7 +264,7 @@ pub(crate) fn prettify_fragment_html(html: &str) -> PyResult<String> {
|
|
|
226
264
|
if normalized.is_empty() {
|
|
227
265
|
continue;
|
|
228
266
|
}
|
|
229
|
-
out.push_str(&
|
|
267
|
+
out.push_str(&escape_html_text(&normalized));
|
|
230
268
|
out.push('\n');
|
|
231
269
|
}
|
|
232
270
|
tl::Node::Comment(_) => {}
|
|
@@ -233,6 +233,10 @@ fn prettify_helpers_format_document_and_fragment() {
|
|
|
233
233
|
assert!(doc_pretty.contains("<span>Hi</span>"));
|
|
234
234
|
assert!(doc_pretty.contains("<p>A & B</p>"));
|
|
235
235
|
assert!(doc_pretty.contains("<br>"));
|
|
236
|
+
assert_eq!(
|
|
237
|
+
prettify_document_html("<p>> & A A &</p>"),
|
|
238
|
+
"<p>> & A A &</p>"
|
|
239
|
+
);
|
|
236
240
|
|
|
237
241
|
let fragment_pretty =
|
|
238
242
|
prettify_fragment_html(r#"hello <span data-x="1"> world </span>"#).unwrap();
|
|
@@ -141,6 +141,9 @@ pub(crate) fn xpath_with_limit(
|
|
|
141
141
|
) -> PyResult<Vec<Element>> {
|
|
142
142
|
let max_size_bytes = max_size_bytes.unwrap_or(DEFAULT_MAX_PARSE_BYTES);
|
|
143
143
|
let html_to_parse = ensure_within_size_limit(html, max_size_bytes, truncate_on_limit)?;
|
|
144
|
+
if html_to_parse.len() < html.len() {
|
|
145
|
+
return evaluate_fragment_xpath_with_fallback(html_to_parse.as_ref(), expr);
|
|
146
|
+
}
|
|
144
147
|
match parse_xpath_documents(html_to_parse.as_ref(), "HTML document") {
|
|
145
148
|
Ok((mut documents, document_handle)) => {
|
|
146
149
|
evaluate_xpath_elements(&mut documents, document_handle, expr)
|
|
@@ -160,6 +163,9 @@ pub(crate) fn xpath_first_with_limit(
|
|
|
160
163
|
) -> PyResult<Option<Element>> {
|
|
161
164
|
let max_size_bytes = max_size_bytes.unwrap_or(DEFAULT_MAX_PARSE_BYTES);
|
|
162
165
|
let html_to_parse = ensure_within_size_limit(html, max_size_bytes, truncate_on_limit)?;
|
|
166
|
+
if html_to_parse.len() < html.len() {
|
|
167
|
+
return evaluate_fragment_xpath_first_with_fallback(html_to_parse.as_ref(), expr);
|
|
168
|
+
}
|
|
163
169
|
match parse_xpath_documents(html_to_parse.as_ref(), "HTML document") {
|
|
164
170
|
Ok((mut documents, document_handle)) => {
|
|
165
171
|
evaluate_xpath_first_element(&mut documents, document_handle, expr)
|
|
@@ -219,12 +225,7 @@ pub(crate) fn evaluate_fragment_xpath_with_fallback(
|
|
|
219
225
|
if normalized.is_empty() {
|
|
220
226
|
return Ok(Vec::new());
|
|
221
227
|
}
|
|
222
|
-
|
|
223
|
-
Ok((mut documents, document_handle)) => {
|
|
224
|
-
evaluate_xpath_elements(&mut documents, document_handle, expr)
|
|
225
|
-
}
|
|
226
|
-
Err(_) => Ok(Vec::new()),
|
|
227
|
-
}
|
|
228
|
+
evaluate_fragment_xpath(&normalized, expr)
|
|
228
229
|
}
|
|
229
230
|
|
|
230
231
|
pub(crate) fn evaluate_fragment_xpath_first_with_fallback(
|
|
@@ -235,12 +236,7 @@ pub(crate) fn evaluate_fragment_xpath_first_with_fallback(
|
|
|
235
236
|
if normalized.is_empty() {
|
|
236
237
|
return Ok(None);
|
|
237
238
|
}
|
|
238
|
-
|
|
239
|
-
Ok((mut documents, document_handle)) => {
|
|
240
|
-
evaluate_xpath_first_element(&mut documents, document_handle, expr)
|
|
241
|
-
}
|
|
242
|
-
Err(_) => Ok(None),
|
|
243
|
-
}
|
|
239
|
+
evaluate_fragment_xpath_first(&normalized, expr)
|
|
244
240
|
}
|
|
245
241
|
|
|
246
242
|
pub(crate) struct XPathDocumentState {
|
|
@@ -43,26 +43,26 @@ wheels = [
|
|
|
43
43
|
|
|
44
44
|
[[package]]
|
|
45
45
|
name = "maturin"
|
|
46
|
-
version = "1.
|
|
46
|
+
version = "1.14.1"
|
|
47
47
|
source = { registry = "https://pypi.org/simple" }
|
|
48
48
|
dependencies = [
|
|
49
49
|
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
50
50
|
]
|
|
51
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
51
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e7/b3/addd877f871fb1860d46d3a4f206ecb10b946c85846805e6367631926fd3/maturin-1.14.1.tar.gz", hash = "sha256:9d6577a62cd08e0ceba7a0db06fb098e0c9b1b3429bad747a4f3a18215a1b3df", size = 369637, upload-time = "2026-06-19T05:19:49.774Z" }
|
|
52
52
|
wheels = [
|
|
53
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
54
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
55
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
56
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
57
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
58
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
59
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
60
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
61
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
62
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
63
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
64
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
65
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
53
|
+
{ url = "https://files.pythonhosted.org/packages/f4/f0/97c5a5bd9c71653a066c0976a484eaaae50b9369557838a4176b7b0bdaa5/maturin-1.14.1-py3-none-linux_armv6l.whl", hash = "sha256:522292398945442cdafa9daeb2271b2340fbde57027b818f923f88eab04174f8", size = 10207496, upload-time = "2026-06-19T05:19:09.321Z" },
|
|
54
|
+
{ url = "https://files.pythonhosted.org/packages/fe/83/294bca639b0e052f1e2f65199b3db258780c7d4e31408b934c9c974a1379/maturin-1.14.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ffe5ad71f21d1e6603c4dd75f7fee34adf5ed5ebcebb692886549888ebb329ed", size = 19680113, upload-time = "2026-06-19T05:19:13.43Z" },
|
|
55
|
+
{ url = "https://files.pythonhosted.org/packages/43/b6/79c881410a3b1c187f7eb3d407aecae646c6a4433d630d72200359015e83/maturin-1.14.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f3306078070c1508fd715b9116070cbcaff5959024272a9f1e6f5cb29768b86c", size = 10169205, upload-time = "2026-06-19T05:19:16.615Z" },
|
|
56
|
+
{ url = "https://files.pythonhosted.org/packages/93/9d/44b6f26dcb7f7a04c5501ac2dbb6ca1490150682baa525ca5860504f9eab/maturin-1.14.1-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:cd457cd88961156e26379e1155bd287cc0ec1c8b2f1582b0660fb31b87c8842d", size = 10188098, upload-time = "2026-06-19T05:19:19.736Z" },
|
|
57
|
+
{ url = "https://files.pythonhosted.org/packages/1a/bd/9c0d5d6983905ce2c9edaa073a7e89355a9cf7f396988e05d32f1c37785d/maturin-1.14.1-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:dfc54ae32e6fcb18302193ab9a30b0b25eefffba994ae13238974805533ef75e", size = 10627576, upload-time = "2026-06-19T05:19:22.713Z" },
|
|
58
|
+
{ url = "https://files.pythonhosted.org/packages/e5/33/b096412bd6a7cb399652b260666f901adf88a687181a6dbd6a3f89f0a94e/maturin-1.14.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:a131d912b5267e640bc96d70f4914e10590aed64082ec9abacba7cea52004224", size = 10085181, upload-time = "2026-06-19T05:19:25.69Z" },
|
|
59
|
+
{ url = "https://files.pythonhosted.org/packages/56/8d/08c3bf469c38a23c9e6c877e338193001eb604d010fedc08341974e38528/maturin-1.14.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:be18fc568fb76884c0205456336892a75105ec398e6b667cd777c6268bd06d69", size = 10026363, upload-time = "2026-06-19T05:19:28.904Z" },
|
|
60
|
+
{ url = "https://files.pythonhosted.org/packages/3a/a4/c4d1a92839f8745ab4aab988a7db884a79d6d710bd3b286fcf9316dece1a/maturin-1.14.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:994a0c8ba3ad8a92b3a9ee1b02645d200d610216b15cff5102b0fe65e8e08666", size = 13321347, upload-time = "2026-06-19T05:19:32.411Z" },
|
|
61
|
+
{ url = "https://files.pythonhosted.org/packages/b3/fa/170f04624d03fd07d2a8b1b67de83a127af93aef9eaa425839553347297b/maturin-1.14.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:be80866363e605d137991b491a741a84cde9ae350183c4c85f49690ca9aaaa65", size = 10877609, upload-time = "2026-06-19T05:19:35.448Z" },
|
|
62
|
+
{ url = "https://files.pythonhosted.org/packages/61/ad/1ae2e1d0ded282bf2c55ac13f0811d87deb425e200ae64a15785675dede9/maturin-1.14.1-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:5282dffd4b539d2be245f4e5b1a5ab6bc1033b58f4a4872f5833f9d43c954aa4", size = 10417316, upload-time = "2026-06-19T05:19:38.28Z" },
|
|
63
|
+
{ url = "https://files.pythonhosted.org/packages/fb/27/bf677183920718da49cd7982d6a3ffc440aad8919329f571d189f81b7bdf/maturin-1.14.1-py3-none-win32.whl", hash = "sha256:1a04de0a20188f95c721b5702eed18140bdcccb28c386797093eca3f62f4d4e0", size = 8931293, upload-time = "2026-06-19T05:19:41.183Z" },
|
|
64
|
+
{ url = "https://files.pythonhosted.org/packages/63/4b/585adeb9167b08d3cdff0032a938b0e72655c92003df4f52c3f696a1bcc2/maturin-1.14.1-py3-none-win_amd64.whl", hash = "sha256:3c9f94640ecc4895e94abaf834a0684430032c865b2748a36c12461fd9252fdd", size = 10314067, upload-time = "2026-06-19T05:19:44.389Z" },
|
|
65
|
+
{ url = "https://files.pythonhosted.org/packages/51/d4/dac8c0720ae246be1700afb6fbdbbea20fe35b13f6570b2f70faa005df77/maturin-1.14.1-py3-none-win_arm64.whl", hash = "sha256:15cea8fcb3ba47dd636f50092bb34baea8b04ac777392f23e6bf8a9a61efb894", size = 9718943, upload-time = "2026-06-19T05:19:47.49Z" },
|
|
66
66
|
]
|
|
67
67
|
|
|
68
68
|
[[package]]
|
|
@@ -94,7 +94,7 @@ wheels = [
|
|
|
94
94
|
|
|
95
95
|
[[package]]
|
|
96
96
|
name = "pytest"
|
|
97
|
-
version = "9.
|
|
97
|
+
version = "9.1.1"
|
|
98
98
|
source = { registry = "https://pypi.org/simple" }
|
|
99
99
|
dependencies = [
|
|
100
100
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
@@ -105,9 +105,9 @@ dependencies = [
|
|
|
105
105
|
{ name = "pygments" },
|
|
106
106
|
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
107
107
|
]
|
|
108
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
108
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
|
|
109
109
|
wheels = [
|
|
110
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
110
|
+
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
|
|
111
111
|
]
|
|
112
112
|
|
|
113
113
|
[[package]]
|
|
@@ -126,32 +126,32 @@ wheels = [
|
|
|
126
126
|
|
|
127
127
|
[[package]]
|
|
128
128
|
name = "ruff"
|
|
129
|
-
version = "0.
|
|
129
|
+
version = "0.16.0"
|
|
130
130
|
source = { registry = "https://pypi.org/simple" }
|
|
131
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
131
|
+
sdist = { url = "https://files.pythonhosted.org/packages/4d/94/1e5e4967626faf12fa56999cd6222dff6992ceb086ad7945756baf70c7a7/ruff-0.16.0.tar.gz", hash = "sha256:e460aafd5495ec89efaa6ced2e4a9a581116451e1c88b9d37ef497e0f8e93982", size = 4790557, upload-time = "2026-07-23T19:11:30.981Z" }
|
|
132
132
|
wheels = [
|
|
133
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
134
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
135
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
136
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
137
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
138
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
139
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
140
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
141
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
142
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
143
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
144
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
145
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
146
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
147
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
148
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
149
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
133
|
+
{ url = "https://files.pythonhosted.org/packages/4b/81/1c8818fee7ce1a04cd7d1b3172e0a8f8e4f1dc4feb7fc390e16daa8af323/ruff-0.16.0-py3-none-linux_armv6l.whl", hash = "sha256:e5115729eb08c585e5121978ba5d5b60caeae394ce21b9fb5e6cd33a1c6c9b1e", size = 10754633, upload-time = "2026-07-23T19:10:46.415Z" },
|
|
134
|
+
{ url = "https://files.pythonhosted.org/packages/23/df/beaf59c09d68db84304d555f188b276a77132a5d5b0b67a5c762aa143628/ruff-0.16.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3c954b1d580bfa035b41654f7858cc7e71d5fc3ac5b723dd62bd9133830ed522", size = 10969164, upload-time = "2026-07-23T19:10:50.271Z" },
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/42/ce/741cd197496a1abbf51352710fd15ed995d2a2be87189c1da26a450d6e83/ruff-0.16.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e01c21d10eb1b29f47b7454e1f4056db9a3f0260c646aa88457c610291db9f81", size = 10488846, upload-time = "2026-07-23T19:10:52.639Z" },
|
|
136
|
+
{ url = "https://files.pythonhosted.org/packages/52/2a/a2db8e88cade358f5cdcb05674a917751074109315d014eb6352d9a893f7/ruff-0.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e364e5ed22ed8dc05082fd78e35308618260907ac2d3c1d637b2e682415b6c9", size = 10889729, upload-time = "2026-07-23T19:10:54.89Z" },
|
|
137
|
+
{ url = "https://files.pythonhosted.org/packages/42/65/62a771694ebd63029dc953e27dbad40e1588bd4860ff9fe881018fddaa49/ruff-0.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d327b8fc113a1d4421a04f3839d3752057c8dd1ee320223a6f3f52d04ada462a", size = 10568275, upload-time = "2026-07-23T19:10:56.993Z" },
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/3f/e2/ced249fe8af5f086c5c58cc21cc3356d50f32f7401c5df87050c999620a7/ruff-0.16.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b50c55e263103586b3dcf5f73d479eb8cb5fdb6098fec59a62891dab653717", size = 11385112, upload-time = "2026-07-23T19:10:59.615Z" },
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/87/0b/05154977a8fd69eeb6c103271f55403bfd8711f5c0f8ed07489d95a504e7/ruff-0.16.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff4a79ce3ec0172f3241943835de1c4cb4e2dcd07f0f8c2d02603dbbbee4b17", size = 12207008, upload-time = "2026-07-23T19:11:02.154Z" },
|
|
140
|
+
{ url = "https://files.pythonhosted.org/packages/fb/29/98225831a3a1eab0e02f4acc6ca6559a98611dcc68b6965ff4b7234627c1/ruff-0.16.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e95c448fca1fb2a18372a9440926c5a6ee789639bb975c72e7ae6d0b04218ab4", size = 11650842, upload-time = "2026-07-23T19:11:04.557Z" },
|
|
141
|
+
{ url = "https://files.pythonhosted.org/packages/91/66/6bd3cf90500653d55dc0ffc8507aa8300bd49d0214b2e8cb4d3fef2943ba/ruff-0.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f11a8d11010301d0a398a2fdef67691feca7294da6aef55e2150e8fa2cd520b", size = 11400718, upload-time = "2026-07-23T19:11:09.233Z" },
|
|
142
|
+
{ url = "https://files.pythonhosted.org/packages/8e/a2/a54eb4eae05d66364050a5d3b8a9c5ef88196531b3cbe7109d873f87f819/ruff-0.16.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:48044c678e9cb8698246c99b14aaccfa6601dea7379eb48a6f8f73f7a6d86cd0", size = 11426177, upload-time = "2026-07-23T19:11:11.994Z" },
|
|
143
|
+
{ url = "https://files.pythonhosted.org/packages/1a/be/16e3eea4b2a478a496919f5e36f17c4559e54620bd3bbac5d6affa068006/ruff-0.16.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7aa0959bad8eb8bef50340154fc9b58678dae31fa4293afa38b44b6e552c0213", size = 10856126, upload-time = "2026-07-23T19:11:14.221Z" },
|
|
144
|
+
{ url = "https://files.pythonhosted.org/packages/a2/84/252eb8b868a16eec7257c14f504f77537e734b2d69c762e639e588e304a3/ruff-0.16.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28ea2b7df8ebf7f9da6b7d47b230ab48f387c0a29be3b474c4d0740e197bb9af", size = 10571208, upload-time = "2026-07-23T19:11:16.378Z" },
|
|
145
|
+
{ url = "https://files.pythonhosted.org/packages/21/09/817a482f542f7570cbb4554b26e896610c7114f539b1d9e2d2145bf6bef6/ruff-0.16.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:33a3dfac8c35f81498dea9181bccc2f4c4bc8f1521a1dd9406e77643e0f0fb09", size = 11063329, upload-time = "2026-07-23T19:11:19.173Z" },
|
|
146
|
+
{ url = "https://files.pythonhosted.org/packages/2e/23/9403c180ca1cb9b1f7335f5c3e5305c09d49ea5b345196682a36028bde4a/ruff-0.16.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a5237a0bda500d30d81b8e07a6973a5cbc772864cbf746ae2f4e8a2e01c9f4ed", size = 11489751, upload-time = "2026-07-23T19:11:21.74Z" },
|
|
147
|
+
{ url = "https://files.pythonhosted.org/packages/b2/1d/1b2ef7bcde851c78d7f17f1cca13fd6dc695fc4b3d6197941e72cae5b132/ruff-0.16.0-py3-none-win32.whl", hash = "sha256:7fab76fa065c873f41ff744347c6e77bcc3dfec4bcc754dc26b63d23c0f7f5fb", size = 10785885, upload-time = "2026-07-23T19:11:23.947Z" },
|
|
148
|
+
{ url = "https://files.pythonhosted.org/packages/b2/a3/d5e4ef7a56be3f928ffb90b94c25ba7d3cb9c7fe0736aeaaedf361770712/ruff-0.16.0-py3-none-win_amd64.whl", hash = "sha256:429c117f022bf481fabd9d551e7a3952b24c65e6ef44337ea09d90bebef14472", size = 11923141, upload-time = "2026-07-23T19:11:26.409Z" },
|
|
149
|
+
{ url = "https://files.pythonhosted.org/packages/cb/9a/8415f2657cbe200f41a4531ccededf135505a92d4a012229121f885b26f9/ruff-0.16.0-py3-none-win_arm64.whl", hash = "sha256:14296fedcd2705c77ab8235439278bbb38f285cf7da5528b00b3e330c3d4872d", size = 11273407, upload-time = "2026-07-23T19:11:28.705Z" },
|
|
150
150
|
]
|
|
151
151
|
|
|
152
152
|
[[package]]
|
|
153
153
|
name = "scraper-rust"
|
|
154
|
-
version = "0.
|
|
154
|
+
version = "0.7.0"
|
|
155
155
|
source = { editable = "." }
|
|
156
156
|
|
|
157
157
|
[package.optional-dependencies]
|
|
@@ -239,9 +239,9 @@ wheels = [
|
|
|
239
239
|
|
|
240
240
|
[[package]]
|
|
241
241
|
name = "typing-extensions"
|
|
242
|
-
version = "4.
|
|
242
|
+
version = "4.16.0"
|
|
243
243
|
source = { registry = "https://pypi.org/simple" }
|
|
244
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
244
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
|
|
245
245
|
wheels = [
|
|
246
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
246
|
+
{ url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
|
|
247
247
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|