Web Scraping: Ubicaciones de Common Service Centers (India)
web-scraping
data-collection
python
freelance
Contexto del Proyecto
Este proyecto nació de una solicitud freelance para mapear los Common Service Centers (CSCs) de India, centros físicos que brindan acceso a servicios digitales gubernamentales en zonas rurales y urbanas. El cliente necesitaba:
- Un listado estructurado de todos los CSCs.
- Filtros por estado, distrito y bloque.
- Datos para análisis de cobertura geográfica.
El sitio oficial (csclocator.com) no ofrecía una API pública, por lo que desarrollé un scraper automatizado para extraer los datos directamente del HTML.
Técnicas Utilizadas
# Tecnologías clave:
- Python (BeautifulSoup, requests, pandas).
- Web scraping con manejo de headers y delays.
- Extracción de datos anidados (selectores HTML).
- Almacenamiento en Excel para facilitar su uso.
Datos Obtenidos
Se extrajeron registros con las siguientes variables:
Variable | Descripción | Ejemplo |
---|---|---|
vle_name |
Nombre del operador del CSC | “Alankit_Deepika Rustagi” |
address |
Dirección física | “Shop No: 4547, Karol Bagh” |
state |
Estado | “andaman-and-nicobar” |
district |
Distrito | “central” |
block |
Bloque administrativo | “central-delhi-nielit” |
Muestra de Datos
Desafíos y Soluciones
- Estructura compleja:
- Los CSCs se organizaban en jerarquías (estado → distrito → bloque).
- Solución: Scrapeé secuencialmente los
select
options del HTML.
- Los CSCs se organizaban en jerarquías (estado → distrito → bloque).
- Protección contra scraping:
- El sitio bloqueaba peticiones rápidas.
- Solución: Implementé
headers
con User-Agent ytime.sleep(15)
entre requests.
- El sitio bloqueaba peticiones rápidas.
- Datos inconsistentes:
- Algunas direcciones usaban formatos no estandarizados (ej: “WZ: 125 Dusghara”).
- Solución: Normalización manual posterior en Excel.
- Algunas direcciones usaban formatos no estandarizados (ej: “WZ: 125 Dusghara”).
Impacto del Proyecto
- El cliente pudo identificar zonas con baja densidad de CSCs para priorizar nuevas instalaciones.
- Los datos se usaron como base para un sistema de geolocalización interno.
- Demostró que el scraping puede ser una alternativa viable cuando no hay APIs disponibles.
Código Completo
El script de Python está disponible en GitHub o en el siguiente bloque:
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
def make_soup(url):
= 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
user_agent = {'User-Agent': user_agent}
headers = requests.get(url, headers=headers)
response
if response.status_code == 200:
= BeautifulSoup(response.text, 'html.parser')
soup return soup
else:
print(f"Error while getting the page. State code: {response.status_code}")
return None
def scrape_select_values(url):
= make_soup(url)
soup
= soup.find('select', {'id': 'state'})
state_selector = soup.find('select', {'id': 'district'})
district_selector = soup.find('select', {'id': 'block'})
block_selector
= state_selector.find_all('option')
state_options = district_selector.find_all('option')
district_options = block_selector.find_all('option')
block_options
= [option.get("value") for option in state_options[1:]]
state_data = [option.get("value") for option in district_options[1:]]
district_data = [option.get("value") for option in block_options[1:]]
block_data
return state_data, district_data, block_data
def get_info(soup, state, district, block):
= soup.find('div', class_='col-lg-12')
headings if headings:
= headings.find(class_="row")
row = [x.text for x in row.find_all("th")]
column_title = row.find_all('tr')
tr_elements
= []
data_list
for tr in tr_elements[1:]:
= tr.find_all('td')
td_elements if len(td_elements) >= 3:
= td_elements[0].text.strip()
vle_name = td_elements[1].text.strip()
address = td_elements[2].find('a')['href']
enlace
= {
data "vle_name": vle_name,
"address": address,
"enlace": enlace,
"state": state,
"district": district,
"block": block
}
data_list.append(data)
return data_list
else:
print(f"No data found for {state}/{district}/{block}")
return []
= "https://www.csclocator.com"
base_url
= []
all_data = scrape_select_values("https://www.csclocator.com/csc/delhi/delhi/new-delhi-nielit")
state_list, district_list, block_list
for state in state_list:
for district in district_list:
for block in block_list:
= f"{base_url}/csc/{state}/{district}/{block}"
url print(f"Scraping: {url}")
= make_soup(url)
soup
if soup:
= get_info(soup, state, district, block)
data_list
all_data.extend(data_list)= pd.DataFrame(all_data)
df "csc_data_partial.xlsx", index=False)
df.to_excel(15)
time.sleep(
= pd.DataFrame(all_data)
final_df "csc_data.xlsx", index=False) final_df.to_excel(