From a0a325303e84babab7133b96f44841e2c80b5f9d Mon Sep 17 00:00:00 2001 From: Quentin Lienhardt Date: Mon, 8 Jun 2026 09:49:56 +0200 Subject: [PATCH 1/5] feat: Switch to Camoufox --- .devcontainer/post-create.sh | 2 +- README.md | 4 +- bases/ecoindex/worker/tasks.py | 61 ++--- components/ecoindex/scraper/scrap.py | 22 +- projects/ecoindex_api/Taskfile.yml | 4 +- .../ecoindex_api/docker/worker/dockerfile | 2 +- projects/ecoindex_api/pyproject.toml | 6 +- projects/ecoindex_cli/README.md | 2 +- projects/ecoindex_cli/dockerfile | 2 +- projects/ecoindex_cli/pyproject.toml | 3 +- projects/ecoindex_scraper/README.md | 2 + projects/ecoindex_scraper/dockerfile | 2 +- projects/ecoindex_scraper/pyproject.toml | 3 +- pyproject.toml | 10 +- tasks/UvTaskfile.yml | 6 +- uv.lock | 253 +++++++++++++++--- 16 files changed, 281 insertions(+), 103 deletions(-) diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 0f3d91f..42ad8d4 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -11,4 +11,4 @@ if [ -S /var/run/docker.sock ]; then fi uv sync --all-groups -uv run playwright install chromium --with-deps +uv run camoufox fetch diff --git a/README.md b/README.md index 97e45f3..361fae7 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ This repository contains the following projects: - The number of DOM elements in the page - The size of the page - The number of external requests of the page -- [Ecoindex Scraper](projects/ecoindex_scraper/README.md): This module provides a simple interface to get the [Ecoindex](http://www.ecoindex.fr) based on a URL. It uses [Playwright](https://playwright.dev/) to get the DOM elements, size and requests of the page. +- [Ecoindex Scraper](projects/ecoindex_scraper/README.md): This module provides a simple interface to get the [Ecoindex](http://www.ecoindex.fr) based on a URL. It uses [Camoufox](https://camoufox.com/) to get the DOM elements, size and requests of the page. - [Ecoindex CLI](projects/ecoindex_cli/README.md): This module provides a CLI tool to get the [Ecoindex](http://www.ecoindex.fr) based on a URL. It uses the [Ecoindex Scraper](projects/ecoindex_scraper/README.md) module. - [Ecoindex API](projects/ecoindex_api/README.md): This module provides a REST API to get the [Ecoindex](http://www.ecoindex.fr) based on a URL. It uses the [Ecoindex Scraper](projects/ecoindex_scraper/README.md) module. @@ -80,4 +80,4 @@ Please also refer to the mentions provided in the code files for specifics on th ## [Contributing](CONTRIBUTING.md) -## [Code of conduct](CODE_OF_CONDUCT.md) \ No newline at end of file +## [Code of conduct](CODE_OF_CONDUCT.md) diff --git a/bases/ecoindex/worker/tasks.py b/bases/ecoindex/worker/tasks.py index a90431a..fade282 100644 --- a/bases/ecoindex/worker/tasks.py +++ b/bases/ecoindex/worker/tasks.py @@ -20,7 +20,6 @@ from ecoindex.models.tasks import QueueTaskError, QueueTaskResult from ecoindex.scraper.scrap import EcoindexScraper from ecoindex.worker_component import app -from playwright._impl._errors import Error as WebDriverException from sentry_sdk import init as sentry_init if Settings().GLITCHTIP_DSN: @@ -97,8 +96,34 @@ async def async_ecoindex_task( ), ) - except WebDriverException as exc: - if exc.message and "ERR_NAME_NOT_RESOLVED" in exc.message: + except TypeError as exc: + return QueueTaskResult( + status=TaskStatus.FAILURE, + error=QueueTaskError( + url=url, # type: ignore + exception=EcoindexContentTypeError.__name__, + status_code=520, + message=exc.args[0], + detail={"mimetype": None}, + ), + ) + + except EcoindexScraperStatusException as exc: + return QueueTaskResult( + status=TaskStatus.FAILURE, + error=QueueTaskError( + url=url, # type: ignore + status_code=521, + exception=EcoindexStatusError.__name__, + message=exc.message, + detail={"status": exc.status}, + ), + ) + + except Exception as exc: + message = getattr(exc, "message", str(exc)) + + if message and "ERR_NAME_NOT_RESOLVED" in message: return QueueTaskResult( status=TaskStatus.FAILURE, error=QueueTaskError( @@ -113,7 +138,7 @@ async def async_ecoindex_task( ), ) - if exc.message and "ERR_CONNECTION_TIMED_OUT" in exc.message: + if message and "ERR_CONNECTION_TIMED_OUT" in message: return QueueTaskResult( status=TaskStatus.FAILURE, error=QueueTaskError( @@ -134,35 +159,11 @@ async def async_ecoindex_task( url=url, # type: ignore exception=type(exc).__name__, status_code=500, - message=str(exc.message) if exc.message else "", + message=message, detail=await format_exception_response(exception=exc), ), ) - except TypeError as exc: - return QueueTaskResult( - status=TaskStatus.FAILURE, - error=QueueTaskError( - url=url, # type: ignore - exception=EcoindexContentTypeError.__name__, - status_code=520, - message=exc.args[0], - detail={"mimetype": None}, - ), - ) - - except EcoindexScraperStatusException as exc: - return QueueTaskResult( - status=TaskStatus.FAILURE, - error=QueueTaskError( - url=url, # type: ignore - status_code=521, - exception=EcoindexStatusError.__name__, - message=exc.message, - detail={"status": exc.status}, - ), - ) - @app.task( name="ecoindex.batch_import", @@ -202,7 +203,7 @@ async def async_ecoindex_batch_import_task( return QueueTaskResult( status=TaskStatus.FAILURE, error=QueueTaskError( - url=None, # type: ignore + url=None, exception=type(exc).__name__, status_code=500, message=str(exc.message) if exc.message else "", # type: ignore diff --git a/components/ecoindex/scraper/scrap.py b/components/ecoindex/scraper/scrap.py index cd80557..b4c34da 100644 --- a/components/ecoindex/scraper/scrap.py +++ b/components/ecoindex/scraper/scrap.py @@ -3,17 +3,18 @@ from datetime import datetime from time import sleep from uuid import uuid4 +from typing import Any, cast from ua_generator.user_agent import UserAgent from ua_generator import generate as ua_generate +from camoufox.async_api import AsyncCamoufox + from ecoindex.compute import compute_ecoindex from ecoindex.exceptions.scraper import EcoindexScraperStatusException from ecoindex.models.compute import PageMetrics, Result, ScreenShot, WindowSize from ecoindex.models.scraper import MimetypeAggregation, RequestItem, Requests from ecoindex.utils.screenshots import convert_screenshot_to_webp, set_screenshot_rights -from playwright._impl._api_structures import SetCookieParam, ViewportSize -from playwright.async_api import async_playwright from typing_extensions import deprecated @@ -30,7 +31,7 @@ def __init__( page_load_timeout: int = 20, headless: bool = True, basic_auth: str | None = None, - cookies: list[SetCookieParam] = [], + cookies: list[dict[str, object]] = [], custom_headers: dict[str, str] = {}, logger=None, ): @@ -84,16 +85,13 @@ async def get_requests_by_category(self) -> MimetypeAggregation: return self.all_requests.aggregation async def scrap_page(self) -> PageMetrics: - async with async_playwright() as p: - browser = await p.chromium.launch( - headless=self.headless, args=["--disable-software-rasterizer"] - ) + async with AsyncCamoufox( + headless=self.headless, + window=(self.window_size.width, self.window_size.height), + ) as browser_handle: + browser = cast(Any, browser_handle) self.context = await browser.new_context( record_har_path=self.har_temp_file_path, - screen=ViewportSize( - width=self.window_size.width, - height=self.window_size.height, - ), ignore_https_errors=True, http_credentials={ "username": self.basic_auth.split(":")[0], @@ -103,7 +101,7 @@ async def scrap_page(self) -> PageMetrics: else None, extra_http_headers=self.custom_headers, ) - await self.context.add_cookies(self.cookies) + await self.context.add_cookies(cast(Any, self.cookies)) self.page = await self.context.new_page() response = await self.page.goto(self.url) await self.check_page_response(response) diff --git a/projects/ecoindex_api/Taskfile.yml b/projects/ecoindex_api/Taskfile.yml index 18a8f01..d3488c2 100644 --- a/projects/ecoindex_api/Taskfile.yml +++ b/projects/ecoindex_api/Taskfile.yml @@ -209,8 +209,8 @@ tasks: - echo "Initialize the project for development" - echo "Install uv dependencies" - task: uv:install - - echo "Install playwright" - - task: uv:install-playwright + - echo "Install Camoufox" + - task: uv:install-camoufox - echo "Create the environment file" - task: init-env - echo "Create the database" diff --git a/projects/ecoindex_api/docker/worker/dockerfile b/projects/ecoindex_api/docker/worker/dockerfile index e8308fb..a0fc0c9 100644 --- a/projects/ecoindex_api/docker/worker/dockerfile +++ b/projects/ecoindex_api/docker/worker/dockerfile @@ -24,7 +24,7 @@ COPY projects/ecoindex_api/dist/$wheel $wheel RUN pip install --no-cache-dir $wheel RUN pip install --no-cache-dir aiomysql -RUN playwright install chromium --with-deps +RUN uv run camoufox fetch RUN rm -rf $wheel requirements.txt /tmp/dist /var/lib/{apt,dpkg,cache,log}/ diff --git a/projects/ecoindex_api/pyproject.toml b/projects/ecoindex_api/pyproject.toml index f980b32..2dcbc2d 100644 --- a/projects/ecoindex_api/pyproject.toml +++ b/projects/ecoindex_api/pyproject.toml @@ -12,8 +12,7 @@ dependencies = [ "celery>=5.3.4", "cryptography>=44.0.2", "fastapi>=0.109.1", - "playwright>=1.39.0", - "playwright-stealth>=1.0.6", + "camoufox>=0.4.11", "pydantic[email]>=2.1.1,<=2.4.2", "pydantic-settings>=2.0.3", "pyyaml>=6.0.1", @@ -41,8 +40,7 @@ backend = [ ] worker = [ "pillow>=12.2.0", - "playwright>=1.39.0", - "playwright-stealth>=1.0.6", + "camoufox>=0.4.11", ] dev = [ "aiosqlite>=0.19.0", diff --git a/projects/ecoindex_cli/README.md b/projects/ecoindex_cli/README.md index f6bcdd2..a1cc689 100644 --- a/projects/ecoindex_cli/README.md +++ b/projects/ecoindex_cli/README.md @@ -352,7 +352,7 @@ At first, you need to install dependencies from the repository root: ```bash uv sync --all-groups -uv run playwright install chromium --with-deps +uv run camoufox fetch ``` ### Usage diff --git a/projects/ecoindex_cli/dockerfile b/projects/ecoindex_cli/dockerfile index f818a53..9a90c7a 100644 --- a/projects/ecoindex_cli/dockerfile +++ b/projects/ecoindex_cli/dockerfile @@ -20,6 +20,6 @@ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt COPY projects/ecoindex_cli/dist/$wheel $wheel RUN pip install --no-cache-dir $wheel -RUN playwright install chromium --with-deps +RUN uv run camoufox fetch RUN rm -rf $wheel requirements.txt /tmp/dist /var/lib/{apt,dpkg,cache,log}/ diff --git a/projects/ecoindex_cli/pyproject.toml b/projects/ecoindex_cli/pyproject.toml index 51061a9..7008de6 100644 --- a/projects/ecoindex_cli/pyproject.toml +++ b/projects/ecoindex_cli/pyproject.toml @@ -14,8 +14,7 @@ dependencies = [ "loguru>=0.7.2", "matplotlib>=3.8.0", "pandas>=2.1.2", - "playwright>=1.39.0", - "playwright-stealth>=1.0.6", + "camoufox>=0.4.11", "pydantic>=2.4.2", "pydantic-settings>=2.0.3", "pyyaml>=6.0.1", diff --git a/projects/ecoindex_scraper/README.md b/projects/ecoindex_scraper/README.md index 8240b7e..4455cce 100644 --- a/projects/ecoindex_scraper/README.md +++ b/projects/ecoindex_scraper/README.md @@ -10,11 +10,13 @@ This module provides a simple interface to get the [Ecoindex](http://www.ecoinde ## Requirements - Python ^3.10 with [pip](https://pip.pypa.io/en/stable/installation/) +- [Camoufox](https://camoufox.com/python/installation/) for the browser binary ## Install ```shell pip install ecoindex_scraper +python -m camoufox fetch ``` If you need to convert the screenshot to webp with the `generate_screenshot` method, you need to install the Pillow dependency. diff --git a/projects/ecoindex_scraper/dockerfile b/projects/ecoindex_scraper/dockerfile index 8094e6a..7668d5c 100644 --- a/projects/ecoindex_scraper/dockerfile +++ b/projects/ecoindex_scraper/dockerfile @@ -13,6 +13,6 @@ RUN uv sync --package ecoindex_scraper --frozen --no-dev --no-editable ENV PATH="/code/.venv/bin:$PATH" -RUN playwright install chromium --with-deps +RUN uv run camoufox fetch RUN rm -rf /tmp/dist /var/lib/{apt,dpkg,cache,log}/ diff --git a/projects/ecoindex_scraper/pyproject.toml b/projects/ecoindex_scraper/pyproject.toml index daf74b3..257178d 100644 --- a/projects/ecoindex_scraper/pyproject.toml +++ b/projects/ecoindex_scraper/pyproject.toml @@ -7,8 +7,7 @@ requires-python = ">=3.10,<3.13" license = { text = "Creative Commons BY-NC-ND" } authors = [{ name = "Vincent Vatelot", email = "vincent.vatelot@ik.me" }] dependencies = [ - "playwright>=1.39.0", - "playwright-stealth>=1.0.6", + "camoufox>=0.4.11", "pydantic>=2.4.2", "pyyaml>=6.0.1", "setuptools>=69.5.1,<79.0.0", diff --git a/pyproject.toml b/pyproject.toml index fa7b3cd..50229c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,8 +24,7 @@ update-values = "ecoindex.scripts:update_values" [dependency-groups] scraper = [ - "playwright>=1.39.0", - "playwright-stealth>=1.0.6", + "camoufox>=0.4.11", ] scraper-webp = [ "pillow>=12.2.0", @@ -87,7 +86,12 @@ namespace_packages = true explicit_package_bases = true ignore_missing_imports = true disallow_untyped_defs = false -exclude = ["test", "dist", "__pycache__", "projects/ecoindex_api/alembic/versions"] +exclude = [ + "test", + "dist", + "__pycache__", + "projects/ecoindex_api/alembic/versions", +] [tool.coverage.run] omit = ["test/*"] diff --git a/tasks/UvTaskfile.yml b/tasks/UvTaskfile.yml index 616099d..90e570a 100644 --- a/tasks/UvTaskfile.yml +++ b/tasks/UvTaskfile.yml @@ -60,9 +60,9 @@ tasks: - uv version --package {{.PACKAGE_NAME}} --short silent: true - install-playwright: - desc: Install playwright + install-camoufox: + desc: Install Camoufox cmds: - - uv run playwright install chromium --with-deps + - uv run camoufox fetch silent: true interactive: true diff --git a/uv.lock b/uv.lock index 3919492..eea4ad5 100644 --- a/uv.lock +++ b/uv.lock @@ -117,6 +117,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, ] +[[package]] +name = "apify-fingerprint-datapoints" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/f1/b74f95767581372ab849c8b13e384b62f60d034584892c60c4a3442d9312/apify_fingerprint_datapoints-0.13.0.tar.gz", hash = "sha256:263141c19e9bc90a821e6b4e2b845925f17e0b8fbd53a897fc71546bd50df7f1", size = 934827, upload-time = "2026-05-04T09:08:45.036Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/58/8402442bf6af5a3a8068fe5431c42ea4f73c1eb18f621f9bf7c5de80caf5/apify_fingerprint_datapoints-0.13.0-py3-none-any.whl", hash = "sha256:0213d42297be19e8035202b41fb2e840a1e5d79874c99c882a5027a7d0b1a0eb", size = 761652, upload-time = "2026-05-04T09:08:43.347Z" }, +] + [[package]] name = "ast-serialize" version = "0.5.0" @@ -220,6 +229,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, ] +[[package]] +name = "browserforge" +version = "1.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "apify-fingerprint-datapoints" }, + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/6f/8975af88d203efd70cc69477ebac702babef38201d04621c9583f2508f25/browserforge-1.2.4.tar.gz", hash = "sha256:05686473793769856ebd3528c69071f5be0e511260993e8b2ba839863711a0c4", size = 36700, upload-time = "2026-02-03T02:52:09.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/35/ce962f738ae28ffce6293e7607b129075633e6bb185a5ab87e49246eedc2/browserforge-1.2.4-py3-none-any.whl", hash = "sha256:fb1c14e62ac09de221dcfc73074200269f697596c642cb200ceaab1127a17542", size = 37890, upload-time = "2026-02-03T02:52:08.745Z" }, +] + [[package]] name = "cached-property" version = "2.0.1" @@ -250,6 +272,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/86/93/1f76c8d1bafe3b0614e06b2195784a3765bbf7b0a067661af9e2dd47fc33/caio-0.9.25-py3-none-any.whl", hash = "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40", size = 19087, upload-time = "2025-12-26T15:22:00.221Z" }, ] +[[package]] +name = "camoufox" +version = "0.4.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "browserforge" }, + { name = "click" }, + { name = "language-tags" }, + { name = "lxml" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "orjson" }, + { name = "platformdirs" }, + { name = "playwright" }, + { name = "pysocks" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "screeninfo" }, + { name = "tqdm" }, + { name = "typing-extensions" }, + { name = "ua-parser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d3/15/e0a1b586e354ea6b8d6612717bf4372aaaa6753444d5d006caf0bb116466/camoufox-0.4.11.tar.gz", hash = "sha256:0a2c9d24ac5070c104e7c2b125c0a3937f70efa416084ef88afe94c32a72eebe", size = 64409, upload-time = "2025-01-29T09:33:20.019Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/7b/a2f099a5afb9660271b3f20f6056ba679e7ab4eba42682266a65d5730f7e/camoufox-0.4.11-py3-none-any.whl", hash = "sha256:83864d434d159a7566990aa6524429a8d1a859cbf84d2f64ef4a9f29e7d2e5ff", size = 71628, upload-time = "2025-01-29T09:33:18.558Z" }, +] + [[package]] name = "celery" version = "5.6.3" @@ -676,6 +725,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] +[[package]] +name = "cython" +version = "3.2.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/3b/ebd94c8b85f8e41b5015a9ed94ee3df866024d480d05cd08b774684fb81d/cython-3.2.5.tar.gz", hash = "sha256:3dd42e4cf36ad15f265bdfec2337cc00c688c8eb6d374ffd13bb19437c27bba1", size = 3286381, upload-time = "2026-05-23T19:34:08.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/b1/0240e3b04fb3c8744bc22dac830284fac1821a44d1afa7da6dceba307e87/cython-3.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:220e8b160b2a4ddc362ad8a8c2ab885aa7156099702cdc48f6518a5de921b553", size = 2969751, upload-time = "2026-05-23T19:34:24.065Z" }, + { url = "https://files.pythonhosted.org/packages/29/d6/f300e5ff4569f706f174ca0eeaadff33c81f4191fe9829c54f261abeb405/cython-3.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5887c24ebd19604b7a76d8ea57446cb562a590f7f2557e5954a69aae38b3195e", size = 2962591, upload-time = "2026-05-23T19:34:32.497Z" }, + { url = "https://files.pythonhosted.org/packages/20/a6/efc97000fdb2f34e2431eb09a6ab4de9fbd3bcdb73a8f9d224afa4a9abd3/cython-3.2.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eb38b89e5a8eb2508a1a0832063826b0703dfb02be84e4aa34b8818ce0ca50fe", size = 2979670, upload-time = "2026-05-23T19:34:41.281Z" }, + { url = "https://files.pythonhosted.org/packages/a3/de/e3e0cf5704fe569d54b8cd5dc316c9fbf08b1b74728732f86e90168b7a3f/cython-3.2.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:224149d18d980e6ea5001b70fc7ce096c1891d59035dfa9cc5ede50f55804913", size = 2879054, upload-time = "2026-05-23T19:35:18.265Z" }, + { url = "https://files.pythonhosted.org/packages/d4/5c/9cd909e6a8bb178e4e0f9a2a9227c8201a2be38abe45ada4a4c3e9154277/cython-3.2.5-py3-none-any.whl", hash = "sha256:dc1c8cebb7df5bce37f5f8dc1e5bf04313272a5973d50a55c0ec76c83812911b", size = 1257622, upload-time = "2026-05-23T19:34:05.163Z" }, +] + [[package]] name = "defusedxml" version = "0.7.1" @@ -696,17 +758,16 @@ wheels = [ [[package]] name = "ecoindex-api" -version = "3.11.1" +version = "3.12.0" source = { editable = "projects/ecoindex_api" } dependencies = [ { name = "aiofile", version = "3.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "aiofile", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "alembic" }, + { name = "camoufox" }, { name = "celery" }, { name = "cryptography" }, { name = "fastapi" }, - { name = "playwright" }, - { name = "playwright-stealth" }, { name = "pydantic", extra = ["email"] }, { name = "pydantic-settings" }, { name = "pyyaml" }, @@ -733,21 +794,19 @@ dev = [ { name = "watchdog" }, ] worker = [ + { name = "camoufox" }, { name = "pillow" }, - { name = "playwright" }, - { name = "playwright-stealth" }, ] [package.metadata] requires-dist = [ { name = "aiofile", specifier = ">=3.8.8" }, { name = "alembic", specifier = ">=1.12.1" }, + { name = "camoufox", specifier = ">=0.4.11" }, { name = "celery", specifier = ">=5.3.4" }, { name = "cryptography", specifier = ">=44.0.2" }, { name = "fastapi", specifier = ">=0.109.1" }, { name = "pillow", marker = "extra == 'webp'", specifier = ">=12.2.0" }, - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, { name = "pydantic", extras = ["email"], specifier = ">=2.1.1,<=2.4.2" }, { name = "pydantic-settings", specifier = ">=2.0.3" }, { name = "pyyaml", specifier = ">=6.0.1" }, @@ -768,18 +827,18 @@ dev = [ { name = "watchdog", specifier = ">=6.0.0" }, ] worker = [ + { name = "camoufox", specifier = ">=0.4.11" }, { name = "pillow", specifier = ">=12.2.0" }, - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, ] [[package]] name = "ecoindex-cli" -version = "2.30.0" +version = "2.30.1" source = { editable = "projects/ecoindex_cli" } dependencies = [ { name = "aiofile", version = "3.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "aiofile", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "camoufox" }, { name = "click" }, { name = "click-spinner" }, { name = "jinja2" }, @@ -787,8 +846,6 @@ dependencies = [ { name = "matplotlib" }, { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "playwright" }, - { name = "playwright-stealth" }, { name = "pydantic" }, { name = "pydantic-settings" }, { name = "pyyaml" }, @@ -801,14 +858,13 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "aiofile", specifier = ">=3.8.8" }, + { name = "camoufox", specifier = ">=0.4.11" }, { name = "click", specifier = ">=8.0.0" }, { name = "click-spinner", specifier = ">=0.1.10" }, { name = "jinja2", specifier = ">=3.1.2" }, { name = "loguru", specifier = ">=0.7.2" }, { name = "matplotlib", specifier = ">=3.8.0" }, { name = "pandas", specifier = ">=2.1.2" }, - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, { name = "pydantic", specifier = ">=2.4.2" }, { name = "pydantic-settings", specifier = ">=2.0.3" }, { name = "pyyaml", specifier = ">=6.0.1" }, @@ -820,7 +876,7 @@ requires-dist = [ [[package]] name = "ecoindex-compute" -version = "5.9.0" +version = "5.10.0" source = { editable = "projects/ecoindex_compute" } dependencies = [ { name = "aiofile", version = "3.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -895,8 +951,7 @@ dev = [ { name = "watchdog" }, ] scraper = [ - { name = "playwright" }, - { name = "playwright-stealth" }, + { name = "camoufox" }, ] scraper-webp = [ { name = "pillow" }, @@ -955,19 +1010,15 @@ dev = [ { name = "types-requests", specifier = ">=2.31.0.10" }, { name = "watchdog", specifier = ">=6.0.0" }, ] -scraper = [ - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, -] +scraper = [{ name = "camoufox", specifier = ">=0.4.11" }] scraper-webp = [{ name = "pillow", specifier = ">=12.2.0" }] [[package]] name = "ecoindex-scraper" -version = "3.16.0" +version = "3.17.0" source = { editable = "projects/ecoindex_scraper" } dependencies = [ - { name = "playwright" }, - { name = "playwright-stealth" }, + { name = "camoufox" }, { name = "pydantic" }, { name = "pyyaml" }, { name = "setuptools" }, @@ -982,9 +1033,8 @@ webp = [ [package.metadata] requires-dist = [ + { name = "camoufox", specifier = ">=0.4.11" }, { name = "pillow", marker = "extra == 'webp'", specifier = ">=12.2.0" }, - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, { name = "pydantic", specifier = ">=2.4.2" }, { name = "pyyaml", specifier = ">=6.0.1" }, { name = "setuptools", specifier = ">=69.5.1,<79.0.0" }, @@ -1348,6 +1398,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/0f/834427d8c03ff1d7e867d3db3d176470c64871753252b21b4f4897d1fa45/kombu-5.6.2-py3-none-any.whl", hash = "sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93", size = 214219, upload-time = "2025-12-29T20:30:05.74Z" }, ] +[[package]] +name = "language-tags" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/39/d1/b37165bed9016c19e2cdecf4176fbd902a014fb8a87dd44415438e190969/language_tags-1.3.1.tar.gz", hash = "sha256:b15f05505dad3ad296a1782d5a6083fd141309186094d1ab08095348f4203f37", size = 222642, upload-time = "2026-05-08T11:46:24.418Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/21/b48b9e8408b3faec9fb7cb2f68352c7724add35a980c948eaceb29ac41e4/language_tags-1.3.1-py3-none-any.whl", hash = "sha256:f7db7ef8879523019603e09644a86d95ba60595ce5e5ea82d46e8971b0f68f7b", size = 216654, upload-time = "2026-05-08T11:46:17.836Z" }, +] + [[package]] name = "librt" version = "0.11.0" @@ -1726,6 +1785,57 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, ] +[[package]] +name = "orjson" +version = "3.11.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/5d/b95ca542a001135cc250a49370f282f578c8f4e46cc8617d73775297eea8/orjson-3.11.9-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:135869ef917b8704ea0a94e01620e0c05021c15c52036e4663baffe75e72f8ce", size = 228986, upload-time = "2026-05-06T15:09:14.765Z" }, + { url = "https://files.pythonhosted.org/packages/80/01/be33fbff646e22f93398429ea645f20d2097aea1a6cdc1e6628e70125f83/orjson-3.11.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115ab5f5f4a0f203cc2a5f0fb09aee503a3f771aa08392949ab5ca230c4fbdbd", size = 132558, upload-time = "2026-05-06T15:09:17.431Z" }, + { url = "https://files.pythonhosted.org/packages/4e/61/73d49333bba660a075daccca10970dc6409ce1cf42ae4046646a19468aad/orjson-3.11.9-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4da3c38a2083ca4aaf9c2a36776cce3e9328e6647b10d118948f3cfb4913ffe4", size = 128213, upload-time = "2026-05-06T15:09:18.719Z" }, + { url = "https://files.pythonhosted.org/packages/1f/7d/30e844b3dac3f74aed66b1f984daf9db3c98c0328c03d965a9e8dc06449e/orjson-3.11.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53b50b0e14084b8f7e29c5ce84c5af0f1160169b30d8a6914231d97d2fe297d4", size = 135430, upload-time = "2026-05-06T15:09:20.257Z" }, + { url = "https://files.pythonhosted.org/packages/16/64/bd815f5c610b3facc204f26ba94e87a9eb49b0d83de3d5fc1eee2402d91b/orjson-3.11.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:231742b4a11dad8d5380a435962c57e91b7c37b79be858f4ef1c0df1a259897e", size = 146178, upload-time = "2026-05-06T15:09:21.616Z" }, + { url = "https://files.pythonhosted.org/packages/c7/35/e744fd36c79b339d27beb06068b5a08a8882ef5418804d0ce545a31f718d/orjson-3.11.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34fd2317602587321faab75ab76c623a0117e80841a6413654f04e47f339a8fb", size = 133068, upload-time = "2026-05-06T15:09:23.228Z" }, + { url = "https://files.pythonhosted.org/packages/2a/56/d54152b67b63a0b3e556cfc549d6ce84f74d7f425ddeadc6c8a74d913da7/orjson-3.11.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f3db16e69b667b132e0f305a833d5497da302d801508cbb051ed9a9819da47", size = 134217, upload-time = "2026-05-06T15:09:24.847Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ee/66154baf69f71c7164a268a5e888908aec5a0819d13c81d5e2755a257758/orjson-3.11.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0b34789fa0da61cf7bef0546b09c738fb195331e017e477096d129e9105ab03d", size = 141917, upload-time = "2026-05-06T15:09:26.647Z" }, + { url = "https://files.pythonhosted.org/packages/09/d3/c5824260ca8b9d7ba82648d042a3f8f4815d18c15bb98a1f30edd1bb2d83/orjson-3.11.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:87e4d4ab280b0c87424d47695bec2182caf8cfc17879ea78dab76680194abc13", size = 415356, upload-time = "2026-05-06T15:09:28.252Z" }, + { url = "https://files.pythonhosted.org/packages/64/cb/509c2e816fe4df641d93dc92f6a89adc8df3ada8ebdee2bd44aba3264c3c/orjson-3.11.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ace6c58523302d3b97b6ac5c38a5298a54b473762b6be82726b4265c41029f92", size = 148112, upload-time = "2026-05-06T15:09:29.783Z" }, + { url = "https://files.pythonhosted.org/packages/db/b5/3ceae56d2e4962979eedb023ba6a46a4bb65f333960379be0ca470686220/orjson-3.11.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:97d0d932803c1b164fde11cb542a9efcb1e0f63b184537cca65887147906ff48", size = 137112, upload-time = "2026-05-06T15:09:31.432Z" }, + { url = "https://files.pythonhosted.org/packages/d7/7a/81fa3f2c7bef79b04cf2ab7838e5ac74b1f12511ceab979759b0275d6bb4/orjson-3.11.9-cp310-cp310-win32.whl", hash = "sha256:b3afcf569c15577a9fe64627292daa3e6b3a70f4fb77a5df246a87ec21681b94", size = 131706, upload-time = "2026-05-06T15:09:32.707Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d8/b64600f9083c7f151ad39717a5877fccbeb0ef6d7efcb55f971ce00b6bee/orjson-3.11.9-cp310-cp310-win_amd64.whl", hash = "sha256:8697ab6a080a5c46edaad50e2bc5bd8c7ca5c66442d24104fa44ec74910a8244", size = 127282, upload-time = "2026-05-06T15:09:33.955Z" }, + { url = "https://files.pythonhosted.org/packages/1e/51/3fb9e65ae76ee97bd611869a503fa3fc0a6e81dd8b737cf3003f682df7ff/orjson-3.11.9-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f01c4818b3fc9b0da8e096722a84318071eaa118df35f6ed2344da0e73a5444f", size = 228522, upload-time = "2026-05-06T15:09:35.362Z" }, + { url = "https://files.pythonhosted.org/packages/16/fa/9d54b07cb3f3b0bfd57841478e42d7a0ece4a9f49f9907eecf5a45461687/orjson-3.11.9-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:3ebca4179031ee716ed076ffadc29428e900512f6fccee8614c9983157fcf19c", size = 128463, upload-time = "2026-05-06T15:09:37.063Z" }, + { url = "https://files.pythonhosted.org/packages/88/b1/6ceafc2eefd0a553e3be77ce6c49d107e772485d9568629376171c50e634/orjson-3.11.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48ee05097750de0ff69ed5b7bbcf0732182fd57a24043dcc2a1da780a5ead3a5", size = 132306, upload-time = "2026-05-06T15:09:38.299Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/f11311285324a40aab1e3031385c50b635a7cd0734fdaf60c7e89a696f60/orjson-3.11.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6082706765a95a6680d812e1daf1c0cfe8adec7831b3ff3b625693f3b461b1c", size = 127988, upload-time = "2026-05-06T15:09:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/9e/85/0ef63bcf1337f44031ce9b91b1919563f62a37527b3ea4368bb15a22e5d7/orjson-3.11.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:277fefe9d76ee17eb14debf399e3533d4d63b5f677a4d3719eb763536af1f4bd", size = 135188, upload-time = "2026-05-06T15:09:40.957Z" }, + { url = "https://files.pythonhosted.org/packages/05/94/b0d27090ea8a2095db3c2bd1b1c96f96f19bbb494d7fef33130e846e613d/orjson-3.11.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03db380e3780fa0015ed776a90f20e8e20bb11dde13b216ce19e5718e3dfba62", size = 145937, upload-time = "2026-05-06T15:09:42.249Z" }, + { url = "https://files.pythonhosted.org/packages/09/eb/75d50c29c05b8054013e221e598820a365c8e64065312e75e202ed880709/orjson-3.11.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33d7d766701847dc6729846362dc27895d2f2d2251264f9d10e7cb9878194877", size = 132758, upload-time = "2026-05-06T15:09:43.945Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/360686f39348aa88827cb6fbf7dc606fd41c831a35235e1abf1db8e3a9e6/orjson-3.11.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147302878da387104b66bb4a8b0227d1d487e976ce41a8501916161072ed87b1", size = 133971, upload-time = "2026-05-06T15:09:45.239Z" }, + { url = "https://files.pythonhosted.org/packages/0e/30/3178eb16f3221aeef068b6f1f1ebe05f656ea5c6dffe9f6c917329fe17a3/orjson-3.11.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3513550321f8c8c811a7c3297b8a630e82dc08e4c10216d07703c997776236cd", size = 141685, upload-time = "2026-05-06T15:09:46.858Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f1/ff2f19ed0225f9680fafa42febca3570dd59444ebf190980738d376214c2/orjson-3.11.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c5d001196b89fa9cf0a4ab79766cd835b991a166e4b621ba95089edc50c429ff", size = 415167, upload-time = "2026-05-06T15:09:48.312Z" }, + { url = "https://files.pythonhosted.org/packages/9b/61/863bddf0da6e9e586765414debd54b4e58db05f560902b6d00658cb88636/orjson-3.11.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:16969c9d369c98eb084889c6e4d2d39b77c7eb38ceccf8da2a9fff62ae908980", size = 147913, upload-time = "2026-05-06T15:09:49.733Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4081492586d75b073d60c5271a8d0f05a0955cabf1e34c8473f6fcd84235/orjson-3.11.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:63e0efbc991250c0b3143488fa57d95affcabbfc63c99c48d625dd37779aafe2", size = 136959, upload-time = "2026-05-06T15:09:51.311Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bd/70b6ab193594d7abb875320c0a7c8335e846f28968c432c31042409c3c8d/orjson-3.11.9-cp311-cp311-win32.whl", hash = "sha256:14ed654580c1ed2bc217352ec82f91b047aef82951aa71c7f64e0dcb03c0e180", size = 131533, upload-time = "2026-05-06T15:09:52.637Z" }, + { url = "https://files.pythonhosted.org/packages/3f/17/1a1a228183d62d1b77e2c30d210f47dd4768b310ebe1607c63e3c0e3a71e/orjson-3.11.9-cp311-cp311-win_amd64.whl", hash = "sha256:57ea77fb70a448ce87d18fca050193202a3da5e54598f6501ca5476fb66cfe02", size = 127106, upload-time = "2026-05-06T15:09:54.204Z" }, + { url = "https://files.pythonhosted.org/packages/b8/95/285de5fa296d09681ee9c546cd4a8aeb773b701cf343dc125994f4d52953/orjson-3.11.9-cp311-cp311-win_arm64.whl", hash = "sha256:19b72ed11572a2ee51a67a903afbe5af504f84ed6f529c0fe44b0ab3fb5cc697", size = 126848, upload-time = "2026-05-06T15:09:55.551Z" }, + { url = "https://files.pythonhosted.org/packages/16/6d/11867a3ffa3a3608d84a4de51ef4dd0896d6b5cc9132fbe1daf593e677bc/orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49", size = 228515, upload-time = "2026-05-06T15:09:57.265Z" }, + { url = "https://files.pythonhosted.org/packages/24/75/05912954c8b288f34fcf5cd4b9b071cb4f6e77b9961e175e56ebb258089f/orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291", size = 128409, upload-time = "2026-05-06T15:09:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/ab/86/1c3a47df3bc8191ea9ac51603bbb872a95167a364320c269f2557911f406/orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09", size = 132106, upload-time = "2026-05-06T15:10:00.798Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cf/b33b5f3e695ae7d63feef9d915c37cc3b8f465493dcd4f8e0b4c697a2366/orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4", size = 127864, upload-time = "2026-05-06T15:10:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/31/6a/6cf69385a58208024fcb8c014e2141b8ce838aba6492b589f8acfff97fab/orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882", size = 135213, upload-time = "2026-05-06T15:10:03.515Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f8/0b1bd3e8f2efcdd376af5c8cfd79eaf13f018080c0089c80ebd724e3c7fb/orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff", size = 145994, upload-time = "2026-05-06T15:10:05.083Z" }, + { url = "https://files.pythonhosted.org/packages/f3/59/dab79f61044c529d2c81aecdc589b1f833a1c8dec11ba3b1c2498a02ca7e/orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe", size = 132744, upload-time = "2026-05-06T15:10:06.853Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/82b7a2fe5d8a67a59ed831b24d59a3d46ea7d207b66e1602d376541d94a6/orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61", size = 134014, upload-time = "2026-05-06T15:10:08.213Z" }, + { url = "https://files.pythonhosted.org/packages/50/c7/375e83a76851b73b2e39f3bcf0e5a19e2b89bad13e5bca97d0b293d27f24/orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2", size = 141509, upload-time = "2026-05-06T15:10:09.595Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7c/49d5d82a3d3097f641f094f552131f1e2723b0b8cb0fa2874ab65ecfffa6/orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206", size = 415127, upload-time = "2026-05-06T15:10:11.049Z" }, + { url = "https://files.pythonhosted.org/packages/3a/dc/7446c538590d55f455647e5f3c61fc33f7108714e7afcffa6a2a033f8350/orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f", size = 148025, upload-time = "2026-05-06T15:10:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/df/e5/4d2d8af06f788329b4f78f8cc3679bb395392fcaa1e4d8d3c33e85308fa4/orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa", size = 136943, upload-time = "2026-05-06T15:10:14.405Z" }, + { url = "https://files.pythonhosted.org/packages/06/69/850264ccf6d80f6b174620d30a87f65c9b1490aba33fe6b62798e618cad3/orjson-3.11.9-cp312-cp312-win32.whl", hash = "sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470", size = 131606, upload-time = "2026-05-06T15:10:15.791Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/973a43fc9c55e20f2051e9830997649f669be0cb3ca52192087c0143f118/orjson-3.11.9-cp312-cp312-win_amd64.whl", hash = "sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be", size = 127101, upload-time = "2026-05-06T15:10:17.129Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/495470f0e4a18f73fa10b7f6b84b464ec4cc5291c4e0c7c2a6c400bef006/orjson-3.11.9-cp312-cp312-win_arm64.whl", hash = "sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624", size = 126736, upload-time = "2026-05-06T15:10:18.645Z" }, +] + [[package]] name = "packaging" version = "26.2" @@ -1908,18 +2018,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/c8/210f282d278e4709cdd71b12a31af45a30a22ab3207b387e29b37e478713/playwright-1.60.0-py3-none-win_arm64.whl", hash = "sha256:6e4f6700a4c2250efff8e690a81d66e3855754fb587b6b87cf5c784014f91537", size = 34037981, upload-time = "2026-05-18T12:00:57.584Z" }, ] -[[package]] -name = "playwright-stealth" -version = "2.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "playwright" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a4/db/6ade5d539c7d151b9defc78fafa8b65aa52352617d0e7699b47008bd801f/playwright_stealth-2.0.3.tar.gz", hash = "sha256:1d8e488fbdd8f190f1269ea8cf5d57d14df3a9f1af1001c41ee3588b2aac3133", size = 25751, upload-time = "2026-04-04T02:50:33.88Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/10/607c409712c02a26c4cb794820514cb7fdaaeac15fb05bed917fb8a354b3/playwright_stealth-2.0.3-py3-none-any.whl", hash = "sha256:1887ade423ab7ff8ae16d363a30a38de0b5817e1e4a29d47b74bf3a0e3dbfcb4", size = 34385, upload-time = "2026-04-04T02:50:35.246Z" }, -] - [[package]] name = "pluggy" version = "1.6.0" @@ -2094,6 +2192,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pyobjc-core" +version = "12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/e8/a6cc12669211e7c9b29e8f26bf2159e67c7a73555dc229018abf46d8167a/pyobjc_core-12.2.tar.gz", hash = "sha256:51d7de4cfa32f508c6a7aac31f131b12d5e196a8dcf588e6e8d7e6337224f66d", size = 1062064, upload-time = "2026-05-30T12:29:55.417Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/cd/2e8cc2d648648186aab7e3eea76d89ad02f10eb752f3846c1aaba2c93e22/pyobjc_core-12.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:45fffb2b8dc0ae4480386429bd1e7fa8aabeb2eec81816d6971b33936dfcfff8", size = 6478736, upload-time = "2026-05-30T09:47:42.912Z" }, + { url = "https://files.pythonhosted.org/packages/c3/dc/b68d8bd769865d509fbcab81f5fae6497bd4e33409a44925e5d5e7c68d72/pyobjc_core-12.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:17b4e003af384d3086c2f39989a0b282c5755dd1066f331cf7c9fa65febe22ce", size = 6475918, upload-time = "2026-05-30T10:00:44.669Z" }, + { url = "https://files.pythonhosted.org/packages/24/be/4771f4fd786f0e1a2bd6d8931a72a5f3929b7bb1b28a1fe6ca8a08371c55/pyobjc_core-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7677ed758a367bbbb5589d6f5276fb360a45c89168276c26162f61840b0fa03d", size = 6421145, upload-time = "2026-05-30T10:17:41.992Z" }, +] + +[[package]] +name = "pyobjc-framework-cocoa" +version = "12.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyobjc-core", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/cc/927169225e72bab9c9b44285656768fb75052a0bc85fdbca62740e1ca43c/pyobjc_framework_cocoa-12.2.tar.gz", hash = "sha256:20b392e2b7241caad0538dfde12143343e5dfe48f72e7df660a7548e635903dc", size = 3125555, upload-time = "2026-05-30T12:35:09.273Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/db/e86555b282c38bb0aadb63bf961c17e3ca70252774a677945f6bc4ee19c0/pyobjc_framework_cocoa-12.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a2efad455ded551d1a033fd22245d57b71b6c065e8b52158dceed1611b5ae033", size = 387272, upload-time = "2026-05-30T11:57:27.512Z" }, + { url = "https://files.pythonhosted.org/packages/a2/10/95edbdee61731dc0e18633071fe56a4220879a92e9ba77c330a34add4b28/pyobjc_framework_cocoa-12.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0bb7cd468ca89bbc2d1d8acb930b4f4fdce8826de30a66608fc28f8880c1f6a6", size = 387272, upload-time = "2026-05-30T11:57:51.391Z" }, + { url = "https://files.pythonhosted.org/packages/30/66/5a91f2eddfced4f26bc2df2bcebb7f5f10c5bf5666aff6fa00ded845af07/pyobjc_framework_cocoa-12.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:06cb92d97d1af9d1f459ae6cf1d1a7b824c12d3aff1b709885966acd6b7208c2", size = 388093, upload-time = "2026-05-30T11:58:14.921Z" }, +] + [[package]] name = "pyopenssl" version = "26.2.0" @@ -2122,6 +2245,15 @@ version = "2.1.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d5/7b/65f55513d3c769fd677f90032d8d8703e3dc17e88a41b6074d2177548bca/PyPyDispatcher-2.1.2.tar.gz", hash = "sha256:b6bec5dfcff9d2535bca2b23c80eae367b1ac250a645106948d315fcfa9130f2", size = 23224, upload-time = "2017-07-03T14:20:51.806Z" } +[[package]] +name = "pysocks" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, +] + [[package]] name = "pytest" version = "9.0.3" @@ -2402,6 +2534,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1d/c9/7cc7631ca85c08b7e1162e1e08ecb0c139f8f640cc403257ac500a8e50fe/scrapy-2.16.0-py3-none-any.whl", hash = "sha256:fcb83db5500743503b1fea4d55c81aac056b15481d0aad46b6f929d324d9bcf5", size = 344254, upload-time = "2026-05-19T12:29:17.206Z" }, ] +[[package]] +name = "screeninfo" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cython", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/bb/e69e5e628d43f118e0af4fc063c20058faa8635c95a1296764acc8167e27/screeninfo-0.8.1.tar.gz", hash = "sha256:9983076bcc7e34402a1a9e4d7dabf3729411fd2abb3f3b4be7eba73519cd2ed1", size = 10666, upload-time = "2022-09-09T11:35:23.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/bf/c5205d480307bef660e56544b9e3d7ff687da776abb30c9cb3f330887570/screeninfo-0.8.1-py3-none-any.whl", hash = "sha256:e97d6b173856edcfa3bd282f81deb528188aff14b11ec3e195584e7641be733c", size = 12907, upload-time = "2022-09-09T11:35:21.351Z" }, +] + [[package]] name = "sentry-sdk" version = "2.61.1" @@ -2575,6 +2720,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/43/8bd850ee71a191bf072e31302c73a66be413fecdd98fdcd111ecbcce13ca/tomlkit-0.15.0-py3-none-any.whl", hash = "sha256:4dbc8f0fc024412b57ced8757ac7461305126a648ff8c2c807fcb8e133a78738", size = 41328, upload-time = "2026-05-10T07:38:23.517Z" }, ] +[[package]] +name = "tqdm" +version = "4.68.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/1e/a5e2602851e2a8c49372e3ed4d8437fa43c996941899af7aea6e30173a3d/tqdm-4.68.0.tar.gz", hash = "sha256:c627124266fe7904cabb70e88a940d75a06b889a0b11680307a67c18ce094f19", size = 170209, upload-time = "2026-06-05T13:17:20.095Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/51/ab57af723f38a041c5027040fc6fa93a9a066ca7294fc380c1c0810813b2/tqdm-4.68.0-py3-none-any.whl", hash = "sha256:b79a3ae57db4c870a55352e43abb33b329557cd75b1483028909286ff22a2c03", size = 78247, upload-time = "2026-06-05T13:17:18.272Z" }, +] + [[package]] name = "twisted" version = "26.4.0" @@ -2677,6 +2834,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/60/8c/a9f0161f2a34752987199425fec7f0068b012a02c98551615a9b82cbd70a/ua_generator-2.1.1-py3-none-any.whl", hash = "sha256:d8fd6e39e44dc057dd232d5f808fe0f61dab4a3163903cfe24446777ecdd2ec4", size = 32769, upload-time = "2026-05-24T09:48:45.44Z" }, ] +[[package]] +name = "ua-parser" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ua-parser-builtins" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/98/5e4b52d772a048af122a6fc5ce365c311efb9f5e79c55fd4fdd7c9f59e83/ua_parser-1.0.2.tar.gz", hash = "sha256:bab404ad42fb37f943107da2f6003ffc79724d11cc95076a7a539513371779da", size = 33239, upload-time = "2026-04-05T20:14:28.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/7c/6367995ff57aaa2d9e1055adbaec2519cf5a979780a83a93fdf8c6ec37be/ua_parser-1.0.2-py3-none-any.whl", hash = "sha256:0f8e6d0484af2a9ff804bba5a4fe696e87c028eaba98ad9a7dfae873fef7788a", size = 31219, upload-time = "2026-04-05T20:14:26.913Z" }, +] + +[[package]] +name = "ua-parser-builtins" +version = "202606" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/a3/f26edd95a2ce2ffbf559da432e5ff544e8dcda3673d1c775f71afffb18b7/ua_parser_builtins-202606-py3-none-any.whl", hash = "sha256:13b483eb12a5419c1094ce02b7df705fefc6b5d869764b3ffbf6c940c6d014cb", size = 90676, upload-time = "2026-06-01T22:40:53.008Z" }, +] + [[package]] name = "urllib3" version = "2.7.0" From f77eb0fc7995554a00578235dd6e0ff4f9867f13 Mon Sep 17 00:00:00 2001 From: Quentin Lienhardt Date: Wed, 8 Jul 2026 12:40:34 +0200 Subject: [PATCH 2/5] Install runtime browser dependencies for Camoufox --- .devcontainer/Dockerfile | 10 ++++++++++ projects/ecoindex_api/docker/worker/dockerfile | 10 ++++++++++ projects/ecoindex_cli/dockerfile | 11 ++++++++++- projects/ecoindex_scraper/dockerfile | 10 ++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7784aff..ff4f9d5 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,6 +3,16 @@ FROM mcr.microsoft.com/devcontainers/python:1-3.12 # Install uv from the official image (recommended by Astral) COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ +# Install runtime browser dependencies for Camoufox +RUN apt-get update && apt-get install -y --no-install-recommends \ + libgtk-3-0 \ + libx11-xcb1 \ + libasound2 \ + libdbus-glib-1-2 \ + libgbm1 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + # Install Task (https://taskfile.dev) RUN curl -fsSL https://taskfile.dev/install.sh | sh -s -- -d -b /usr/local/bin diff --git a/projects/ecoindex_api/docker/worker/dockerfile b/projects/ecoindex_api/docker/worker/dockerfile index e141188..02b9525 100644 --- a/projects/ecoindex_api/docker/worker/dockerfile +++ b/projects/ecoindex_api/docker/worker/dockerfile @@ -17,6 +17,16 @@ WORKDIR /code RUN apt-get update && apt-get install -y --no-install-recommends git \ && rm -rf /var/lib/apt/lists/* +# Install runtime browser dependencies for Camoufox +RUN apt-get update && apt-get install -y --no-install-recommends \ + libgtk-3-0 \ + libx11-xcb1 \ + libasound2 \ + libdbus-glib-1-2 \ + libgbm1 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt diff --git a/projects/ecoindex_cli/dockerfile b/projects/ecoindex_cli/dockerfile index 9a90c7a..bf24091 100644 --- a/projects/ecoindex_cli/dockerfile +++ b/projects/ecoindex_cli/dockerfile @@ -11,9 +11,18 @@ FROM python:3.12-slim ARG wheel=ecoindex_cli-2.26.0a0-py3-none-any.whl ENV DOCKER_CONTAINER=True - WORKDIR /code +# Install runtime browser dependencies for Camoufox +RUN apt-get update && apt-get install -y --no-install-recommends \ + libgtk-3-0 \ + libx11-xcb1 \ + libasound2 \ + libdbus-glib-1-2 \ + libgbm1 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt diff --git a/projects/ecoindex_scraper/dockerfile b/projects/ecoindex_scraper/dockerfile index 7668d5c..ed71b40 100644 --- a/projects/ecoindex_scraper/dockerfile +++ b/projects/ecoindex_scraper/dockerfile @@ -9,6 +9,16 @@ COPY projects/ecoindex_scraper/ projects/ecoindex_scraper/ COPY bases/ bases/ COPY components/ components/ +# Install runtime browser dependencies for Camoufox +RUN apt-get update && apt-get install -y --no-install-recommends \ + libgtk-3-0 \ + libx11-xcb1 \ + libasound2 \ + libdbus-glib-1-2 \ + libgbm1 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + RUN uv sync --package ecoindex_scraper --frozen --no-dev --no-editable ENV PATH="/code/.venv/bin:$PATH" From ea0299667da59988766834a036b3157e5da3ebc8 Mon Sep 17 00:00:00 2001 From: Quentin Lienhardt Date: Wed, 8 Jul 2026 14:03:05 +0200 Subject: [PATCH 3/5] Update uv.lock --- uv.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uv.lock b/uv.lock index 15cef96..67d4d22 100644 --- a/uv.lock +++ b/uv.lock @@ -769,7 +769,7 @@ requires-dist = [ { name = "aiofile", specifier = ">=3.8.8" }, { name = "alembic", specifier = ">=1.12.1" }, { name = "camoufox", specifier = ">=0.4.11" }, - { name = "cryptography", specifier = ">=44.0.2" }, + { name = "cryptography", specifier = ">=48.0.1" }, { name = "fastapi", specifier = ">=0.109.1" }, { name = "pillow", marker = "extra == 'webp'", specifier = ">=12.2.0" }, { name = "pydantic", extras = ["email"], specifier = ">=2.1.1,<=2.4.2" }, @@ -929,7 +929,7 @@ scraper-webp = [ [package.metadata] requires-dist = [ { name = "aiofile", specifier = ">=3.8.8" }, - { name = "cryptography", specifier = ">=44.0.2" }, + { name = "cryptography", specifier = ">=48.0.1" }, { name = "haralyzer", specifier = ">=2.4.0" }, { name = "loguru", specifier = ">=0.7.2" }, { name = "pydantic", specifier = ">=2.4.2" }, From e84fdb97df1754a38627c889f250a205f629ed3e Mon Sep 17 00:00:00 2001 From: Quentin Lienhardt Date: Wed, 2 Sep 2026 10:02:28 +0200 Subject: [PATCH 4/5] Bump camoufox (fixed compability with newer playwright) --- README.md | 2 +- projects/ecoindex_api/README.md | 4 +- projects/ecoindex_api/pyproject.toml | 4 +- projects/ecoindex_cli/README.md | 2 +- projects/ecoindex_cli/pyproject.toml | 2 +- projects/ecoindex_compute/README.md | 1 - projects/ecoindex_scraper/pyproject.toml | 2 +- pyproject.toml | 3 +- uv.lock | 356 ++++++++++++++++++++--- 9 files changed, 322 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 95cf994..b216bad 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ For API development, also run: task api:init-dev-project ``` -This installs dependencies, Playwright, creates the `.env` file and runs database migrations. +This installs dependencies, Camoufox, creates the `.env` file and runs database migrations. ### Usage diff --git a/projects/ecoindex_api/README.md b/projects/ecoindex_api/README.md index f0262be..61e3e2f 100644 --- a/projects/ecoindex_api/README.md +++ b/projects/ecoindex_api/README.md @@ -52,7 +52,7 @@ cp .env.template .env && \ task api:docker-up-postgres -- -d ``` -Every services should start normaly, then you can go to: +Every services should start normally, then you can go to: - [http://localhost:8001/docs](http://localhost:8001/docs) to access to the swagger of the API @@ -142,7 +142,7 @@ From the repository root: ```bash task uv:install # Install Python 3.12 and all dependencies -task api:init-dev-project # Initialize API dev environment (Playwright, .env, migrations) +task api:init-dev-project # Initialize API dev environment (Camoufox, .env, migrations) ``` ### Run the API locally diff --git a/projects/ecoindex_api/pyproject.toml b/projects/ecoindex_api/pyproject.toml index 057e4f5..65e9196 100644 --- a/projects/ecoindex_api/pyproject.toml +++ b/projects/ecoindex_api/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "rq>=2.1.0", "cryptography>=48.0.1", "fastapi>=0.109.1", - "camoufox>=0.4.11", + "camoufox>=0.5.5", "pydantic[email]>=2.1.1,<=2.4.2", "pydantic-settings>=2.0.3", "pyyaml>=6.0.1", @@ -41,7 +41,7 @@ backend = [ ] worker = [ "pillow>=12.2.0", - "camoufox>=0.4.11", + "camoufox>=0.5.5", ] dev = [ "aiomysql>=0.2.0", diff --git a/projects/ecoindex_cli/README.md b/projects/ecoindex_cli/README.md index a1cc689..60c66a8 100644 --- a/projects/ecoindex_cli/README.md +++ b/projects/ecoindex_cli/README.md @@ -342,7 +342,7 @@ width,height,url,size,nodes,requests,grade,score,ges,water,date,page_type ### Requirements -- Python 3.10+ +- Python ^3.10 with [pip](https://pip.pypa.io/en/stable/installation/) - [uv](https://docs.astral.sh/uv/getting-started/installation/) - [Task](https://taskfile.dev/#/installation) diff --git a/projects/ecoindex_cli/pyproject.toml b/projects/ecoindex_cli/pyproject.toml index 7008de6..ec2f54f 100644 --- a/projects/ecoindex_cli/pyproject.toml +++ b/projects/ecoindex_cli/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ "loguru>=0.7.2", "matplotlib>=3.8.0", "pandas>=2.1.2", - "camoufox>=0.4.11", + "camoufox>=0.5.5", "pydantic>=2.4.2", "pydantic-settings>=2.0.3", "pyyaml>=6.0.1", diff --git a/projects/ecoindex_compute/README.md b/projects/ecoindex_compute/README.md index b392ab2..66a1275 100644 --- a/projects/ecoindex_compute/README.md +++ b/projects/ecoindex_compute/README.md @@ -48,4 +48,3 @@ Result example: ```python Ecoindex(grade='B', score=72.0, ges=1.56, water=2.34, ecoindex_version='3.0.0') ``` - diff --git a/projects/ecoindex_scraper/pyproject.toml b/projects/ecoindex_scraper/pyproject.toml index 6e30bee..b65b4fc 100644 --- a/projects/ecoindex_scraper/pyproject.toml +++ b/projects/ecoindex_scraper/pyproject.toml @@ -7,7 +7,7 @@ requires-python = ">=3.10,<3.13" license = { text = "Creative Commons BY-NC-ND" } authors = [{ name = "Vincent Vatelot", email = "vincent.vatelot@ik.me" }] dependencies = [ - "camoufox>=0.4.11", + "camoufox>=0.5.5", "pydantic>=2.4.2", "pyyaml>=6.0.1", "setuptools>=83.0.0,<85.0.0", diff --git a/pyproject.toml b/pyproject.toml index 441ce13..f8e7821 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,8 +25,7 @@ update-values = "ecoindex.scripts:update_values" [dependency-groups] scraper = [ - "playwright>=1.39.0", - "playwright-stealth>=1.0.6", + "camoufox>=0.5.5", ] scraper-webp = [ "pillow>=12.2.0", diff --git a/uv.lock b/uv.lock index 4e35a85..6f6e52c 100644 --- a/uv.lock +++ b/uv.lock @@ -109,6 +109,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] +[[package]] +name = "ansicon" +version = "1.89.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/e2/1c866404ddbd280efedff4a9f15abfe943cb83cde6e895022370f3a61f85/ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1", size = 67312, upload-time = "2019-04-29T20:23:57.314Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/f9/f1c10e223c7b56a38109a3f2eb4e7fe9a757ea3ed3a166754fb30f65e466/ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec", size = 63675, upload-time = "2019-04-29T20:23:53.83Z" }, +] + [[package]] name = "anyio" version = "4.14.1" @@ -123,6 +132,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" }, ] +[[package]] +name = "apify-fingerprint-datapoints" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/3e/d52b92dd3efe64d21b7652cc8b85d6948beef220c24ceaafd95f4d587047/apify_fingerprint_datapoints-0.15.0.tar.gz", hash = "sha256:5776fe73feaa3910265cae55599552b098f3a716d8872c9a0c2295ff2bb680dc", size = 943698, upload-time = "2026-08-05T14:08:57.213Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/05/14521197ae61dba29f01dd23264d7457aa402568b92bf3fb89fd12a5d6db/apify_fingerprint_datapoints-0.15.0-py3-none-any.whl", hash = "sha256:fc9299b3136880f47b468897cd00ac622ad2c2a93e9cc9f972d10dbf5ed2b3aa", size = 761805, upload-time = "2026-08-05T14:08:55.518Z" }, +] + [[package]] name = "arrow" version = "1.4.0" @@ -312,6 +330,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, ] +[[package]] +name = "blessed" +version = "1.49.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinxed" }, + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/0a/5ad035b1fb3ce21f8d2892f697c17b9ff7b9f5905aeddab1ce438b7fd48c/blessed-1.49.0.tar.gz", hash = "sha256:a1c5e15c895898b976d85e6c7278496b325bbb4e656794413bf5c1648c9c12c9", size = 14055928, upload-time = "2026-08-31T15:04:20.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/d9/d0a5f6cce623ae6fecda1a59d87a360ac2cd971ac6d4d06d3b3d832a2ccc/blessed-1.49.0-py3-none-any.whl", hash = "sha256:770279b4066218f38a06ee87976ccc658fbffd4ffb43a5ee1e9a5eef3d4f3ba0", size = 138119, upload-time = "2026-08-31T15:04:18.308Z" }, +] + [[package]] name = "blinker" version = "1.9.0" @@ -407,6 +438,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4e/17/17c22d48819001ca08cadab63b09b00e0c56a7579478aa7c2623f4280de6/brotlicffi-1.2.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:5eb5563173afb92c9111b180349ff17d7c83c79febabadca5de983b552565c3c", size = 378395, upload-time = "2026-08-21T17:29:16.857Z" }, ] +[[package]] +name = "browserforge" +version = "1.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "apify-fingerprint-datapoints" }, + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/6f/8975af88d203efd70cc69477ebac702babef38201d04621c9583f2508f25/browserforge-1.2.4.tar.gz", hash = "sha256:05686473793769856ebd3528c69071f5be0e511260993e8b2ba839863711a0c4", size = 36700, upload-time = "2026-02-03T02:52:09.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/35/ce962f738ae28ffce6293e7607b129075633e6bb185a5ab87e49246eedc2/browserforge-1.2.4-py3-none-any.whl", hash = "sha256:fb1c14e62ac09de221dcfc73074200269f697596c642cb200ceaab1127a17542", size = 37890, upload-time = "2026-02-03T02:52:08.745Z" }, +] + [[package]] name = "cached-property" version = "2.0.1" @@ -437,6 +481,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/86/93/1f76c8d1bafe3b0614e06b2195784a3765bbf7b0a067661af9e2dd47fc33/caio-0.9.25-py3-none-any.whl", hash = "sha256:06c0bb02d6b929119b1cfbe1ca403c768b2013a369e2db46bfa2a5761cf82e40", size = 19087, upload-time = "2025-12-26T15:22:00.221Z" }, ] +[[package]] +name = "camoufox" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "browserforge" }, + { name = "inquirer" }, + { name = "language-tags" }, + { name = "lxml" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "orjson" }, + { name = "platformdirs" }, + { name = "playwright" }, + { name = "pysocks" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "rich" }, + { name = "rich-click" }, + { name = "screeninfo" }, + { name = "typing-extensions" }, + { name = "ua-parser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/13/f413d29251d15da0e43278aede5db16126d71cfd210a6ca2afcbfcb109ac/camoufox-0.5.5.tar.gz", hash = "sha256:18b7ab7f8472efb2a88e72358265aa3eb57aebd7f87d93bdfa8656dcab7035ba", size = 1386265, upload-time = "2026-08-18T01:23:00.861Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/5c/76e400fac42c166cb950361a918859c052c6cefd4248f230833008c19fc4/camoufox-0.5.5-py3-none-any.whl", hash = "sha256:8e1ca8bc956e15b6fff0fa51190fa46eb08c3df66e917948423d4eb3f810d81a", size = 1397373, upload-time = "2026-08-18T01:22:59.278Z" }, +] + [[package]] name = "certifi" version = "2026.6.17" @@ -810,6 +883,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] +[[package]] +name = "cython" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/d8/4981ef716ad0e3ff0d3ef383aefc6b03c4a88dee33b272bf8e0d833001ca/cython-3.3.0.tar.gz", hash = "sha256:eed0d93fbca7087f143b42c34b05a825849bdf17f101572c2105acfa49aa88b8", size = 3727515, upload-time = "2026-08-22T05:16:39.493Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/62/eae58684ab7a46e877f3193f393244a4de6fd5e700cca4265a7d33664f38/cython-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0507d9caf7dc35f1212627145d5d13dbc5dd7128529a6608ab72690472fa688e", size = 3142413, upload-time = "2026-08-22T05:16:50.378Z" }, + { url = "https://files.pythonhosted.org/packages/af/67/dffaf12b7203f7e936d98b967e065c50a7883f152c51ded44ed8762128f4/cython-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ec09dbf73ff4f7be2b339b995fadae9c4bb517bbbed7ec11d6fe99c2092b48fd", size = 3134644, upload-time = "2026-08-22T05:16:59.112Z" }, + { url = "https://files.pythonhosted.org/packages/da/0f/95bad838a80ac52c9e982dad00bd9a0b2bad57fb4c688e5f53ac3ef65ff0/cython-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03bc5333932f5dda3ba9315298ecdd21daa1b58410bb1f8ce04c78ec8337130a", size = 3143472, upload-time = "2026-08-22T05:17:07.956Z" }, + { url = "https://files.pythonhosted.org/packages/14/59/bc1a84b434cb5bebb0cd6f50da8f239d35a5c141b20fdeafc2817fd87778/cython-3.3.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e0d2713d2b292c826bc21dc8732bd9e47628103aa3764180c881e04b3fef95dc", size = 3063660, upload-time = "2026-08-22T05:17:40.923Z" }, + { url = "https://files.pythonhosted.org/packages/bf/77/67b0b24e45073a699610e50f00c18474ff9b09ea29ecc95083bdf5e60acd/cython-3.3.0-py3-none-any.whl", hash = "sha256:9b24b5c8cd536946b62086fcafee6d5509d3f549f72d553d2336af87ffbe0da1", size = 1349151, upload-time = "2026-08-22T05:16:36.741Z" }, +] + [[package]] name = "defusedxml" version = "0.7.1" @@ -837,10 +923,9 @@ dependencies = [ { name = "aiofile", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "alembic" }, { name = "boto3" }, + { name = "camoufox" }, { name = "cryptography" }, { name = "fastapi" }, - { name = "playwright" }, - { name = "playwright-stealth" }, { name = "pydantic", extra = ["email"] }, { name = "pydantic-settings" }, { name = "pyyaml" }, @@ -871,9 +956,8 @@ dev = [ { name = "watchdog" }, ] worker = [ + { name = "camoufox" }, { name = "pillow" }, - { name = "playwright" }, - { name = "playwright-stealth" }, ] [package.metadata] @@ -881,11 +965,10 @@ requires-dist = [ { name = "aiofile", specifier = ">=3.8.8" }, { name = "alembic", specifier = ">=1.12.1" }, { name = "boto3", specifier = ">=1.39.0" }, + { name = "camoufox", specifier = ">=0.5.5" }, { name = "cryptography", specifier = ">=48.0.1" }, { name = "fastapi", specifier = ">=0.109.1" }, { name = "pillow", marker = "extra == 'webp'", specifier = ">=12.2.0" }, - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, { name = "pydantic", extras = ["email"], specifier = ">=2.1.1,<=2.4.2" }, { name = "pydantic-settings", specifier = ">=2.0.3" }, { name = "pyyaml", specifier = ">=6.0.1" }, @@ -910,9 +993,8 @@ dev = [ { name = "watchdog", specifier = ">=6.0.0" }, ] worker = [ + { name = "camoufox", specifier = ">=0.5.5" }, { name = "pillow", specifier = ">=12.2.0" }, - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, ] [[package]] @@ -922,6 +1004,7 @@ source = { editable = "projects/ecoindex_cli" } dependencies = [ { name = "aiofile", version = "3.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "aiofile", version = "3.11.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "camoufox" }, { name = "click" }, { name = "click-spinner" }, { name = "jinja2" }, @@ -930,8 +1013,6 @@ dependencies = [ { name = "matplotlib", version = "3.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "playwright" }, - { name = "playwright-stealth" }, { name = "pydantic" }, { name = "pydantic-settings" }, { name = "pyyaml" }, @@ -944,14 +1025,13 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "aiofile", specifier = ">=3.8.8" }, + { name = "camoufox", specifier = ">=0.5.5" }, { name = "click", specifier = ">=8.0.0" }, { name = "click-spinner", specifier = ">=0.1.10" }, { name = "jinja2", specifier = ">=3.1.2" }, { name = "loguru", specifier = ">=0.7.2" }, { name = "matplotlib", specifier = ">=3.8.0" }, { name = "pandas", specifier = ">=2.1.2" }, - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, { name = "pydantic", specifier = ">=2.4.2" }, { name = "pydantic-settings", specifier = ">=2.0.3" }, { name = "pyyaml", specifier = ">=6.0.1" }, @@ -1040,8 +1120,7 @@ dev = [ { name = "watchdog" }, ] scraper = [ - { name = "playwright" }, - { name = "playwright-stealth" }, + { name = "camoufox" }, ] scraper-webp = [ { name = "pillow" }, @@ -1101,10 +1180,7 @@ dev = [ { name = "types-requests", specifier = ">=2.31.0.10" }, { name = "watchdog", specifier = ">=6.0.0" }, ] -scraper = [ - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, -] +scraper = [{ name = "camoufox", specifier = ">=0.5.5" }] scraper-webp = [{ name = "pillow", specifier = ">=12.2.0" }] [[package]] @@ -1112,8 +1188,7 @@ name = "ecoindex-scraper" version = "3.17.0" source = { editable = "projects/ecoindex_scraper" } dependencies = [ - { name = "playwright" }, - { name = "playwright-stealth" }, + { name = "camoufox" }, { name = "pydantic" }, { name = "pyyaml" }, { name = "setuptools" }, @@ -1128,9 +1203,8 @@ webp = [ [package.metadata] requires-dist = [ + { name = "camoufox", specifier = ">=0.5.5" }, { name = "pillow", marker = "extra == 'webp'", specifier = ">=12.2.0" }, - { name = "playwright", specifier = ">=1.39.0" }, - { name = "playwright-stealth", specifier = ">=1.0.6" }, { name = "pydantic", specifier = ">=2.4.2" }, { name = "pyyaml", specifier = ">=6.0.1" }, { name = "setuptools", specifier = ">=83.0.0,<85.0.0" }, @@ -1139,6 +1213,19 @@ requires-dist = [ ] provides-extras = ["webp"] +[[package]] +name = "editor" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "runs" }, + { name = "xmod" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/5f/fe06c2a13a5282dcef4c7133bb348d4125a9aa69c5fb49037a004599d73a/editor-1.8.0.tar.gz", hash = "sha256:b07e1bbcb8b33f05c2e6ed3ce77ee9756354ada840a18aad7c0536d967fe4c0b", size = 27455, upload-time = "2026-05-09T13:42:59.796Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/b8/6648cadf38b61045262bf2a534e526f04eb833275ad1fc77a2026e9f7e3a/editor-1.8.0-py3-none-any.whl", hash = "sha256:7d47ff88ae6c5f6c43d28c30b6f7fd59a24741175a1771ab06c969d946d7dfd0", size = 4012, upload-time = "2026-05-09T13:43:00.847Z" }, +] + [[package]] name = "email-validator" version = "2.3.0" @@ -1390,6 +1477,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "inquirer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blessed" }, + { name = "editor" }, + { name = "readchar" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/79/165579fdcd3c2439503732ae76394bf77f5542f3dd18135b60e808e4813c/inquirer-3.4.1.tar.gz", hash = "sha256:60d169fddffe297e2f8ad54ab33698249ccfc3fc377dafb1e5cf01a0efb9cbe5", size = 14069, upload-time = "2025-08-02T18:36:27.901Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/fd/7c404169a3e04a908df0644893a331f253a7f221961f2b6c0cf44430ae5a/inquirer-3.4.1-py3-none-any.whl", hash = "sha256:717bf146d547b595d2495e7285fd55545cff85e5ce01decc7487d2ec6a605412", size = 18152, upload-time = "2025-08-02T18:36:26.753Z" }, +] + [[package]] name = "itemadapter" version = "0.13.1" @@ -1434,6 +1535,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "jinxed" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ansicon", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/d7/6e6d474ec5eaeca6a61acc17766bb19563b3a372b4b9d92910078f5fe49f/jinxed-2.1.0.tar.gz", hash = "sha256:7e755b831faa2443d44fb4ce7c0202eb9c3ed39bd5bf1193365888f4f6092b54", size = 61287, upload-time = "2026-07-03T15:46:48.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/1d/0a6be99b69bb448448403a45a241aff0dca751832ae54f027ed940d2eb52/jinxed-2.1.0-py2.py3-none-any.whl", hash = "sha256:43b802d18b70e405d410fb66eb2837d1101e7e5ea922e666507bb43f34d11d09", size = 104999, upload-time = "2026-07-03T15:46:47.114Z" }, +] + [[package]] name = "jmespath" version = "1.1.0" @@ -1508,6 +1621,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" }, ] +[[package]] +name = "language-tags" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/39/d1/b37165bed9016c19e2cdecf4176fbd902a014fb8a87dd44415438e190969/language_tags-1.3.1.tar.gz", hash = "sha256:b15f05505dad3ad296a1782d5a6083fd141309186094d1ab08095348f4203f37", size = 222642, upload-time = "2026-05-08T11:46:24.418Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/21/b48b9e8408b3faec9fb7cb2f68352c7724add35a980c948eaceb29ac41e4/language_tags-1.3.1-py3-none-any.whl", hash = "sha256:f7db7ef8879523019603e09644a86d95ba60595ce5e5ea82d46e8971b0f68f7b", size = 216654, upload-time = "2026-05-08T11:46:17.836Z" }, +] + [[package]] name = "loguru" version = "0.7.3" @@ -1878,6 +2000,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2b/2a/d1a88066b1c14186f5d3c0d18c94f17b064511982bab0578d49ee9d43c29/numpy-2.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:17a25e09640602e10bc8de0e6fa2b3fd68eedd84ba6d7842dc8f32f9ab87bd0b", size = 10350488, upload-time = "2026-07-04T17:06:37.785Z" }, ] +[[package]] +name = "orjson" +version = "3.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/742fb1f62b825f2c010697eaf4e828004bc2a81e7e806666989c132c7c42/orjson-3.12.0.tar.gz", hash = "sha256:d14203fb1aae2ad9b3d52f8a0e82aeb10197ef1c9bc61da7f358bd70b00123d5", size = 4142915, upload-time = "2026-08-14T16:13:30.607Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/35/819eeb4fa8ee676d38fdbb8213a76fd496f7dbbfdfafa89d34e02b22dfac/orjson-3.12.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:747843254519dd43b93eee3153a19e5a509334320c4d2f823ec879232db5c796", size = 224133, upload-time = "2026-08-14T16:12:00.607Z" }, + { url = "https://files.pythonhosted.org/packages/58/ab/d9221d4a2b085b073fcddc91728d490f20b9cf010c62c2f42371ab997695/orjson-3.12.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:7c2ad193c8004254f34b499f3bd2c80f043d10754aff2b38f93da574f4883f98", size = 113669, upload-time = "2026-08-14T16:12:02.126Z" }, + { url = "https://files.pythonhosted.org/packages/15/12/644cbbcabb26df61d9ef0c66e6f2bf8b687cc7b66137597f2858951f1952/orjson-3.12.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:bc7a872f03522d90e0429e6c0c5cd23084f767bedcb4c58048eec19294613344", size = 130410, upload-time = "2026-08-14T16:12:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/14/6d/e3a8c34d687895aecd8b267a01c46106eb98d8424a83bfa7bacb723854f6/orjson-3.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18a87929f31d94a77f7dc93cf527e91f39ce7fe7813d588a4de2507efd32a387", size = 131101, upload-time = "2026-08-14T16:12:04.918Z" }, + { url = "https://files.pythonhosted.org/packages/75/20/930824c07685c22af23f26818ed3853b0270488a412b6ab757904b7f787b/orjson-3.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9683ee9ea0659da64f36574ef675b8a86330c34c19ea75db1fb93c3ff99e0ef", size = 131479, upload-time = "2026-08-14T16:12:06.11Z" }, + { url = "https://files.pythonhosted.org/packages/4c/a6/22e863bbbe8917aa292e33e0db597000f9a07eb5e6f52efed623fa16bae1/orjson-3.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:103b5db66aa53c1f9e88c2524be4f383e831ba7dfd5f9f5af6336a177c622f11", size = 135865, upload-time = "2026-08-14T16:12:07.392Z" }, + { url = "https://files.pythonhosted.org/packages/50/a0/ceb5008914a65e9a19a46a09d94bc67a74d120209fdfa772750023ceb377/orjson-3.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd57d79aefa3f84eec851d6de7a366795b9345cfaf17f82b4820430a7a5fa241", size = 127843, upload-time = "2026-08-14T16:12:08.607Z" }, + { url = "https://files.pythonhosted.org/packages/12/3d/61c6b3b84c250cb09cb7229701ff77e4d763773ad7f577d0b6abf2892664/orjson-3.12.0-cp310-cp310-win32.whl", hash = "sha256:3dbce9b6b3074b31a5d5dd322a9c4e5b16f206091ece4194c2e36952847a105e", size = 128293, upload-time = "2026-08-14T16:12:09.819Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0e/ea0f4a563253b6363195a4f704123c6bfbf156641bd3be5a75de81c5e917/orjson-3.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:3bb17a06f9bd15237b3216c044209fe92597379124018cfc196fbb846cde64df", size = 122216, upload-time = "2026-08-14T16:12:11.261Z" }, + { url = "https://files.pythonhosted.org/packages/75/1a/a7075a8e8b0d3f5097d17ac3099017104b6b7b42012041147995d5b2da05/orjson-3.12.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a94f0f0c6fcbb2b5bd9734c57a489c7584a732bbdf04a39e8c83b861e9d03e92", size = 223409, upload-time = "2026-08-14T16:12:12.654Z" }, + { url = "https://files.pythonhosted.org/packages/05/34/c2eb3b2900e5597db7841a4c6416ac2d90081bd956b02d4dd1833fa2b96b/orjson-3.12.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:a696529ec96a90d9a5f9570207efe403c8b08f8e4aa2783ee3403511e2fdfa10", size = 124015, upload-time = "2026-08-14T16:12:14.025Z" }, + { url = "https://files.pythonhosted.org/packages/1c/df/b49081766a75b6a37b3d33bdc0a39e492abab8441dd25e3e1998e7b83fcb/orjson-3.12.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:e4ac5059baab4b3acbd99485de019ff8cda0fdf34b61fa74f7197a53db78bfe8", size = 113471, upload-time = "2026-08-14T16:12:15.81Z" }, + { url = "https://files.pythonhosted.org/packages/48/d4/58ea28eeef95c2a27358ed927380a621162cf20bd740bbccf9c3f09a200a/orjson-3.12.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8e29957429c35bbb5a185a119c523aa2428b7bbf1a293724c7b9375ed8f892a3", size = 129998, upload-time = "2026-08-14T16:12:17.503Z" }, + { url = "https://files.pythonhosted.org/packages/e2/f4/1e82aa2efc9916422d804697876ce433c907a1abd7c7e5c6d3d48565e5f9/orjson-3.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dce0166feb0a737ab84f598c9a338cbc0b764a036617aa686194f53c7eba0c3e", size = 130891, upload-time = "2026-08-14T16:12:18.762Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e1/15169e9d22b59a406264f99d6db387c0b0b12b6357a8a0169917c2a713eb/orjson-3.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9caf3d09f47c3c70c4451ada20ef9bc4a4cdffa26f49862cf0a253b329aae2d5", size = 131285, upload-time = "2026-08-14T16:12:20.251Z" }, + { url = "https://files.pythonhosted.org/packages/a4/3a/763dbd426290d044ec3e615a05e70adb6d8b6f95bf17dc355c0081a5e8b6/orjson-3.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b9dca132b1fda5565088e65a6b6e742285e0aeceb6fae549fa8863e16c7d3998", size = 135707, upload-time = "2026-08-14T16:12:21.652Z" }, + { url = "https://files.pythonhosted.org/packages/04/d1/3b2038ed168d22e14182ed715d6963f9c073a83a2ba43cfe918a4fc43c64/orjson-3.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a791f793b287bbc135b8e87c34e35c8bfc693e2a8a620fab1ae682b925f9a32e", size = 127669, upload-time = "2026-08-14T16:12:22.926Z" }, + { url = "https://files.pythonhosted.org/packages/88/ae/b84b3d3e65f5629ada0edcb1d2bccc55d7c5f89d8b981537ecdc3d6f31ec/orjson-3.12.0-cp311-cp311-win32.whl", hash = "sha256:31ed278a36304390adc3eec5d7f6fd593a7c3e99e5a06cd07866396c4b1b4710", size = 128043, upload-time = "2026-08-14T16:12:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/35/24/2ed0e6f51ea3d0af45d807233a851175af75bec83ef5fd0d6a2601904ec0/orjson-3.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb2539159dfe8d371914f354360fa50e4a577cc89222a3828b9650a5e5040252", size = 122084, upload-time = "2026-08-14T16:12:25.813Z" }, + { url = "https://files.pythonhosted.org/packages/21/dd/95d25fcfbc9471799ef6bb01c552d64ee5cde93ee40ba2f423dd3442c708/orjson-3.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:61318b6de893c7a9d9f3e5ecbadccbfc26a7eb417ccc7bbf0771de3b4d72f868", size = 127035, upload-time = "2026-08-14T16:12:27.201Z" }, + { url = "https://files.pythonhosted.org/packages/be/4a/295da39c651c2faac8bd351a2a346f0fdedd9d50b847ee9dfc27d2207ef6/orjson-3.12.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:aa3e43a6846e91d7bde3d5a9c66090fcd8744f569a9b6cffc5e1ca38f6a461c0", size = 223427, upload-time = "2026-08-14T16:12:28.525Z" }, + { url = "https://files.pythonhosted.org/packages/29/98/758cf90fbeaaafb7f8141bfac75a432099959f3a2f5db93a412e876415d8/orjson-3.12.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:11edb4660a6680abee9788a3a9072208a2c96538cc1322bd79542065229d8e54", size = 123725, upload-time = "2026-08-14T16:12:30.013Z" }, + { url = "https://files.pythonhosted.org/packages/32/b5/5b934d251f8651f7e41df180ad0c57a6e1cabe15c7bd331638413a50ebc9/orjson-3.12.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:2d3a9da945a4d96ae758fdaaca56742e6b73b6fd554c5d8876f252a6dad70b83", size = 113375, upload-time = "2026-08-14T16:12:31.209Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d2/37efb5b12a176ce3ced29f4144f20da57d02757f78ce549637dc1b4e1fc8/orjson-3.12.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:92ffc09e07233a6ab6d4e067f7841edcbcc134cb4812155cf171ea5255a421d7", size = 129983, upload-time = "2026-08-14T16:12:32.721Z" }, + { url = "https://files.pythonhosted.org/packages/50/22/0644b87c73f13e0092df8f35a1fe280d991e5e90072087411e0dd7e44e0c/orjson-3.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf44e374aadde77b1f6109f1030be51433eb61984379852766b6f4e187db7b1e", size = 130629, upload-time = "2026-08-14T16:12:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/8c/57/80b986ebfecd9c6a177ddf1c2319717f0cd8feffb2b78946595a18a2fc88/orjson-3.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1192a7021b6d071aaf909864f6e924d6a2675ca360485b972b8401749311750b", size = 131245, upload-time = "2026-08-14T16:12:35.713Z" }, + { url = "https://files.pythonhosted.org/packages/80/3d/75c5ac5a69161f44492a68fbdde66f4cc4ce48cd5e1fb05918e46f0c8848/orjson-3.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:53c0c474a9d9aff9aebfc0c88de1f28f843d940e6e3a80729abdf6a20274356f", size = 135397, upload-time = "2026-08-14T16:12:37.128Z" }, + { url = "https://files.pythonhosted.org/packages/71/93/4d71f2df314a97ff0d27a4559bf5888fc8406e3c6dec90e92291e3511215/orjson-3.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:532ff8cd4bd59a327a953a7dcde922c7fc25b85e29721bb8633265430d3a3873", size = 127693, upload-time = "2026-08-14T16:12:38.627Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1d/0dbc6be5adfd1730491072fb60beb6bcdf5d7b2596ee41b7fc2e298bfc09/orjson-3.12.0-cp312-cp312-win32.whl", hash = "sha256:a6cf4b18e7de173f209f2084ffbd736dd72389a396326ee80a7022168be232e5", size = 128000, upload-time = "2026-08-14T16:12:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c9/97b1ce0112ebf5e949c775ed5b1755e562233179f3584579673cc24d6378/orjson-3.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:010811c1b69773450a01cef97727a67b223242f350b77d4ca000e59a9ef2155a", size = 122106, upload-time = "2026-08-14T16:12:41.324Z" }, + { url = "https://files.pythonhosted.org/packages/a8/6a/facd8b312e4a0d3a7fa978c7e15821f74a336adf1d65529faec33b48e18b/orjson-3.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:ad29eece0c601737f2a60edc2752a84e7a0785df3efb62e3012834700a5afe0d", size = 126869, upload-time = "2026-08-14T16:12:42.651Z" }, +] + [[package]] name = "packaging" version = "26.2" @@ -2039,33 +2200,21 @@ wheels = [ [[package]] name = "playwright" -version = "1.61.0" +version = "1.60.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet" }, { name = "pyee" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/44/ee/31e4e0db36588b817a10b299a0285082545fde7d36543c2abe498bb3d61a/playwright-1.61.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:ff138c3a604f69911e9d42fd036e55c2a171e5616edf04c1e7f60a2a285540b0", size = 43421877, upload-time = "2026-06-29T10:32:48.428Z" }, - { url = "https://files.pythonhosted.org/packages/42/35/71395dd3ecc798965be4a3ef8c443217d4abca168e7cb34536304f9489e6/playwright-1.61.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:009588c2a7e499bc5a8b425b61fa65490968bbda9cd69e0cf2cff10f8304659a", size = 42205016, upload-time = "2026-06-29T10:32:52.104Z" }, - { url = "https://files.pythonhosted.org/packages/f4/44/323164cf5cd1647bdefce76ffce27651aadb959d089b48f53ea40918276e/playwright-1.61.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:9f7de4536088d12037c13a52b7ea34b59270b78926bb56935070597ffac6b1af", size = 43421884, upload-time = "2026-06-29T10:32:55.773Z" }, - { url = "https://files.pythonhosted.org/packages/ab/f8/a35bf179e4ba2522c1893635094a64e407572547bd61528820fc0abc87fe/playwright-1.61.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:54f3b39f6eab832e33458c1dd7da0b5682aedab3b09ae731b5c59fa12fd2024e", size = 47421381, upload-time = "2026-06-29T10:32:59.903Z" }, - { url = "https://files.pythonhosted.org/packages/b7/eb/e3f922348ec17c315f98c463f72faa1181a1c3de0bfe31a8d2edf6561723/playwright-1.61.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93454322ade8c11d5d6c211bfd91bdfb9ffb4810e3e026371bcbc4bec1b7ee4c", size = 47120545, upload-time = "2026-06-29T10:33:03.574Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a6/5be4e52b40a9c0c8a073e7c5b0785c05cf5a9ea8f8a7b5b260e32d970342/playwright-1.61.0-py3-none-win32.whl", hash = "sha256:372d55a6f1248fa1dd47599686980cb8fb5bbe6fcda59eab793eb657c11d8a9b", size = 37844841, upload-time = "2026-06-29T10:33:07.361Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fd/2b78036e5fbe9d5f5645bbe08a1eac7160c51243c0093963edbcf67c35d9/playwright-1.61.0-py3-none-win_amd64.whl", hash = "sha256:35c6cc4589a5d00964a59d7b3e59641e0aac0c02f15479a7af77d20f6bc79597", size = 37844846, upload-time = "2026-06-29T10:33:10.637Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/1b0f3c4ee4eb0514bc805b5c2f9a223e5b6de4f11a926f5235d51d0fc81b/playwright-1.61.0-py3-none-win_arm64.whl", hash = "sha256:e9fcbffcf557a8620fdedd92491eb59a32d18e23d6f3b4f6214b952be324fe51", size = 33955127, upload-time = "2026-06-29T10:33:14.008Z" }, -] - -[[package]] -name = "playwright-stealth" -version = "2.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "playwright" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a4/db/6ade5d539c7d151b9defc78fafa8b65aa52352617d0e7699b47008bd801f/playwright_stealth-2.0.3.tar.gz", hash = "sha256:1d8e488fbdd8f190f1269ea8cf5d57d14df3a9f1af1001c41ee3588b2aac3133", size = 25751, upload-time = "2026-04-04T02:50:33.88Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/10/607c409712c02a26c4cb794820514cb7fdaaeac15fb05bed917fb8a354b3/playwright_stealth-2.0.3-py3-none-any.whl", hash = "sha256:1887ade423ab7ff8ae16d363a30a38de0b5817e1e4a29d47b74bf3a0e3dbfcb4", size = 34385, upload-time = "2026-04-04T02:50:35.246Z" }, + { url = "https://files.pythonhosted.org/packages/21/f0/832bd9677194908da118064eef20082f2791e3d18215cc6d9391ee2c5a67/playwright-1.60.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:6a8cd0fec171fb3089e95e898c8bc8a6f35dea0b78b399e12fcc19427e91b1d7", size = 43474635, upload-time = "2026-05-18T12:00:31.969Z" }, + { url = "https://files.pythonhosted.org/packages/59/7b/e1d32ae8a3ed937ec2be3721c5f728b13d731a0b7c6442e0b3bec5094ac0/playwright-1.60.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:39b5420ba6145045b69ced4c5c47d4d9fe5bddfc8ff816c518913afcb25ec7a5", size = 42261327, upload-time = "2026-05-18T12:00:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/d7/bc/23de499ded6411c188a20c5a0dea6f0cd4ed5d2b3cc6042a5dbd3ed609aa/playwright-1.60.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:2581d0e6a3392c71f91b27460c7fd093356818dc430f48153896c8aeeaef7705", size = 43474636, upload-time = "2026-05-18T12:00:39.294Z" }, + { url = "https://files.pythonhosted.org/packages/22/7b/1d679f4fced4ea94efadd17103856d8c565384f68382a1681264e46f5925/playwright-1.60.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:1c2bfae7884fb3fb05b853290eab8f343d524e5016f2f1def702acbbdf14c93e", size = 47467220, upload-time = "2026-05-18T12:00:43.179Z" }, + { url = "https://files.pythonhosted.org/packages/84/c2/1528d267d4442bd2c6b8eaeab819dd52c2030bf80e89293f0ba1f687473b/playwright-1.60.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43e66564125ee31b07a58cefb21e256d62d67d8d1713e6858df7a3019d8ed353", size = 47154856, upload-time = "2026-05-18T12:00:46.715Z" }, + { url = "https://files.pythonhosted.org/packages/bb/4e/b008b6440a7a1624378041da94829956d4b8f7ab9ef5aad22d0dc3f2e26d/playwright-1.60.0-py3-none-win32.whl", hash = "sha256:ec94e416ea320711e0ad4bf185dcbf41833672961e90773e1885255d7db7b7e7", size = 37902157, upload-time = "2026-05-18T12:00:50.374Z" }, + { url = "https://files.pythonhosted.org/packages/55/f0/0541524133104f9cc20bf900870ff4a736b76a23483f3a55295ddfa58409/playwright-1.60.0-py3-none-win_amd64.whl", hash = "sha256:9566821ce6030a1f9e7146a24e19355ab0d98805fd0f9be50bb3d8fef1750c02", size = 37902159, upload-time = "2026-05-18T12:00:53.728Z" }, + { url = "https://files.pythonhosted.org/packages/80/c8/210f282d278e4709cdd71b12a31af45a30a22ab3207b387e29b37e478713/playwright-1.60.0-py3-none-win_arm64.whl", hash = "sha256:6e4f6700a4c2250efff8e690a81d66e3855754fb587b6b87cf5c784014f91537", size = 34037981, upload-time = "2026-05-18T12:00:57.584Z" }, ] [[package]] @@ -2239,6 +2388,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c4/bd/2534e130295c8cfd4f0a2e31623baab7502278f1e97bcfe61db75656a77f/pymysql-1.2.0-py3-none-any.whl", hash = "sha256:62169ce6d5510f08e140c5e7990ee884a9764024e4a9a27b2cc11f1099322ae0", size = 45716, upload-time = "2026-05-19T08:26:20.974Z" }, ] +[[package]] +name = "pyobjc-core" +version = "12.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/78/abc4ce5920305780aeb36b4067a86253378b36e29ba96673a3deb02eb03a/pyobjc_core-12.2.2.tar.gz", hash = "sha256:3906452339cd06a3bb07df103c2511d4cb0f7a22d8771c0b802eba15d9a642b6", size = 1067701, upload-time = "2026-08-11T19:43:39.059Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/1d/baf7197cee12f32a8eb9f8633093da1ec1ea702b0e1346bc1c7bfe022673/pyobjc_core-12.2.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:56c6c39f1de059fcbb174ebca5525505fc8feaa89be2a28c329bf09b6b25ee75", size = 6483051, upload-time = "2026-08-11T13:55:45.262Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8e/18284fec7913ef78b25a1c97f9689ebef98bc14038386191491516abeb25/pyobjc_core-12.2.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b9cdd686e32db8e451feb19f8a85bc4cd52c2893103881d04aca51e1f35371d1", size = 6481574, upload-time = "2026-08-11T14:13:27.153Z" }, + { url = "https://files.pythonhosted.org/packages/86/b2/bbf7f049880ab40d110e66f25122342a1f6c98d6fe3c59bb98985503c660/pyobjc_core-12.2.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:122e6ad302a2abf5d4d4adb0156db751600ddf2768441696cba17b31323085e7", size = 6427637, upload-time = "2026-08-11T14:51:36.038Z" }, +] + +[[package]] +name = "pyobjc-framework-cocoa" +version = "12.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyobjc-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/76/49c6da2c6a831020b4854ba20079d5a1030474bffc776b7b73c2eeff8c15/pyobjc_framework_cocoa-12.2.2.tar.gz", hash = "sha256:c96c0ef69a71afbbb0e6a7d594b455c5fe47d62e0db376ee7a2b4b828c16ace9", size = 3132831, upload-time = "2026-08-11T19:44:02.288Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/dd/aba439652cae293a736680ef5ed5cc29419adbbac1c6d4555b910741d516/pyobjc_framework_cocoa-12.2.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5a751c8033a3b51f7996f0327e0675eb44dcfdfe7920fae01e3d78b662723fff", size = 387296, upload-time = "2026-08-11T19:32:40.684Z" }, + { url = "https://files.pythonhosted.org/packages/f6/a7/370f12143661dff66f2c68a735938afab6530aa3b153f6a7a6f12b5eabab/pyobjc_framework_cocoa-12.2.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:851dca4c16e70b405e5cd5a8c166cf7c445ae54a4cdd95ce9a523803172f32d1", size = 387300, upload-time = "2026-08-11T19:32:42.043Z" }, + { url = "https://files.pythonhosted.org/packages/fd/2f/b67e73d8bc367e03fe7861cd9c49fff9dcfa6db83bc0630c0adcfb25b7fa/pyobjc_framework_cocoa-12.2.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e106f395531e67694376b0f1184612cbeea3ec8b9bf56b55ef41d026171d2a2d", size = 388117, upload-time = "2026-08-11T19:32:43.161Z" }, +] + [[package]] name = "pyopenssl" version = "26.4.0" @@ -2267,6 +2441,15 @@ version = "2.1.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d5/7b/65f55513d3c769fd677f90032d8d8703e3dc17e88a41b6074d2177548bca/PyPyDispatcher-2.1.2.tar.gz", hash = "sha256:b6bec5dfcff9d2535bca2b23c80eae367b1ac250a645106948d315fcfa9130f2", size = 23224, upload-time = "2017-07-03T14:20:51.806Z" } +[[package]] +name = "pysocks" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, +] + [[package]] name = "pytest" version = "9.1.1" @@ -2436,6 +2619,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4a/1c/8df7b461497b42fcc1e7c44529201975ec77b0e1ebecd00df4b1f096c1d4/queuelib-1.9.0-py3-none-any.whl", hash = "sha256:c5fd3bebf2c924446fa94fca6b72e81168f79cf4c2a9143b8b26f266a423fcf3", size = 13585, upload-time = "2026-01-29T11:19:35.616Z" }, ] +[[package]] +name = "readchar" +version = "4.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/49/a10341024c45bed95d13197ec9ef0f4e2fd10b5ca6e7f8d7684d18082398/readchar-4.2.2.tar.gz", hash = "sha256:e3b270fe16fc90c50ac79107700330a133dd4c63d22939f5b03b4f24564d5dd8", size = 9762, upload-time = "2026-04-06T19:45:54.226Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/ca/36133653e00939922dd1416f4c56177361289172a30563fcb9552c9ccde4/readchar-4.2.2-py3-none-any.whl", hash = "sha256:92daf7e42c52b0787e6c75d01ecfb9a94f4ceff3764958b570c1dddedd47b200", size = 9401, upload-time = "2026-04-06T19:45:52.993Z" }, +] + [[package]] name = "redis" version = "8.0.1" @@ -2505,6 +2697,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, ] +[[package]] +name = "rich-click" +version = "1.9.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "rich" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/ea/21e4867ea0ef881ffd4c0550fc21a061435e50d6324bcd034396633cbc18/rich_click-1.9.8.tar.gz", hash = "sha256:4008f921da88b5d91646c134ec881c1500e5a6b3f093e90e8f29400e09608371", size = 75363, upload-time = "2026-05-28T19:54:59.144Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/97/a87901aef6b7e7e4a34c6dd6cc17dca8594a592ef9d9dd765fca2b7facf7/rich_click-1.9.8-py3-none-any.whl", hash = "sha256:12873865396e6927835d4eabb1cc3996edcd65b7ac9b2391a29eca4f335a2f93", size = 72189, upload-time = "2026-05-28T19:54:57.867Z" }, +] + [[package]] name = "rq" version = "2.10.0" @@ -2560,6 +2767,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/75/e90ab9aeece218a9fc5a5bc3ec97d0ee6bb3c4ff95869463c1de58e29a1c/ruff-0.15.21-py3-none-win_arm64.whl", hash = "sha256:6e83115d4b9377c1cbc13abf0e051f069fab0ef815ea0504a8a008cee24dd0a8", size = 11375265, upload-time = "2026-07-09T20:01:31.772Z" }, ] +[[package]] +name = "runs" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "xmod" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/ae/095cb626504733e288a81f871f86b10530b787d77c50193c170daaca0df1/runs-1.3.0.tar.gz", hash = "sha256:cca304b631dbefec598c7bfbcfb50d6feace6d3a968734b67fd42d3c728f5a05", size = 4585, upload-time = "2026-02-03T15:59:58.974Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/b6/049c75d399ccf6e25abea0652b85bf7e7e101e0300aa9c1d284ad7061c0b/runs-1.3.0-py3-none-any.whl", hash = "sha256:e71a551cfa8da9ef882cac1d5a108bda78c9edee5b8d87e37c1003da5b6a7bed", size = 6406, upload-time = "2026-02-03T15:59:59.96Z" }, +] + [[package]] name = "s3transfer" version = "0.19.0" @@ -2604,6 +2823,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3d/c0/1c8b2b4cba1f07038c3494de4f88e00dae69a41f1852cc0282731346b671/scrapy-2.18.0-py3-none-any.whl", hash = "sha256:7cd2eaa6fb013247ca259fd4cad75208a2b37cd65fbdd3a6c3fbc75cdf61f3d9", size = 365413, upload-time = "2026-08-20T15:15:37.522Z" }, ] +[[package]] +name = "screeninfo" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cython", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/bb/e69e5e628d43f118e0af4fc063c20058faa8635c95a1296764acc8167e27/screeninfo-0.8.1.tar.gz", hash = "sha256:9983076bcc7e34402a1a9e4d7dabf3729411fd2abb3f3b4be7eba73519cd2ed1", size = 10666, upload-time = "2022-09-09T11:35:23.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/bf/c5205d480307bef660e56544b9e3d7ff687da776abb30c9cb3f330887570/screeninfo-0.8.1-py3-none-any.whl", hash = "sha256:e97d6b173856edcfa3bd282f81deb528188aff14b11ec3e195584e7641be733c", size = 12907, upload-time = "2022-09-09T11:35:21.351Z" }, +] + [[package]] name = "sentry-sdk" version = "2.64.0" @@ -2892,6 +3124,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/85/07aa75d9e65fadd162bda4a6ffb22b574f4f042b4010efe6d74c9a9e91b8/ua_generator-2.1.2-py3-none-any.whl", hash = "sha256:18a52f4bebdc43963defb071cd66a2a68f84607767403d425a9fcf7542aeb727", size = 32776, upload-time = "2026-06-14T23:22:18.663Z" }, ] +[[package]] +name = "ua-parser" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ua-parser-builtins" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/98/5e4b52d772a048af122a6fc5ce365c311efb9f5e79c55fd4fdd7c9f59e83/ua_parser-1.0.2.tar.gz", hash = "sha256:bab404ad42fb37f943107da2f6003ffc79724d11cc95076a7a539513371779da", size = 33239, upload-time = "2026-04-05T20:14:28.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/7c/6367995ff57aaa2d9e1055adbaec2519cf5a979780a83a93fdf8c6ec37be/ua_parser-1.0.2-py3-none-any.whl", hash = "sha256:0f8e6d0484af2a9ff804bba5a4fe696e87c028eaba98ad9a7dfae873fef7788a", size = 31219, upload-time = "2026-04-05T20:14:26.913Z" }, +] + +[[package]] +name = "ua-parser-builtins" +version = "202606" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/a3/f26edd95a2ce2ffbf559da432e5ff544e8dcda3673d1c775f71afffb18b7/ua_parser_builtins-202606-py3-none-any.whl", hash = "sha256:13b483eb12a5419c1094ce02b7df705fefc6b5d869764b3ffbf6c940c6d014cb", size = 90676, upload-time = "2026-06-01T22:40:53.008Z" }, +] + [[package]] name = "urllib3" version = "2.7.0" @@ -2953,6 +3205,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, ] +[[package]] +name = "wcwidth" +version = "0.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/57/ed58088fafdf4c55a0ad6bde846502567645424d7ebf325230b9237f4085/wcwidth-0.8.3.tar.gz", hash = "sha256:d128512515fbf4612e0ff21fd6380399210318b7b54a9af59dff8454cf9730eb", size = 1458450, upload-time = "2026-08-28T18:10:06.875Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/0e/57f6bb3024a597b2e8ec4aee710ffe62ddc95af2e2bb1ee7a7abdc22c68c/wcwidth-0.8.3-py3-none-any.whl", hash = "sha256:d5b73dba6158a595ec9370350e7f2637bcac8d6c5e4fde34f30fcffb6103a5e4", size = 331669, upload-time = "2026-08-28T18:10:04.909Z" }, +] + [[package]] name = "werkzeug" version = "3.1.8" @@ -2974,6 +3235,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, ] +[[package]] +name = "xmod" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/3b/5a0d2670bab661164e27a5c27c448ae6204458c97cb94ccf89d0c47715bc/xmod-1.10.0.tar.gz", hash = "sha256:b40b2a54d56684b01eb9627892b0c179918e8ef0bd4d7f3bac7a3fdba11cd6e6", size = 17862, upload-time = "2026-05-09T13:46:46.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/e9/c362d47ed2b1928d65d61888c1419fae8ef69417fba2067d2fd3da1d400b/xmod-1.10.0-py3-none-any.whl", hash = "sha256:bebf8493b7ac63097401590a329c9ed20da224de0583a522e7ccb634af122f5a", size = 4661, upload-time = "2026-05-09T13:46:45.776Z" }, +] + [[package]] name = "zope-interface" version = "8.5" From 259a0b311e97f2c117ddca7bb611e480e39a47b6 Mon Sep 17 00:00:00 2001 From: Quentin Lienhardt Date: Wed, 2 Sep 2026 11:04:17 +0200 Subject: [PATCH 5/5] Merge miss --- bases/ecoindex/worker/tasks.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bases/ecoindex/worker/tasks.py b/bases/ecoindex/worker/tasks.py index 2a3c80a..f715ce6 100644 --- a/bases/ecoindex/worker/tasks.py +++ b/bases/ecoindex/worker/tasks.py @@ -22,6 +22,10 @@ from ecoindex.models.tasks import QueueTaskError, QueueTaskResult from ecoindex.monitoring import capture_task_failure, init_sentry from ecoindex.scraper.scrap import EcoindexScraper +from ecoindex.screenshot_storage import ( + get_screenshot_local_folder, + persist_screenshot, +) from rq import get_current_job init_sentry(with_rq=True, release=get_api_version()) @@ -179,6 +183,13 @@ async def async_ecoindex_task( ), ) + capture_task_failure( + exc, + status_code=500, + url=url, + task_id=str(task_id), + task_name="ecoindex_task", + ) return QueueTaskResult( status=TaskStatus.FAILURE, error=QueueTaskError(