Programació del WebScraping
✅ Justificació i reflexió: De base de dades estàtica a dinàmica
He implementat un sistema de web scraping per transformar el meu WordPress en una base de dades dinàmica que alimentarà el XatBot. Aquesta decisió respon a una anàlisi crítica de les necessitats del projecte:
- De contingut estàtic a dinàmic: En lloc de mantenir manualment un fitxer de coneixement, el scraper actualitza automàticament la base de dades del xatbot cada cop que publico contingut nou. Això garanteix que les respostes del bot estiguin sempre actualitzades sense esforç addicional.
- Ètica del scraping: He implementat delays de 0.5 segons entre peticions per respectar la infraestructura del servidor i evitar ser identificat com a atac. Aquesta pràctica ètica és fonamental per mantenir la bona relació amb els serveis web.
- Robustesa del sistema: He incorporat gestió d'errors exhaustiva (try-except), timeouts de 10 segons i verificació de codis d'estat HTTP per garantir que l'script continuï funcionant encara que alguna pàgina falli.
- Integritat de les dades: L'ús d'un sistema de cache (set d'URLs visitades) evita duplicats i garanteix que cada pàgina només es processi una vegada, assegurant la integritat del JSON final.
Millores implementades respecte a una versió bàsica
📄 Versió bàsica
- Scraper lineal
- Delay simple
- Timeout bàsic
- Try-except genèric
- JSON simple
⭐ Versió millorada (Tarik Aberdane)
- ✅ Scraper recursiu amb cua (deque)
- ✅ Delay dinàmic (0.5s + jitter aleatori)
- ✅ Timeout configurable amb reintents (retry)
- ✅ Excepcions específiques (ConnectionError, Timeout)
- ✅ JSON amb metadades (data_scraped, versió)
- ✅ Filtratge avançat d'etiquetes (h1, h2, h3, p, li)
- ✅ Sistema de cache amb hash de contingut
1. Lògica de Scraping i Profunditat BeautifulSoup + Recursivitat
Scraper recursiu que navega per tota la jerarquia del WordPress
He implementat un scraper recursiu amb BeautifulSoup que utilitza una cua de treball (deque) per explorar totes les pàgines del meu WordPress de manera eficient. A diferència d'un scraper lineal, aquest mètode garanteix que no es perdi cap pàgina, fins i tot en estructures complexes.
# ============================================ # SCRAPER RECURSIU AMB BEAUTIFULSOUP # Autor: Tarik Aberdane # Versió: 3.0 (amb millores de robustesa) # ============================================ import requests from bs4 import BeautifulSoup from collections import deque import time import json import hashlib import random from urllib.parse import urljoin, urlparse class WebScraper: def __init__(self, base_url, delay=0.5, timeout=10): self.base_url = base_url self.visited = set() # Cache d'URLs visitades self.queue = deque([base_url]) # Cua de treball self.delay = delay self.timeout = timeout self.data = [] # Dades extretes self.content_hash = set() # Evitar duplicats per contingut def extract_content(self, soup, url): """Extreu el contingut net de la pàgina evitant brossa HTML""" content = { "url": url, "titol": "", "contingut": [], "data_scraped": time.strftime("%Y-%m-%d %H:%M:%S") } # Extracció del títol (h1 principal) title_tag = soup.find('h1') if title_tag: content["titol"] = title_tag.get_text(strip=True) else: # Fallback: títol de la pàgina title_tag = soup.find('title') if title_tag: content["titol"] = title_tag.get_text(strip=True) # Extracció de contingut: h1, h2, h3, p, li for tag in soup.find_all(['h1', 'h2', 'h3', 'p', 'li']): text = tag.get_text(strip=True) if text and len(text) > 10: # Ignorar text massa curt # Evitar contingut repetitiu (menús, etc.) if not any(skip in text.lower() for skip in ['menu', 'cerca', 'search', 'login']): content["contingut"].append({ "tipus": tag.name, "text": text }) return content if content["contingut"] else None def get_internal_links(self, soup, current_url): """Troba tots els enllaços interns de la pàgina""" links = [] for a in soup.find_all('a', href=True): href = a['href'] full_url = urljoin(current_url, href) # Només enllaços interns (mateix domini) if urlparse(full_url).netloc == urlparse(self.base_url).netloc: # Eliminar fragments (#) i paràmetres innecessaris clean_url = full_url.split('#')[0].split('?')[0] if clean_url not in self.visited: links.append(clean_url) return links def request_with_retry(self, url, max_retries=3): """Realitza una petició HTTP amb reintents automàtics""" for attempt in range(max_retries): try: # Jitter aleatori per evitar patrons recognoscibles time.sleep(self.delay + random.uniform(0.1, 0.3)) response = requests.get( url, timeout=self.timeout, headers={'User-Agent': 'Mozilla/5.0 (compatible; TarikBot/1.0)'} ) if response.status_code == 200: return response elif response.status_code in [404, 403, 500]: print(f"⚠️ Error HTTP {response.status_code} a {url}") return None else: print(f"⚠️ Codi inesperat {response.status_code} a {url}") return None except requests.exceptions.Timeout: print(f"⏰ Timeout a {url} (intent {attempt+1}/{max_retries})") if attempt == max_retries - 1: return None except requests.exceptions.ConnectionError: print(f"🔌 Error de connexió a {url}") return None except Exception as e: print(f"❌ Error inesperat a {url}: {e}") return None return None def scrape(self): """Executa el scraping recursiu""" print(f"🚀 Iniciant scraping de {self.base_url}") while self.queue: current_url = self.queue.popleft() if current_url in self.visited: continue response = self.request_with_retry(current_url) if not response: continue soup = BeautifulSoup(response.text, 'html.parser') # Extreure contingut content = self.extract_content(soup, current_url) if content: # Evitar duplicats per hash de contingut content_hash = hashlib.md5(str(content["contingut"]).encode()).hexdigest() if content_hash not in self.content_hash: self.content_hash.add(content_hash) self.data.append(content) print(f"✅ Extret: {content['titol'][:50]}...") # Afegir nous enllaços a la cua new_links = self.get_internal_links(soup, current_url) for link in new_links: if link not in self.visited and link not in self.queue: self.queue.append(link) self.visited.add(current_url) print(f"🏁 Scraping finalitzat! {len(self.data)} pàgines processades") return self.data def save_to_json(self, filename="dades_tarik_total.json"): """Guarda les dades en JSON amb metadades""" output = { "metadata": { "version": "3.0", "data_scraped": time.strftime("%Y-%m-%d %H:%M:%S"), "total_pagines": len(self.data), "base_url": self.base_url }, "contingut": self.data } with open(filename, 'w', encoding='utf-8') as f: json.dump(output, f, ensure_ascii=False, indent=2) print(f"💾 Dades guardades a {filename}") return filename
"Necessito un script de Python per a Google Colab que faci WebScraping recursiu a la meva web. Vull que el codi comenci a la pàgina principal i segueixi tots els enllaços interns que trobi. Ha d'utilitzar BeautifulSoup per extreure només el text net dels títols (h1, h2, h3) i els paràgrafs (p), evitant menús i publicitat. També ha d'incloure delays per no saturar el servidor, un sistema per no repetir pàgines ja visitades, i gestionar errors de connexió."
2. Automatització i Estructura JSON Escalable + Cache
Bolcat automàtic a JSON amb sistema de verificació de duplicats
El text extret es bolca automàticament en un fitxer JSON perfectament estructurat. He implementat un sistema de cache basat en hash de contingut per evitar duplicats i garantir que cada pàgina només es processi una vegada. L'estructura JSON és escalable i inclou metadades importants.
{
"metadata": {
"version": "3.0",
"data_scraped": "2026-03-23 14:30:00",
"total_pagines": 249,
"base_url": "https://taberdane.inscastellbisbal.net"
},
"contingut": [
{
"url": "https://taberdane.inscastellbisbal.net/sobre-mi/",
"titol": "Sobre mi - Tarik Aberdane",
"data_scraped": "2026-03-23 14:30:05",
"contingut": [
{"tipus": "h1", "text": "Sobre mi"},
{"tipus": "p", "text": "Sóc Tarik Aberdane, estudiant de SMX..."},
{"tipus": "h2", "text": "Formació"},
{"tipus": "p", "text": "Estudiant de Sistemes Microinformàtics i Xarxes..."}
]
}
]
}
3. Robustesa: Delays i Gestió d'Errors Timeouts + Retry + Excepcions
Sistema avançat per garantir l'estabilitat del scraping
He implementat tres capes de robustesa per garantir que l'script no s'aturi davant de cap problema:
- Delays ètics (0.5s + jitter aleatori): He afegit un delay base de 0.5 segons entre peticions, amb un petit jitter aleatori per evitar patrons recognoscibles i no saturar el servidor.
- Timeouts configurats (10 segons): Cada petició té un límit de temps màxim per evitar que l'script es quedi congelat esperant una resposta.
- Gestió d'excepcions específiques: Diferencío entre errors de connexió, timeouts, errors HTTP (404, 500) i errors inesperats, permetent que l'script continuï amb la següent URL.
- Sistema de reintents: Si una petició falla per timeout, es reintenta fins a 3 vegades abans de saltar a la següent URL.
# Funció amb reintents i jitter aleatori import random def request_with_retry(url, max_retries=3, timeout=10): """Realitza una petició HTTP amb reintents automàtics""" for attempt in range(max_retries): try: # Jitter aleatori per evitar patrons time.sleep(self.delay + random.uniform(0.1, 0.3)) response = requests.get( url, timeout=timeout, headers={'User-Agent': 'Mozilla/5.0 (compatible; TarikBot/1.0)'} ) if response.status_code == 200: return response elif response.status_code in [404, 403, 500]: print(f"⚠️ Error HTTP {response.status_code} a {url}") return None else: print(f"⚠️ Codi inesperat {response.status_code} a {url}") return None except requests.exceptions.Timeout: print(f"⏰ Timeout a {url} (intent {attempt+1}/{max_retries})") if attempt == max_retries - 1: return None except requests.exceptions.ConnectionError: print(f"🔌 Error de connexió a {url}") return None except Exception as e: print(f"❌ Error inesperat a {url}: {e}") return None return None # Delay ètic amb jitter time.sleep(self.delay + random.uniform(0.1, 0.3))
"Com puc fer que el meu scraper sigui més robust? Necessito gestionar errors de connexió, timeouts, i afegir reintents automàtics. També vull un delay amb jitter aleatori per no saturar el servidor."
4. Iteració i Co-programació amb IA 3 Iteracions + Prompts refinats
Procés de millora contínua amb ajuda de la IA
He utilitzat la IA (Gemini) com a copilot per generar, millorar i documentar el codi. El procés ha constat de 3 iteracions principals:
Scraper bàsic amb BeautifulSoup i navegació lineal
Feedback IA: "Afegeix recursivitat i sistema de cache"
Scraper recursiu amb cua i verificació de duplicats
Feedback IA: "Millora la gestió d'errors i afegeix reintents"
Versió final amb jitter, timeouts, JSON amb metadades
Feedback IA: "Documenta el codi i crea README/CHANGELOG"
# Exemple de conversa amb IA per depurar un error [USUARI] El meu scraper es queda penjat quan troba una pàgina que triga massa a respondre. Com ho puc solucionar? [IA] Afegeix un timeout a la petició requests i gestiona l'excepció TimeoutError: response = requests.get(url, timeout=10) try: response = requests.get(url, timeout=10) except requests.exceptions.Timeout: print(f"Timeout a {url}, saltant...") continue També pots afegir reintents automàtics per a pàgines que fallen temporalment. # Després de la correcció, vaig implementar el sistema de reintents amb retry
5. Documentació del Repositori README + CHANGELOG + Commits
Documentació professional en Markdown
# XatBot Talent 2026 - WebScraper
## 🚀 Descripció
Scraper recursiu desenvolupat en Python per extreure contingut del WordPress personal i convertir-lo en una base de dades JSON per al XatBot.
## 🛠️ Tecnologies utilitzades
- Python 3.10+
- BeautifulSoup4
- Requests
- JSON (estructura de dades)
## 📁 Estructura del projecte
- `scraper.py` - Script principal de scraping
- `dades_tarik_total.json` - Base de dades generada
- `README.md` - Documentació del projecte
- `CHANGELOG.md` - Historial de versions
## 🔧 Instal·lació
```bash
pip install beautifulsoup4 requests
python scraper.py 