# Python 3.15 para web scraping: qué se instala y qué se rompe

Source: https://ipvolt.com/es/blog/python-3-15-web-scraping
Markdown: https://ipvolt.com/es/blog/python-3-15-web-scraping.md
Language: es

[Inicio de ipvolt](https://ipvolt.com/es.md) / [Blog](https://ipvolt.com/es/blog.md) / Python 3.15 para web scraping: qué se instala y qué falla

Análisis
Publicado: 2026-10-16
Actualizado: 2026-10-16
Por ipvolt
9 min de lectura

pip install scrapy falla en Python 3.15 sin compilador de C porque brotli no tiene wheel cp315. Instalamos 19 paquetes de scraping con pip y uv para ver el resto.

En Python 3.15.0, la mayor parte de un stack de scraping se instala desde wheels ya compilados y pasó una prueba de humo. Todo lo que necesita brotli o zstandard falla en una máquina sin compilador de C, porque ninguno de los dos tiene wheel para 3.15, y eso incluye un simple `pip install scrapy`. La solución rápida es instalar `gcc` antes de `pip install` (solución 1, más abajo) o quedarse en Python 3.14 hasta que brotli publique wheels cp315 (solución 2).

**Última comprobación: 16 de octubre de 2026, 10:09 UTC.** En ese momento PyPI seguía sin wheel cp315 para brotli 1.2.0 y zstandard 0.25.0. zope.interface 8.7, publicado el 15 de octubre, ya trae wheels cp315.

| Paquete | Versión | Wheel cp315 | Con compilador | Sin compilador | Notas |
| --- | --- | --- | --- | --- | --- |
| `scrapy` | 2.19.0 | Python puro; no (brotli 1.2.0) | se instala | falla al compilar brotli | exige brotli en CPython desde la 2.18.0 |
| `requests[socks]` | 2.34.2 + PySocks 1.7.1 | Python puro | se instala | se instala |  |
| `httpx[socks]` | 0.28.1 + socksio 1.0.0 | Python puro | se instala | se instala |  |
| `httpx[brotli]` | 0.28.1 | no (brotli 1.2.0) | se instala | falla al compilar brotli | `httpx brotlicffi` se instala sin compilador |
| `httpx[zstd]` | 0.28.1 | no (zstandard 0.25.0) | se instala | falla al compilar zstandard | la compilación más lenta |
| `aiohttp` | 3.14.5 | sí | se instala | se instala |  |
| `aiohttp[speedups]` | 3.14.5 + aiodns 4.0.4 | no (Brotli 1.2.0) | se instala | falla al compilar Brotli | `aiohttp aiodns brotlicffi` se instala |
| `aiohttp-socks` | 0.12.0 | Python puro | se instala | se instala |  |
| `urllib3[brotli]` | 2.8.0 | no (brotli 1.2.0) | se instala | falla al compilar brotli | `urllib3 brotlicffi` se instala |
| `curl_cffi` | 0.16.3 | abi3 | se instala | se instala |  |
| `playwright` | 1.64.0 | no depende de la versión | se instala | se instala | navegadores sin probar |
| `lxml` | 6.1.3 | sí | se instala | se instala |  |
| `beautifulsoup4` | 4.15.0 | Python puro | se instala | se instala |  |
| `parsel` | 1.12.1 | Python puro | se instala | se instala |  |
| `selectolax` | 1.0.0 | sí | se instala | se instala | 1.0: `selectolax.parser` lanzó ImportError, usa `selectolax.lexbor` |
| `pandas` | 3.0.6 + numpy 2.5.4 | sí | se instala | se instala |  |
| `pydantic` | 2.14.1 + pydantic-core 2.50.1 | pydantic-core: sí | se instala | se instala |  |
| `orjson` | 3.13.0 | sí | se instala | se instala |  |
| `tenacity` | 9.2.1 | Python puro | se instala | se instala |  |

"Con compilador" y "sin compilador" son la imagen oficial `python:3.15-slim` con y sin `gcc`, y cada paquete se instaló tanto con pip como con uv, que coincidieron en todos los casos. Un paquete de Python puro no tiene código compilado, así que no necesita wheel cp315. Cada instalación correcta pasó su prueba de humo.

## pip install scrapy falla en Python 3.15: Failed building wheel for brotli

En la imagen slim, pip 26.2.1 se detiene en brotli:

```text
  × Building wheel for brotli (pyproject.toml) did not run successfully.
  │ exit code: 1
      error: [Errno 2] No such file or directory: 'gcc'
  ERROR: Failed building wheel for brotli
error: failed-wheel-build-for-install

× Failed to build installable wheels for some pyproject.toml based projects
╰─> brotli
```

uv 0.13.1 falla en el mismo paquete y muestra la cadena:

```text
error: Failed to build `brotli==1.2.0`
  cause: The build backend returned an error
hint: `brotli` (v1.2.0) was included because `scrapy` (v2.19.0) depends on `brotli`
```

La cadena es corta. Scrapy 2.19.0 es Python puro, pero requiere `brotli>=1.2.0; implementation_name != "pypy"`, así que toda instalación en CPython arrastra brotli. Ese requisito llegó con Scrapy 2.18.0 (20 de agosto de 2026); la 2.17.0 no lo tenía. brotli 1.2.0, publicado el 5 de noviembre de 2025, tiene wheels para CPython 2.7 y de 3.6 a 3.14, y nada más reciente. Sin un wheel compatible, pip y uv descargan el archivo de código fuente y compilan la extensión `_brotli`, que necesita `gcc`. Lo mismo ocurre con `httpx[brotli]`, `aiohttp[speedups]` y `urllib3[brotli]`, que en CPython también piden el paquete `brotli`, y con `httpx[zstd]`, que pide zstandard 0.25.0.

### Solución 1: añadir un compilador a la imagen

Esta única línea, ejecutada en la imagen slim, bastó para todos los paquetes que fallaban:

```sh
apt-get update && apt-get install -y --no-install-recommends gcc libc6-dev
```

Instaló gcc 14.2.0. Después, `pip install scrapy` tardó 28 s en lugar de fallar, uv tardó 19 s, y el spider rastreó sus páginas de prueba, incluida una comprimida con brotli. La compilación lenta es la de zstandard: 63 s para `httpx[zstd]` con pip. En un Dockerfile, pon la línea antes del `pip install`.

Solo se probó esta línea de Debian. En Windows, el compilador que necesitan estas compilaciones viene con las [C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) de Microsoft; no probamos Windows.

### Solución 2: quedarse en Python 3.14 hasta que existan los wheels

Python 3.14 tiene wheels de brotli y zstandard, así que allí el problema no existe. Para saber si brotli ya se ha puesto al día, ejecuta esto con Python 3.15:

```sh
python3 -m pip download --only-binary :all: --no-deps -d /tmp/wheel-check brotli
```

El 16 de octubre seguía terminando con `ERROR: No matching distribution found for brotli`. Cuando descargue un archivo `.whl`, los wheels cp315 estarán publicados y `pip install scrapy` funcionará sin compilador.

En el proyecto original, el [issue #25 de google/brotli-wheels](https://github.com/google/brotli-wheels/issues/25), "Python 3.15 wheels missing", se abrió el 9 de octubre y el 16 de octubre no tenía respuestas. El [pull request #1583 de google/brotli](https://github.com/google/brotli/pull/1583) añade un trabajo de CI que compila wheels cp315. El 16 de octubre seguía abierto sin revisión, y el único comentario era el del bot del CLA. Su descripción indica que un mantenedor aún tendría que publicar los wheels en PyPI.

### ¿No usas Scrapy? brotlicffi tiene wheel

brotlicffi 1.2.0.2 publica un wheel `abi3` que pip instaló en 3.15. HTTPX, urllib3 y aiohttp lo aceptan en lugar de `brotli`, así que sin compilador estas combinaciones funcionaron y descomprimieron una respuesta en brotli:

```sh
pip install httpx brotlicffi
pip install urllib3 brotlicffi
pip install aiohttp aiodns brotlicffi
```

La última es `aiohttp[speedups]` sin el requisito `Brotli`. Scrapy no puede usar esta vía: en CPython exige el propio paquete `brotli`.

Con zstd es más fácil de lo que parece. En Python 3.14 y posteriores, urllib3 2.8.0 y aiohttp 3.14.5 descomprimen zstd con el módulo `compression.zstd` de la biblioteca estándar, así que ambos ofrecieron `zstd` y descomprimieron una respuesta en zstd sin ningún paquete adicional. Solo `httpx[zstd]` necesita zstandard.

## No uses --only-binary a ciegas

`--only-binary :all:` le dice a pip que nunca compile desde el código fuente. Parece la solución obvia, y no falla:

```sh
pip install --only-binary :all: "httpx[brotli]" "aiohttp[speedups]"
```

pip imprimió `INFO: This is taking longer than usual` mientras buscaba y después instaló httpx 0.18.2 (junio de 2021) y aiohttp 0.13.1 (diciembre de 2014). La única otra pista fue una línea:

```text
WARNING: aiohttp 0.13.1 does not provide the extra 'speedups'
```

El resolvedor retrocedió por versiones antiguas hasta encontrar unas cuyos requisitos podían cumplirse solo con wheels. `uv pip install --no-build` eligió las mismas dos versiones e imprimió ``warning: The package `aiohttp==0.13.1` does not have an extra named `speedups` ``.

Limitar el flag a un paquete no lo hace seguro. `pip install --only-binary brotli scrapy` también termina con código 0 e instala Scrapy 2.17.0, la última versión sin el requisito de brotli. uv hizo lo mismo. Ese Scrapy rastreó las páginas de prueba, pero solo ofrecía a los servidores `gzip, deflate`, mientras que 2.19.0 con brotli ofrecía `gzip, deflate, br, zstd`. Si tus spiders usan `async def start()`, 2.17.0 los sigue ejecutando.

Para detectar una bajada de versión silenciosa, revisa las versiones instaladas después de cada instalación:

```sh
pip list
uv pip list
```

Para que falle de forma visible, fija la versión que quieres junto al flag:

```sh
pip install --only-binary brotli "scrapy==2.19.0"
```

```text
ERROR: Could not find a version that satisfies the requirement brotli>=1.2.0; implementation_name != "pypy" (from scrapy) (from versions: none)
ERROR: No matching distribution found for brotli>=1.2.0; implementation_name != "pypy"
```

uv con la misma versión fijada se detuvo con `error: No solution found when resolving dependencies`. Un `requirements.txt` con versiones exactas para tus paquetes principales te da la misma protección en cada build.

## Resuelto el 15 de octubre: zope.interface sin aceleraciones en C

Twisted, y con él Scrapy, depende de zope.interface. zope.interface 8.6 solo tiene wheels hasta CPython 3.14. El 14 de octubre, sin compilador, pip y uv lo instalaron igualmente en 3.15: su compilación pasa a Python puro cuando la extensión en C no se puede compilar. La salida de la instalación no mostró ningún error ni advertencia.

El coste se veía en la comprobación básica de interfaces. En el laboratorio, `IThing.providedBy(obj)` tardó 271 ns por llamada sin la extensión en C y 101 ns con ella. El laboratorio no midió qué supone eso para un rastreo completo.

zope.interface 8.7, publicado el 15 de octubre, incluye wheels cp315. El 16 de octubre, una instalación limpia sin compilador obtuvo el wheel binario 8.7 con pip y con uv, la extensión en C se importó y la misma comprobación tardó 102 ns. Un entorno virtual creado antes del 15 de octubre conserva la 8.6 en Python puro hasta que actualices zope.interface.

Para ver cuál tienes:

```sh
python -c "import zope.interface._zope_interface_coptimizations"
```

Un `ModuleNotFoundError` significa la versión de respaldo en Python puro. Con el wheel 8.7, o con gcc instalado, la importación funcionó.

## Otros cambios de 3.15 que importan al hacer scraping

El que más se va a notar es el modo UTF-8, ahora activado por defecto ([PEP 686](https://peps.python.org/pep-0686/)). [What's New in Python 3.15](https://docs.python.org/3.15/whatsnew/3.15.html#whatsnew315-utf8-default) dice que las operaciones de E/S sin una codificación explícita, como `open('flying-circus.txt')`, usan ahora UTF-8 sea cual sea el entorno del sistema. En el laboratorio, con `LANG=C`, `open()` sin `encoding` escribió UTF-8 en 3.15; con `-X utf8=0` volvió a `ANSI_X3.4-1968`, es decir, ASCII.

En scraping esto aparece sobre todo en las exportaciones. Un CSV o JSON escrito con `open(path, "w")` es ahora UTF-8 en cualquier máquina, incluidos los sistemas Windows donde 3.14 usaba la página de códigos de la configuración regional. Normalmente es lo que quieres. Si una herramienta posterior espera la codificación antigua, pasa `encoding="locale"` o define `PYTHONUTF8=0`; mejor aún, indica la codificación de forma explícita al escribir y al leer.

## Si actualizas Scrapy al mismo tiempo

Pasar a Python 3.15 suele venir con un Scrapy nuevo. Desde la 2.16, Scrapy ya no llama a `start_requests()`: un spider que solo define ese método se abre, no envía nada y se cierra como terminado, sin ninguna advertencia. [Scrapy rastrea 0 páginas después de actualizar](/es/guides/scrapy-start-requests-not-called) tiene la corrección de una línea, `async def start()`, que es la que usó el spider de este laboratorio.

## Método y límites

Registrado el 14 de octubre de 2026 en un host Ubuntu x86_64, dentro del sistema de archivos de la imagen oficial `python:3.15-slim` (Debian 13 trixie, digest de índice `sha256:ea4f1f29…`, construida el 12 de octubre de 2026), ejecutado en un espacio de nombres privado de Linux en lugar de Docker. El intérprete informó `Python 3.15.0 (main, Oct 12 2026, 22:33:31) [GCC 14.2.0]`, con el pip 26.2.1 incluido y uv 0.13.1. La copia con compilador añadió gcc 14.2.0 con la línea de apt de arriba. La tabla sale de esas ejecuciones. El 16 de octubre se repitieron sin compilador los casos de Scrapy, `--only-binary brotli` y zope.interface: todo coincidió con el 14 de octubre salvo zope.interface, que ahora vino del wheel 8.7.

Cada caso tuvo un entorno virtual nuevo y ninguna caché de paquetes. Las versiones instaladas y si cada una venía de un wheel binario, de un wheel de Python puro o de una compilación local se leyeron de las etiquetas de los wheels. Las pruebas de humo usaron un `http.server` local que solo comprime con las codificaciones que ofrece el cliente. Playwright se instaló, pero no sus navegadores. No se usó ningún proxy.

El laboratorio no dice nada sobre macOS, Windows, ARM, imágenes basadas en musl ni builds sin GIL. PyPI cambia a diario, así que repite la comprobación de wheels antes de actuar según cualquier fila. El script, las pruebas de humo y la salida en bruto están en la [carpeta de descarga](/downloads/python-3-15-web-scraping/README.md): [lab315.py](/downloads/python-3-15-web-scraping/lab315.py), [results.csv](/downloads/python-3-15-web-scraping/results.csv), [results.json](/downloads/python-3-15-web-scraping/results.json) y la repetición del 16 de octubre, [results-2026-10-16.json](/downloads/python-3-15-web-scraping/results-2026-10-16.json).

## Relacionado

- [Proxies en Scrapy 2.19](/es/guides/scrapy-proxy-setup), para autenticación, rotación y los errores de proxy que registra Scrapy.
- [Proxies en aiohttp](/es/guides/aiohttp-proxy-setup) y [proxies en curl_cffi](/es/guides/curl-cffi-proxy-setup), que se instalaron desde wheels en 3.15.
- [Corregir "Missing dependencies for SOCKS support"](/es/guides/fix-missing-dependencies-for-socks-support), el otro error de instalación de este stack.
- [¿Cuántos GB de proxies necesito?](/es/blog/how-many-gb-of-proxies-do-i-need), para dimensionar el tráfico cuando el stack funcione. La compresión importa ahí: un cliente que ofrece menos codificaciones puede descargar más bytes.

ipvolt está construyendo infraestructura de proxies para desarrolladores y agentes. [Únete a la lista de espera](https://ipvolt.com/#waitlist-hero) para recibir un correo cuando se abra el acceso.

## Fuentes

- [Python.org: página de la versión Python 3.15.0](https://www.python.org/downloads/release/python-3150/)
- [What's New in Python 3.15: UTF-8 como codificación por defecto](https://docs.python.org/3.15/whatsnew/3.15.html#whatsnew315-utf8-default)
- [PEP 686: Make UTF-8 mode default](https://peps.python.org/pep-0686/)
- [PyPI: archivos de brotli 1.2.0](https://pypi.org/project/brotli/1.2.0/#files)
- [PyPI: archivos de zstandard 0.25.0](https://pypi.org/project/zstandard/0.25.0/#files)
- [PyPI: archivos de zope.interface 8.6](https://pypi.org/project/zope.interface/8.6/#files)
- [PyPI: archivos de zope.interface 8.7](https://pypi.org/project/zope.interface/8.7/#files)
- [PyPI: Scrapy 2.19.0](https://pypi.org/project/Scrapy/2.19.0/)
- [google/brotli-wheels, issue #25: Python 3.15 wheels missing](https://github.com/google/brotli-wheels/issues/25)
- [google/brotli, pull request #1583: Build and test CPython 3.15 wheels in CI](https://github.com/google/brotli/pull/1583)
- [Microsoft C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)

## Entérate cuando se abra el acceso a ipvolt.

Ya que estás aquí

ipvolt está en desarrollo. Deja tu correo y te avisaremos una sola vez cuando se abra el acceso.

Un correo cuando se abra el acceso. Nada más.

[Solicitar acceso anticipado](https://ipvolt.com/es/blog/python-3-15-web-scraping#waitlist-blog-end)

[Privacidad](https://ipvolt.com/privacy)


## Artículos relacionados

- [Comprobar fugas de IP, DNS y WebRTC de un proxy con ToDetect](https://ipvolt.com/es/blog/proxy-leak-check-todetect.md) (Análisis, 23 oct 2026, 10 min de lectura): Comprueba si un proxy de navegador filtra IP, DNS, WebRTC o zona horaria con las herramientas gratuitas de ToDetect, con resultados medidos en una línea móvil 4G.
- [Parsear precios japoneses: yenes, ancho completo y etiquetas fiscales](https://ipvolt.com/es/blog/japanese-price-parsing.md) (Análisis, 6 oct 2026, 7 min de lectura): Parsea precios japoneses sin perder la divisa ni la base fiscal, con un fixture de Python probado: yen de ancho completo, precios mixtos y casos de revisión deliberados.
- [SOCKS5 vs SOCKS5h: diferencias y qué envían 6 clientes (probado)](https://ipvolt.com/es/blog/socks5-vs-socks5h.md) (Comparativa, 4 oct 2026, 8 min de lectura): socks5:// no significa DNS local en todos los clientes. Registramos qué envían curl, Requests, HTTPX, aiohttp, Playwright y Node a un proxy SOCKS5 con cada esquema.

## Guías relacionadas

- [Proxies en Scrapy 2.19: autenticación, rotación, errores](https://ipvolt.com/es/guides/scrapy-proxy-setup.md): Configura proxies en Scrapy 2.19: credenciales en meta["proxy"], rotación por petición, HTTP_PROXY y soluciones para TunnelError 407, el 407 ignorado y socks5.
- [Scrapy no llama a start_requests(): Crawled 0 pages](https://ipvolt.com/es/guides/scrapy-start-requests-not-called.md): Scrapy 2.16 eliminó Spider.start_requests(): el spider registra Crawled 0 pages y se cierra como finished sin avisos. Solución: async def start(). Probado en 2.19.
- [Proxies en aiohttp 3.14: auth, trust_env y errores](https://ipvolt.com/es/guides/aiohttp-proxy-setup.md): Configura un proxy en aiohttp 3.14: sustituye BasicAuth y proxy_auth, ya obsoletos, activa trust_env y entiende los errores 407 y Bad status line. Probado en 3.14.4.

## Sobre ipvolt

Análisis técnico del equipo de ipvolt.

El acceso a ipvolt aún no está abierto.

[Leer el original en inglés](https://ipvolt.com/blog/python-3-15-web-scraping.md)
