Buscador de vídeos con OpenSearch y React | Parte 2 | Recolección de datos
Tags: tutorial, go, webscraping
Published: 2022-11-18 14:05:09

**⚠️ Nota:** La idea original de este proyecto surgió gracias al canal **Soumil Shah**, por lo que doy crédito y recomiendo ver su serie de vídeos sobre Elastic Search [Aquí](https://www.youtube.com/watch?v=obTK8dAaOkc).

El repositorio con el resultado final puede ser consultado en [Github](https://github.com/PChaparro/search-engine).

## Índice

1. Post 1 - Introducción: [Aquí](https://dev.to/pedrochaparro/buscador-de-videos-con-opensearch-y-react-parte-1-introduccion-4gfd)
2. Post 2 - Recolección de los datos: [Aquí](https://dev.to/pedrochaparro/buscador-de-videos-con-opensearch-y-react-parte-2-recoleccion-de-datos-5b6l)
3. Post 3 - Limpieza y almacenamiento de los datos: [Aquí](https://dev.to/pedrochaparro/buscador-de-videos-con-opensearch-y-react-parte-3-limpieza-y-almacenamiento-de-los-datos-5e21)
4. Post 4 - Desarrollo de la API (🚧 Trabajando en ello...) 
5. Post 5 - Desarrollo del cliente Web (🚧 Trabajando en ello...) 

## Requisitos: 

1. Go (1.19.2 Opcional) y conocimientos básicos de Go. 

## Creación de las carpetas y archivos

Para el desarrollo de las diferentes partes del proyecto, crearemos 3 carpetas principales, `frontend`, `backend` y `data`: 

```
.
├── frontned
├── backend
└── data
```

Ahora, dentro de la carpeta de data, crearemos la carpeta `scraping` y `cleansing`, para la obtención y limpieza de los datos respectivamente: 

```
data/
├── scraping
├── cleansing
```

Por ahora solo crearemos un nuevo módulo de Go para obtener los datos, esto dentro de la carpeta `scraping/`:

```bash
cd data/scraping
go mod init github.com/username/reponame
```

Si el anterior paso fue ejecutado correctamente, debería haber un archivo `go.mod` dentro de la carpeta `scraping/`:

```
data/
├── scraping
│   ├── go.mod
├── cleansing
```

## Obtención de los datos

En primer lugar, debemos descargar el paquete [youtubescraper](https://pkg.go.dev/github.com/PChaparro/go-youtube-scraper), para ello, estando dentro de la carpeta `scraping/` ejecutamos: 

```bash
go get github.com/PChaparro/go-youtube-scraper
```

Si no se mostró ningún error en la consola, dentro de la misma carpeta, creamos el archivo `main.go`:

```bash
touch main.go
```

Dentro del nuevo archivo creado, podemos copiar el siguiente código para comprobar que la instalación haya sido correcta y el paquete esté funcionando: 

```
package main

import (
	"fmt"

	// Importamos el paquete para hacer el scraping
	youtubescraper "github.com/PChaparro/go-youtube-scraper"
)

func main() {
	// Usamos el metodo GetVideosData con los siguientes argumentos
	// El primer argumento es una key de la api de youtube, en este caso no lo vamos a usar
	// El segundo argumento es el texto que queremos buscar en youtube
	// El tercer argumento es el numero de videos que queremos obtener (100)
	// El cuarto argumenteo es el limite de concurrencia para el funcionamiento del paquete
	// El ultimo argumento es si queremos usar la api de youtube en lugar de web scraping
	videos, err := youtubescraper.GetVideosData("", "Learn web development", 100, 64, false)

	if err != nil {
		fmt.Print("Error :(")
	}

	fmt.Println(len(videos.Videos))
}
```

El argumento del límite de concurrencia hace referencia al número máximo de `go routines` que el paquete podrá utilizar para obtener los datos de los vídeos. Lo anterior puede verse como **el número de vídeos que el paquete procesará al mismo tiempo**. [Más información](https://www.geeksforgeeks.org/difference-between-concurrency-and-parallelism/).

Si todo sale bien, podemos ejecutar el archivo `main.go` y al cabo de unos segundos (en mi caso 20 segundos aunque puede variar dependiendo de la conexión a internet) veremos un número en consola: 

```bash
go run main.go
... Luego de unos segundos
100
```

El número mostrado hace referencia al **número de datos obtenido por el paquete**, puede ser exactamente el mismo que el número de datos solicitado o puede ser un poco menor. 

Sabiendo que el paquete funcionó correctamente, es momento de obtener los más de 2000 datos para nuestro buscador, esta vez, utilizando una API key de Youtube para que el proceso sea mucho más rápido. 

Primero creamos un archivo `.env` en dentro de la carpeta `scraping/`:

```bash
touch .env
```

Dentro del archivo creado, colocaremos la variable de entorno `KEY` (aunque puede tener cualquier nombre) y su valor será nuestra _API key_ de Youtube. Si no tienes una _API key_, te recomiendo ver [este vídeo](https://youtu.be/zVJKcbjE52w):

```
KEY=your api key
```

Una vez creado el archivo `.env` con la variable `KEY` que almacena nuestra API Key, es momento de hacer algunos cambios en el código (Leer los comentarios): 

```
package main

import (
	// Paquete para convertir los videos a bytes para guardarlos como json
	"encoding/json"
	"fmt"

	// Paquete para crear el archivo .json final
	"os"
	// Paquete para medir el tiempo que demora la ejecucion
	"time"

	// Paquete para obtener los datos
	youtubescraper "github.com/PChaparro/go-youtube-scraper"
	// Interfaces / tipos del paquete para obtener los datos
	ysi "github.com/PChaparro/go-youtube-scraper/interfaces"
	// Paquete para leer la variable de entorno
	"github.com/joho/godotenv"
)

func main() {
	// Cargar las variables de entorno
	err := godotenv.Load()

	if err != nil {
		fmt.Println("Error al cargar las variables de entorno.")
	}

	// Leer y verificar la variable de entorno
	key := os.Getenv("KEY")

	if key == "" {
		fmt.Println("La variable de entorno está vacía")
	} else {
		// Búsquedas que queremos realizar
		queue := []string{
			"Learn web development",
		}

		// Array para guardar los vídeos
		results := []ysi.Video{}

		// Iterar cada una de las búsquedas definidas anteriormente
		for _, query := range queue {
			// Tomar el tiempo de inicio
			start := time.Now()

			// Obtener los vídeos de la búsqueda actual
			videos, _ := youtubescraper.GetVideosData(key, query, 100, 64, true)
			// Agregar los vídeos de la búsqueda actual al array de resultados
			results = append(results, videos.Videos...)

			// Imrprimir el tiempo que tomó
			fmt.Printf("%s query took %v\n", query, time.Since(start))
		}

		// Crear los bytes que serán guardasos en formato json
		json, err := json.Marshal(results)

		if err != nil {
			fmt.Printf("Error al convertir a bytes")
		} else {
			// Si no hay ningún error, guardamos el resultado como un .json
			// El 0666 final son permisos de lectura y escritura para todos los usuarios
			os.WriteFile("data.json", json, 0666)
		}

	}

}
```

Si no hay ningún error, luego de ejecutar el archivo veríamos un mensaje en la consola como el siguiente: 

```bash
go run main.go
... Luego de unos segundos
Learn web development query took 4.953656575s
```

El mensaje, como el caso anterior, nos indica que los datos se obtubieron de manera satisfactoria, y deberíamos poder encontrar un archivo `data.json` creado dentro de la carpeta `scraping/`: 

```
data/
├── scraping
│   ├── .env
│   ├── go.mod
│   ├── go.sum
│   ├── main.go
│   ├── data.json
├── cleansing
```

Dentro del archivo `data.json` podremos ver un array de objetos como el siguiente: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/as3erm4omwwp407af3lz.png)

Si lo anterior es cierto, felicidades 🎉! Haz completado el paso de recolección satisfactoriamente, el único paso restante es agregar más búsquedas al array `queue`, como en el siguiente ejemplo: 

```
queue := []string{
	"Learn web development",
	"Top new games", 
	"Best world carnivals",
        ...
}
```

Luego de agregar más elementos (**Ten en cuenta el límite de peticiones diarios de la API de Youtube, con 10 o 15 está más que bien**), puedes ejecutar de nuevo el archivo `main.go` para obtener los nuevos resultados.

## Final

Con esto se concluye esta segunda parte en la que recolectamos nuestros datos utilizando un paquete de _web scraping_, te invito a continuar con la siguiente en la que limpiaremos nuestros datos y los almacenaremos en Open Search.

## Referencias

Para consultar las referencias dirigirse a cada uno de los enlaces que aparencen dentro o al final de los diferentes párrafos.