[Python] Beginner in Python and Web Scraping – Looking for Feedback on My Script

Discussão em 'Python' iniciado por Stack, Agosto 28, 2025.

  1. Stack

    Stack Membro Participativo

    I’m a software engineering student currently doing an internship in the Business Intelligence area at a university. As part of a project, I decided to create a script that scrapes job postings from a website to later use in data analysis.

    Here’s my situation:


    • I’m completely new to both Python and web scraping.


    • I’ve been learning through documentation, tutorials, and by asking ChatGPT :'( .


    • After some effort, I managed to put together a semi-functional script, but it still contains many errors and inefficiencies.

    import os
    import csv
    import time
    import threading
    import tkinter as tk

    from datetime import datetime

    from selenium import webdriver

    from selenium.common.exceptions import NoSuchElementException

    from selenium.webdriver import Chrome
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC

    from webdriver_manager.chrome import ChromeDriverManager

    # Variables globales
    URL = "https://www.elempleo.com/co/ofertas-empleo/?Salaries=menos-1-millon:10-125-millones&PublishDate=hoy"
    ofertas_procesadas = set()

    # Configuración carpeta y archivo
    now = datetime.now()
    fecha = now.strftime("%Y-%m-%d - %H-%M")
    CARPETA_DATOS = "datos"
    ARCHIVO_CSV = os.path.join(CARPETA_DATOS, f"ofertas_elempleo - {fecha}.csv")

    if not os.path.exists(CARPETA_DATOS):
    os.makedirs(CARPETA_DATOS)

    if not os.path.exists(ARCHIVO_CSV):
    with open(ARCHIVO_CSV, "w", newline="", encoding="utf-8") as file:
    # Cambiar delimiter al predeterminado
    writer = csv.writer(file, delimiter="|")
    writer.writerow(["id", "Titulo", "Salario", "Ciudad", "Fecha", "Detalle", "Cargo", "Tipo de puesto", "Nivel de educación", "Sector", "Experiencia", "Tipo de contrato", "Vacantes", "Areas", "Profesiones", "Nombre empresa", "Descripcion empresa", "Habilidades", "Cargos"])

    # Ventana emnergente
    root = tk.Tk()
    root.title("Ejecución en proceso")
    root.geometry("350x100")
    root.resizable(False, False)
    label = tk.Label(root, text="Ejecutando script...", font=("Arial", 12))
    label.pack(pady=20)

    def setup_driver():
    # Configuracion del navegador
    service = Service(ChromeDriverManager().install())
    option=webdriver.ChromeOptions()
    ## option.add_argument('--headless')
    option.add_argument("--ignore-certificate-errors")
    driver = Chrome(service=service, options=option)
    return driver

    def cerrar_cookies(driver):
    # Cerrar ventana cookies
    try:
    btn_cookies = WebDriverWait(driver, 5).until(
    EC.presence_of_element_located((By.XPATH, "//div[@class='col-xs-12 col-sm-4 buttons-politics text-right']//a"))
    )
    btn_cookies.click()
    except NoSuchElementException:
    pass

    def extraer_info_oferta(driver):
    label.config(text="Escrapeando ofertas...")

    try:
    # Elementos sencillos
    titulo_oferta_element = driver.find_element(By.XPATH, "//div[@class='eeoffer-data-wrapper']//h1")
    salario_oferta_element = driver.find_element(By.XPATH, "//div[@class='eeoffer-data-wrapper']//span[contains(@class,'js-joboffer-salary')]")
    ciudad_oferta_element = driver.find_element(By.XPATH, "//div[@class='eeoffer-data-wrapper']//span[contains(@class,'js-joboffer-city')]")
    fecha_oferta_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-clock-o')]/following-sibling::span[2]")
    detalle_oferta_element = driver.find_element(By.XPATH, "//div[@class='description-block']//p//span")
    cargo_oferta_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-sitemap')]/following-sibling::span")
    tipo_puesto_oferta_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-user-circle')]/parent::p")
    sector_oferta_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-building')]/following-sibling::span")
    experiencia_oferta_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-list')]/following-sibling::span")
    tipo_contrato_oferta_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-file-text')]/following-sibling::span")
    vacantes_oferta_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-address-book')]/parent::p")

    # Limpiar el texto de detalle_oferta_element
    detalle_oferta_texto = detalle_oferta_element.text.replace("\n", " ").replace("|", " ").replace(" ", " ").replace(" ", " ").replace(" ", " ").replace("\t", " ").replace(";" , " ").strip()

    # Campo Id
    try:
    id_oferta_element = WebDriverWait(driver, 5).until(
    EC.presence_of_element_located((By.XPATH, "//div[contains(@class,'offer-data-additional')]//p//span[contains(@class,'js-offer-id')]"))
    )
    id_oferta_texto = id_oferta_element.get_attribute("textContent").strip()
    except:
    if not id_oferta_texto:
    id_oferta_texto = WebDriverWait(driver, 1).until(
    EC.presence_of_element_located((By.XPATH, "//div[contains(@class,'offer-data-additional')]//p//span[contains(@class,'js-offer-id')]"))
    )
    id_oferta_texto = id_oferta_element.get_attribute("textContent").strip()

    # Campos sensibles
    try:
    nivel_educacion_oferta_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-graduation-cap')]/following-sibling::span")
    nivel_educacion_oferta_texto = nivel_educacion_oferta_element.text
    except:
    nivel_educacion_oferta_texto = ""

    # Elementos con menú desplegable
    try:
    boton_area_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-users')]/following-sibling::a")
    driver.execute_script("arguments[0].click();", boton_area_element)
    areas = WebDriverWait(driver, 1).until(
    EC.presence_of_all_elements_located((By.XPATH, "//div[@class='modal-content']//div[@class='modal-body']//li[@class='js-area']"))
    )
    areas_texto = [area.text.strip() for area in areas]
    driver.find_element(By.XPATH, "//div[@id='AreasLightBox']//i[contains(@class,'fa-times-circle')]").click()
    except:
    area_oferta = driver.find_element(By.XPATH, "//i[contains(@class,'fa-users')]/following-sibling::span")
    areas_texto = [area_oferta.text.strip()]

    areas_oferta = ", ".join(areas_texto)

    try:
    boton_profesion_element = driver.find_element(By.XPATH, "//i[contains(@class,'fa-briefcase')]/following-sibling::a")
    driver.execute_script("arguments[0].click();", boton_profesion_element)
    profesiones = WebDriverWait(driver, 1).until(
    EC.presence_of_all_elements_located((By.XPATH, "//div[@class='modal-content']//div[@class='modal-body']//li[@class='js-profession']"))
    )
    profesiones_texto = [profesion.text.strip() for profesion in profesiones]
    driver.find_element(By.XPATH, "//div[@id='ProfessionLightBox']//i[contains(@class,'fa-times-circle')]").click()
    except:
    profesion_oferta = driver.find_element(By.XPATH, "//i[contains(@class,'fa-briefcase')]/following-sibling::span")
    profesiones_texto = [profesion_oferta.text.strip()]

    profesiones_oferta = ", ".join(profesiones_texto)

    # Información de la empresa
    try:
    nombre_empresa_oferta_element = driver.find_element(By.XPATH, "//div[contains(@class,'ee-header-company')]//strong")
    except:
    nombre_empresa_oferta_element = driver.find_element(By.XPATH, "//div[contains(@class,'data-company')]//span//span//strong")

    try:
    descripcion_empresa_oferta_element = driver.find_element(By.XPATH, "//div[contains(@class,'eeoffer-data-wrapper')]//div[contains(@class,'company-description')]//div")
    except:
    descripcion_empresa_oferta_element = driver.find_element(By.XPATH, "//div[contains(@class,'eeoffer-data-wrapper')]//span[contains(@class,'company-sector')]")

    # Información adicional
    try:
    habilidades = driver.find_elements(By.XPATH, "//div[@class='ee-related-words']//div[contains(@class,'ee-keywords')]//li//span")

    habilidades_texto = [habilidad.text.strip() for habilidad in habilidades if habilidad.text.strip()]
    except:
    try:
    habilidades = driver.find_elements(By.XPATH, "//div[contains(@class,'ee-related-words')]//div[contains(@class,'ee-keywords')]//li//span")
    habilidades_texto = [habilidad.text.strip() for habilidad in habilidades if habilidad.text.strip()]
    except:
    habilidades_texto = []

    if habilidades_texto:
    habilidades_oferta = ", ".join(habilidades_texto)
    else:
    habilidades_oferta = ""

    try:
    cargos = driver.find_elements(By.XPATH, "//div[@class='ee-related-words']//div[contains(@class,'ee-container-equivalent-positions')]//li")
    cargos_texto = [cargo.text.strip() for cargo in cargos if cargo.text.strip()]
    except:
    try:
    cargos = driver.find_elements(By.XPATH, "//div[contains(@class,'ee-related-words')]//div[contains(@class,'ee-equivalent-positions')]//li//span")
    cargos_texto = [cargo.text.strip() for cargo in cargos if cargo.text.strip()]
    except:
    cargos_texto = []

    if cargos_texto:
    cargos_oferta = ", ".join(cargos_texto)
    else:
    cargos_oferta = ""

    # Tratamiento fecha invisible
    fecha_oferta_texto = fecha_oferta_element.get_attribute("textContent").strip()
    return id_oferta_texto, titulo_oferta_element, salario_oferta_element, ciudad_oferta_element, fecha_oferta_texto, detalle_oferta_texto, cargo_oferta_element, tipo_puesto_oferta_element, nivel_educacion_oferta_texto, sector_oferta_element, experiencia_oferta_element, tipo_contrato_oferta_element, vacantes_oferta_element, areas_oferta, profesiones_oferta, nombre_empresa_oferta_element, descripcion_empresa_oferta_element, habilidades_oferta, cargos_oferta
    except Exception:
    return label.config(text=f"Error al obtener la información de la oferta")

    def escritura_datos(id_oferta_texto,
    titulo_oferta_element,
    salario_oferta_element,
    ciudad_oferta_element,
    fecha_oferta_texto,
    detalle_oferta_texto,
    cargo_oferta_element,
    tipo_puesto_oferta_element,
    nivel_educacion_oferta_texto,
    sector_oferta_element,
    experiencia_oferta_element,
    tipo_contrato_oferta_element,
    vacantes_oferta_element,
    areas_oferta,
    profesiones_oferta,
    nombre_empresa_oferta_element,
    descripcion_empresa_oferta_element,
    habilidades_oferta,
    cargos_oferta
    ):
    datos = [id_oferta_texto,
    titulo_oferta_element.text,
    salario_oferta_element.text,
    ciudad_oferta_element.text,
    fecha_oferta_texto,
    detalle_oferta_texto,
    cargo_oferta_element.text,
    tipo_puesto_oferta_element.text,
    nivel_educacion_oferta_texto,
    sector_oferta_element.text,
    experiencia_oferta_element.text,
    tipo_contrato_oferta_element.text,
    vacantes_oferta_element.text,
    areas_oferta,
    profesiones_oferta,
    nombre_empresa_oferta_element.text,
    descripcion_empresa_oferta_element.text,
    habilidades_oferta,
    cargos_oferta
    ]
    label.config(text="Escrapeando ofertas..")
    with open(ARCHIVO_CSV, "a", newline="", encoding="utf-8") as file:
    writer = csv.writer(file, delimiter="|")
    writer.writerow(datos)

    def procesar_ofertas_pagina(driver):
    global ofertas_procesadas
    while True:
    try:
    WebDriverWait(driver, 10).until(
    EC.presence_of_all_elements_located((By.XPATH, "//div[contains(@class, 'js-results-container')]"))
    )
    except Exception as e:
    print(f"No se encontraron ofertas: {str(e)}")
    return

    ofertas = WebDriverWait(driver, 5).until(
    EC.presence_of_all_elements_located((By.XPATH, "//div[contains(@class,'result-item')]//a[contains(@class,'js-offer-title')]"))
    )
    print(f"Ofertas encontradas en la página: {len(ofertas)}")

    for index in range(len(ofertas)):
    try:
    ofertas_actulizadas = WebDriverWait(driver, 5).until(
    EC.presence_of_all_elements_located((By.XPATH, "//div[contains(@class,'result-item')]//a[contains(@class,'js-offer-title')]"))
    )
    oferta = ofertas_actulizadas[index]

    enlace = oferta.get_attribute("href")
    label.config(text="Ofertas encontradas.")

    if not enlace:
    label.config(text="Error al obtener el enlace de la oferta")
    continue

    label.config(text="Escrapeando ofertas...")
    driver.execute_script(f"window.open('{enlace}', '_blank')")
    time.sleep(2)
    driver.switch_to.window(driver.window_handles[-1])

    try:
    datos_oferta = extraer_info_oferta(driver)
    if datos_oferta:
    id_oferta = datos_oferta[0]
    if id_oferta not in ofertas_procesadas:
    escritura_datos(*datos_oferta)
    ofertas_procesadas.add(id_oferta)
    print(f"Oferta numero {index + 1} de {len(ofertas)}.")

    except Exception as e:
    print(f"Error en la oferta: {str(e)}")

    driver.close()
    driver.switch_to.window(driver.window_handles[0])
    except Exception as e:
    print(f"Error procesando laoferta {index}: {str(e)}")
    return False

    label.config(text="Cambiando página de ofertas...")
    if not siguiente_pagina(driver):
    break

    def siguiente_pagina(driver):
    try:
    btn_siguiente = driver.find_element(By.XPATH, "//ul[contains(@class,'pagination')]//li//a//i[contains(@class,'fa-angle-right')]")
    li_contenedor = driver.find_element(By.XPATH, "//ul[contains(@class,'pagination')]//li//a//i[contains(@class,'fa-angle-right')]/ancestor::li")
    if "disabled" in li_contenedor.get_attribute("class").split():
    return False
    else:
    driver.execute_script("arguments[0].click();", btn_siguiente)
    WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.XPATH, "//div[@class='result-item']//a"))
    )
    return True
    except NoSuchElementException:
    return False



    def main():
    global root
    driver = setup_driver()
    try:
    driver.get(URL)
    cerrar_cookies(driver)

    while True:
    procesar_ofertas_pagina(driver)

    # label.config(text="Cambiando página de ofertas...")
    # if not siguiente_pagina(driver):
    # break
    finally:
    driver.quit()
    root.destroy()

    def run_scraping():
    main()

    threading.Thread(target=run_scraping).start()
    root.mainloop()


    I would really appreciate it if someone with more experience in Python/web scraping could take a look and give me advice on what I could improve in my code (best practices, structure, libraries, etc.).

    Thank you in advance!

    Continue reading...

Compartilhe esta Página