Quería crear un scraper de LinkedIn desde cero. Intenté buscar en varias soluciones disponibles públicamente pero ninguna me funcionó. Me di cuenta de que cualquier solución tradicional que funcionara obteniendo elementos de la página web y extrayendo datos relevantes era muy propensa a fallar si cambiaba algún código. Así que decidí lanzar GPT en la mezcla. Me di cuenta de que no necesitaría hacer toda esa extracción minuciosa de elementos si pudiera obtener «todo» el texto de la página y pedirle al LLM que extrajera la información por mí. Al fin y al cabo, la inferencia es una de sus aplicaciones más potentes. Así que después de adquirir conocimientos básicos sobre Selenium y estudiar algunas soluciones existentes, construí una propia. Vamos a sumergirnos en ella a continuación.
actions.py
import getpass
from . import constants as c
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
def login(driver, email=None, password=None, cookie = None, timeout=30):
try:
driver.get("https://www.linkedin.com/login")
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located(("id", "username")))
email_elem = driver.find_element("id","username")
email_elem.send_keys(email)
password_elem = driver.find_element("id","password")
password_elem.send_keys(password)
password_elem.submit()
element = WebDriverWait(driver, timeout).until(EC.presence_of_element_located(("class name", "global-nav__content"))
except Exception as e:
print(f"Failed to log in: {e}")
Utilizamos la función driver.get() para obtener la página web de inicio de sesión de LinkedIn. Usamos la función WebDriverWait() para buscar un elemento específico en la página web. Utilizamos esta función para esperar a que la página web se cargue completamente. Una vez cargada la página, utilizamos driver.find_element() para obtener el id de nombre de usuario y el id de contraseña. Una vez que obtenemos esos elementos usamos la función send_keys() para colocar nuestros valores en la caja de texto. Para el elemento password, usamos la función submit() para iniciar sesión. A continuación, utilizamos WebDriverWait() de nuevo, esta vez comprobando la existencia de la barra de navegación global que se ve en la parte superior. Si obtenemos eso, significa que estamos dentro.
entity.py
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
from .objects import Experience, Education, Scraper, Interest, Accomplishment, Contact
import os
from linkedin_scraper import selectors
class Entity(Scraper):
__TOP_CARD = "top-card-background-hero-image"
__WAIT_FOR_ELEMENT_TIMEOUT = 5
def __init__(
self,
linkedin_url=None,
driver=None,
get=True,
close_on_complete=True,
):
self.driver = driver
Creamos una clase llamada Entidad y tenemos dos constantes en ella que se utilizarán más adelante. El constructor inicializa con un linkedin_url, driver, get y close_on_complete. A continuación, establecemos el controlador de acuerdo con lo que se pasó cuando se llamó al constructor.
def scrape(self, close_on_complete=True):
driver = self.driver
try:
# Wait for the page to load
WebDriverWait(driver, self.__WAIT_FOR_ELEMENT_TIMEOUT).until(
EC.presence_of_element_located(
(
"tag name", "body"
)
)
)
self.focus()
self.wait(5)
# Scroll to the bottom of the page to load all dynamic content
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
self.wait(3) # wait for additional content to load
# Get all the text on the page
page_text = driver.find_element("tag name", "body").text
print("Scraped Text:")
print(page_text)
return page_text
except Exception as e:
print(f"Failed to scrape the page: {e}")
page_text = ""
finally:
if close_on_complete:
driver.quit()
return page_text
La función scrape() va a comprobar la existencia del cuerpo en la página. A continuación, utilizarás la función driver.execute_script() para desplazarte hasta el final de la página y cargarlo todo. Después de eso, extraerás todo el texto de la página web utilizando driver.find_element(«tag name», «body»).text y luego imprimiremos y devolveremos el texto raspado.
linkedin_scraper.py
from linkedin_scraper import Person, actions
from openai import OpenAI
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def scrape_linkedin(linkedin_url, email, password):
# Set up Chrome options
chrome_options = Options()
chrome_options.add_argument("--headless") # Run in headless mode
chrome_options.add_argument("--disable-gpu") # Disable GPU acceleration
chrome_options.add_argument("--no-sandbox") # Bypass OS security model
chrome_options.add_argument("--disable-dev-shm-usage") # Overcome limited resource problems
chrome_options.add_argument("start-maximized") # Start maximized
chrome_options.add_argument("enable-automation") # Enable automation controls
chrome_options.add_argument("--disable-infobars") # Disable infobars
chrome_options.add_argument("--disable-extensions") # Disable extensions
chrome_options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")
# Initialize the Chrome driver with the options
service = Service('/usr/bin/chromedriver') # Update with the correct path to your chromedriver
driver = webdriver.Chrome(service=service, options=chrome_options)
client = OpenAI(api_key="OPENAI_API_KEY")
if email and password:
try:
# Log in to LinkedIn
actions.login(driver, email, password)
# Wait for the LinkedIn homepage to load or for login to complete
WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".global-nav__me-photo"))
)
# Navigate to the profile page
driver.get(linkedin_url)
# Wait for the profile page to load
WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".pv-top-card__photo-wrapper.ml0"))
)
# Create an Entity object for the LinkedIn profile
entity = Entity(linkedin_url=linkedin_url, driver=driver, scrape=False)
# Scrape the LinkedIn profile data
linkedin_data = entity.scrape(close_on_complete=True) # Close browser after scraping
prompt = """Extract and summarize the LinkedIn profile data into the following format:
linkedin_data = {
"name": person.name,
"linkedin_url": person.linkedin_url,
"about": person.about,
"experiences": [str(exp) for exp in person.experiences],
"educations": [str(edu) for edu in person.educations],
"interests": [str(interest) for interest in person.interests],
"accomplishments": [str(accomplishment) for accomplishment in person.accomplishments],
"company": person.company,
"job_title": person.job_title
}
"""
response = client.chat.completions.create(
model="gpt-4o-mini",
temperature=0.1,
messages=[
{"role": "system", "content": f'{prompt}'},
{"role": "user", "content": f'parse the following data: {linkedin_data}'}
]
)
print(response.choices[0].message.content)
return response.choices[0].message.content
except Exception as e:
print(f"Failed to scrape {linkedin_url}: {e}")
linkedin_data = {}
finally:
driver.quit()
else:
return {}
En nuestra función principal scrape_linkedin() primero estamos creando algunas configuraciones necesarias. Estos son chrome_options, services, driver y el cliente OpenAI. A continuación, ejecutamos la función login() con el controlador, el correo electrónico y la contraseña. A continuación, utilizamos el WebDriverWait() para comprobar si estamos en la página de inicio. Comprobamos la foto de perfil en la barra de navegación. Luego usamos la función driver.get() para ir al perfil que queremos scrapear.
Una vez que estemos en esa página, comprobaremos la foto de perfil utilizando WebDriverWait() de nuevo. Si la obtenemos, pasaremos a inicializar nuestro objeto Entidad y luego ejecutaremos la función scrape() para obtener todo el texto de la página web. Una vez que tenemos esos datos, podemos enviarlos a OpenAI utilizando la API de Chat Completions y hacer que extraiga los datos necesarios de ese gran trozo de texto. A continuación, podemos imprimir y devolver los datos.
Toda esta solución se inspiró en el siguiente repositorio de Github: Échale un vistazo.