Cómo construir una tubería de scraping web incremental con Scrapeless
Senior Web Scraping Engineer
TL;DR:
- Una solicitud condicional convierte una página sin cambios en un
304con un cuerpo de 0 bytes; la respuesta completa fue de 50,388 bytes, y diez verificaciones condicionales se ejecutaron en 4.21 s frente a 5.52 s para diez descargas completas. - El hashing de contenido detecta cambios pero no ahorra ancho de banda: la segunda descarga de una página sin validador aún transfería 11,064 bytes antes de que se pudiera comparar el hash.
- Las huellas digitales por registro son las que reducen la escritura. Un precio trasladado hizo que una página de 20 registros se redujera a 1 fila escrita en downstream.
- Huela los campos que te importan en lugar de todo el registro, o un campo no relacionado cambiando marca todo como sucio.
- Los validadores HTTP describen el documento que envió el servidor, por lo que dejan de ser útiles en el momento en que los registros llegan a través del renderizado del lado del cliente.
- Un
304solo está disponible si la ejecución anterior almacenó el validador, lo que hace que la tabla de estado sea parte del pipeline en lugar de una optimización. - Pon un rastreo incremental en páginas renderizadas con el plan gratuito de Scrapeless.
La mayoría de los scrapers programados vuelven a descargar todo, vuelven a analizar todo y reescriben cada fila, y luego informan éxito. Eso funciona hasta que el catálogo crece, momento en el cual el trabajo diario gasta casi todo su tiempo confirmando que los datos de ayer siguen siendo los datos de ayer.
El scraping incremental es el arreglo opuesto: preguntar qué ha cambiado y hacer trabajo solo donde la respuesta es sí. La pregunta se puede hacer en tres niveles, y cada uno ahorra algo diferente: ancho de banda, análisis o escrituras. Cada capa debajo fue medida contra la misma página de catálogo en vivo, por lo que las compensaciones vienen con números en lugar de adjetivos.
Pipeline a Vistazo
| Capa | Pregunta | Mecanismo | Resultado medido |
|---|---|---|---|
| HTTP | ¿Cambió el documento? | If-None-Match / If-Modified-Since |
304, 0 bytes vs 50,388 |
| Documento | ¿Cambió los bytes? | SHA-256 del cuerpo | detectado, pero 11,064 bytes aún transferidos |
| Registro | ¿Qué filas cambiaron? | huella digital de campo por registro | 1 de 20 filas escritas |
El flujo es verificar → descargar solo si ha cambiado → extraer → comparar huellas digitales → escribir solo las diferencias. Las capas se apilan: la verificación HTTP es la más barata y menos disponible, la verificación de registro siempre está disponible y cuesta una descarga completa.
Verificar con menos frecuencia es la otra mitad del mismo problema. El Protocolo de Exclusión de Robots es donde un sitio declara lo que quiere que se rastree, y un diseño incremental es lo que permite que un scraper honre una cadencia más lenta sin quedarse atrás: cada verificación cuesta un viaje redondo en lugar de una transferencia completa.
Capa 1: Deja que el Servidor Responda
Un validador HTTP es la propia opinión del servidor sobre si su representación ha cambiado. Existen dos: ETag, un token de versión opaco, y Last-Modified, una marca de tiempo. La primera respuesta los lleva.
python
import requests
URL = "https://books.toscrape.com/catalogue/category/books/mystery_3/index.html"
first = requests.get(URL, timeout=30)
first.raise_for_status()
etag = first.headers["ETag"]
print(f"first GET {first.status_code} {len(first.content)} bytes ETag={etag}")
second = requests.get(URL, headers={"If-None-Match": etag}, timeout=30)
print(f"second GET {second.status_code} {len(second.content)} bytes")
print(f"bytes avoided: {len(first.content) - len(second.content)}")
text
first GET 200 50388 bytes ETag=W/"63e40de8-c4d4"
second GET 304 0 bytes
bytes avoided: 50388
El 304 no lleva ningún cuerpo en absoluto. El mecanismo de solicitud condicional está definido para que el cliente mantenga la copia que ya tiene, lo que significa que el scraper tiene que haber mantenido una.
Last-Modified funciona de la misma manera a través de un encabezado diferente:
python
third = requests.get(URL, headers={"If-Modified-Since": first.headers["Last-Modified"]}, timeout=30)
print(first.headers["Last-Modified"], "->", third.status_code, len(third.content), "bytes")
text
Wed, 08 Feb 2023 21:02:32 GMT -> 304 0 bytes
Prefiere el ETag cuando ambos están presentes. La especificación de semántica HTTP hace que Last-Modified sea una marca de tiempo de un segundo de resolución, por lo que dos cambios dentro del mismo segundo son indistinguibles, mientras que una etiqueta de entidad puede cambiar en cualquier edición.
El ahorro es real, pero no es toda la ejecución:
text
10 conditional GETs 4.21s
10 full GETs 5.52s
La verificación condicional se ejecutó 1.31 veces más rápido y dejó 492 KB no transferidos. La solicitud aún cuesta un viaje redondo: lo que desaparece es el cuerpo, no la conexión.
Capa 2: Hashea el Documento Cuando el Servidor No Dice Nada
Muchos objetivos no envían ningún validador. Entonces, la única forma de saber si el documento ha cambiado es descargarlo y mirar. Un resumen criptográfico es la herramienta estándar para esa comparación: el estándar SHA-256 proporciona un valor de ancho fijo donde cualquier diferencia de un solo byte produce un resumen no relacionado, por lo que la igualdad es una señal confiable de "nada se movió".
python
import hashlib
import requests
q1 = requests.get("https://quotes.toscrape.com/", timeout=30)
q1.raise_for_status()
print("ETag:", q1.headers.get("ETag"), " Last-Modified:", q1.headers.get("Last-Modified"))
q2 = requests.get("https://quotes.toscrape.com/", timeout=30)
h1 = hashlib.sha256(q1.content).hexdigest()
h2 = hashlib.sha256(q2.content).hexdigest()
print(f"sha256 run 1: {h1[:16]}...")
print(f"sha256 run 2: {h2[:16]}...")
print(f"unchanged: {h1 == h2} bytes still transferred: {len(q2.content)}")
text
ETag: None Last-Modified: None
sha256 run 1: efdc2605a2062dce...
sha256 run 2: efdc2605a2062dce...
unchanged: True bytes still transferred: 11064
Nota lo que esta capa hace y no compra. El hash informa correctamente que no hay cambio, y 11,064 bytes cruzaron la red de todos modos. El hashing de documentos ahorra análisis, escrituras en la base de datos, alertas en downstream y cualquier re-incrustación, nunca ancho de banda.
También tiene un problema de falsos positivos que los números aquí no muestran. Las páginas que llevan un token de sesión, un anuncio que rota o una marca de tiempo renderizada producen un hash diferente en cada extracción mientras que los datos son idénticos. Hacer hash de los registros extraídos en lugar del cuerpo sin procesar es lo que hace que la señal sea estable, que es la siguiente capa.
Capa 3: Huella Digital de los Registros
Las capas anteriores responden preguntas sobre un documento. Lo que una canalización generalmente necesita saber es qué filas escribir.
python
import json, hashlib
def fingerprint(record):
payload = json.dumps({k: record[k] for k in ("price", "rating")}, sort_keys=True)
return hashlib.sha256(payload.encode()).hexdigest()[:16]
Huellas digitales de un subconjunto de campos elegidos en lugar de todo el registro es la decisión que hace que esto funcione. Incluya un campo que cambie por sí mismo: un conteo de vistas, una marca de tiempo de "última vista", una posición en una lista clasificada, y cada registro se verá sucio en cada ejecución.
Almacene una huella digital por registro, luego compare la siguiente extracción contra ella:
python
known = {title: (fp, price) for title, fp, price in conn.execute("SELECT title, fp, price FROM snap")}
new, changed, same = [], [], 0
for record in second_pass:
previous = known.get(record["title"])
if previous is None:
new.append(record)
elif previous[0] != fingerprint(record):
changed.append((record["title"], previous[1], record["price"]))
else:
same += 1
Contra la página activa de 20 registros con un precio alterado para representar un movimiento real:
text
parsed 20 records from the live page
stored baseline: 20 fingerprints
example fingerprint: 'Sharp Objects' -> e974692e9d935048
unchanged 19 | changed 1 | new 0
CHANGED A Murder in Time £16.64 -> £99.99
rows written downstream: 1 of 20
Una fila escrita en lugar de veinte. En un catálogo donde la realidad diaria es que casi nada se mueve, esa proporción es todo el argumento a favor de la huella digital: el volumen de escritura sigue la tasa de cambio en lugar del tamaño del catálogo.
Mantener el valor anterior junto a la huella digital es lo que convierte la detección en un evento utilizable. £16.64 -> £99.99 es un registro de cambio de precio; una bandera sucia es solo una pista de que algo sucedió.
¿Ejecutando un rastreo incremental contra páginas que renderizan del lado del cliente? El plan gratuito de Scrapeless cubre suficientes solicitudes para construir la línea base y los primeros diffs.
Donde la Capa HTTP Deja de Aplicar
Un validador describe el documento que envió el servidor. Cuando los registros llegan a través de la renderización del lado del cliente, ese documento es el contenedor de la aplicación, y su ETag rastrea el despliegue del contenedor en lugar del catálogo.
La consecuencia es específica: una página puede devolver 304 mientras que los precios detrás de ella han cambiado, porque el contenedor realmente no cambió. Cualquier objetivo cuyo contenido se ensamble en el navegador debe ser verificado a nivel de registro, utilizando la API Universal de Scraping para renderizar antes de comparar.
Lo mismo se aplica a un punto final JSON interno que la página llama. A menudo envían validadores, y cuando lo hacen, la capa superior vuelve a funcionar contra la carga útil que realmente lleva los registros.
Almacenando el Estado
Nada de esto funciona sin un lugar para mantener lo que la última ejecución aprendió. El estado que necesita una canalización es pequeño:
| Columna | Propósito |
|---|---|
url |
lo que se verificó |
etag / last_modified |
reproducido como el encabezado condicional |
body_sha256 |
comparación a nivel de documento cuando no existe un validador |
checked_at |
cuando la respuesta fue confirmada por última vez |
Por registro, la tabla contiene la clave, la huella digital, y los valores anteriores que desee informar. Ambos encajan junto a los datos extraídos en la misma base de datos, y la forma de la canalización ETL no cambia: se añade un paso de verificación frente a la extracción.
Una nota operativa que es fácil de pasar por alto: un ETag solo es válido para la URL que lo emitió. Reproducir una etiqueta almacenada contra una cadena de consulta diferente o una variante paginada producirá un 200 y un cuerpo completo, lo cual es un comportamiento correcto y no un error.
Conclusión
El scraping incremental son tres preguntas, y saber cuál estás haciendo decide lo que guardas. El validador HTTP guarda el cuerpo: 50,388 bytes se convirtieron en un 304 de 0 bytes. El hash del documento guarda el análisis y las escrituras pero nunca el ancho de banda, ya que los 11,064 bytes llegan antes de la comparación. La huella digital del registro guarda la escritura, y llevó una página de veinte registros a una sola fila.
Comienza con el validador cuando el servidor ofrezca uno, vuelve al hashing de los registros extraídos en lugar del cuerpo sin procesar, y haz huella digital solo de los campos cuyo movimiento realmente te importa. Precios enumera lo que cuestan las extracciones restantes una vez que las que no cambiaron dejan de ocurrir.
¿Listo para dejar de volver a extraer páginas que no se han movido? Comienza con el plan gratuito de Scrapeless y construye la línea base contra la que compara tu próxima ejecución.
FAQ
Q: ¿Qué es el raspado web incremental?
Raspar solo lo que cambió desde la última ejecución, en lugar de volver a recolectar todo el objetivo. Se implementa como un chequeo que se ejecuta antes de la obtención o antes de la escritura: una solicitud HTTP condicional, una comparación de hash de documentos, o una comparación de huellas digitales por registro. El efecto medido aquí fue de 50,388 bytes evitados en la capa HTTP y 19 de 20 filas omitidas en la capa de registros.
Q: ¿Cómo uso ETag en un raspador?
Almacena el encabezado ETag de la respuesta, luego envíalo de vuelta como If-None-Match en la siguiente solicitud para esa misma URL. Un servidor que acepta que no ha cambiado responde 304 con un cuerpo vacío, que es la señal para omitir el resto de la canalización. La etiqueta está vinculada a la URL exacta, por lo que una etiqueta almacenada reproducida contra una cadena de consulta diferente devuelve un 200 normal.
Q: ¿Qué pasa si un sitio no envía ETag o Last-Modified?
Obtén y compara hashes en su lugar. Hashea los registros extraídos en lugar del HTML crudo; un hash de cuerpo crudo cambia cuando un token de sesión, un anuncio o una marca de tiempo renderizada cambian, lo que marca la página como sucia mientras que los datos son idénticos. Esto consume el ancho de banda de cualquier manera; el ahorro está en el análisis, la escritura y cualquier cosa a continuación.
Q: ¿Debería hashear todo el registro o campos específicos?
Campos específicos. Un hash de registro completo incluye cualquier cosa que la página lleve, por lo que una posición de rango o un contador de "última vista" hace que cada registro parezca cambiado en cada ejecución. Hacer huella digital solo de los campos cuyo movimiento importa es lo que produjo el resultado estable de 19 sin cambios mencionado arriba.
Q: ¿Puede una página devolver 304 mientras sus datos han cambiado en realidad?
Sí, en páginas renderizadas por el cliente. El validador describe el documento HTML que el servidor envió, que para una aplicación de una sola página es la estructura en lugar de los registros, por lo que la etiqueta rastrea las implementaciones de la estructura. Esos objetivos necesitan comparación en la capa de registros después de la renderización, o contra el punto final interno de JSON que lleva los datos.
Q: ¿Cuánto ahorra realmente el raspado incremental?
Depende de qué capa responda. En la medición anterior, las solicitudes condicionales eliminaron el cuerpo de la respuesta por completo y funcionaron 1.31 veces más rápido durante diez verificaciones, y la capa de registros redujo las escrituras en un 95% en una página donde uno de veinte elementos se movió. El viaje de ida y vuelta permanece en todos los casos, por lo que el ahorro escala con el tamaño de la página y la tasa de cambio en lugar de con el conteo de solicitudes.
En Scrapeless, solo accedemos a datos disponibles públicamente y cumplimos estrictamente con las leyes, regulaciones y políticas de privacidad del sitio web aplicables. El contenido de este blog es sólo para fines de demostración y no implica ninguna actividad ilegal o infractora. No ofrecemos garantías y renunciamos a toda responsabilidad por el uso de la información de este blog o enlaces de terceros. Antes de realizar cualquier actividad de scraping, consulte a su asesor legal y revise los términos de servicio del sitio web de destino u obtenga los permisos necesarios.



