diff --git a/lab-web-scraping.ipynb b/lab-web-scraping.ipynb deleted file mode 100644 index e552783..0000000 --- a/lab-web-scraping.ipynb +++ /dev/null @@ -1,148 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "7e7a1ab8-2599-417d-9a65-25ef07f3a786", - "metadata": { - "id": "7e7a1ab8-2599-417d-9a65-25ef07f3a786" - }, - "source": [ - "# Lab | Web Scraping" - ] - }, - { - "cell_type": "markdown", - "id": "ce8882fc-4815-4567-92fa-b4816358ba7d", - "metadata": { - "id": "ce8882fc-4815-4567-92fa-b4816358ba7d" - }, - "source": [ - "Welcome to the \"Books to Scrape\" Web Scraping Adventure Lab!\n", - "\n", - "**Objective**\n", - "\n", - "In this lab, we will embark on a mission to unearth valuable insights from the data available on Books to Scrape, an online platform showcasing a wide variety of books. As data analyst, you have been tasked with scraping a specific subset of book data from Books to Scrape to assist publishing companies in understanding the landscape of highly-rated books across different genres. Your insights will help shape future book marketing strategies and publishing decisions.\n", - "\n", - "**Background**\n", - "\n", - "In a world where data has become the new currency, businesses are leveraging big data to make informed decisions that drive success and profitability. The publishing industry, much like others, utilizes data analytics to understand market trends, reader preferences, and the performance of books based on factors such as genre, author, and ratings. Books to Scrape serves as a rich source of such data, offering detailed information about a diverse range of books, making it an ideal platform for extracting insights to aid in informed decision-making within the literary world.\n", - "\n", - "**Task**\n", - "\n", - "Your task is to create a Python script using BeautifulSoup and pandas to scrape Books to Scrape book data, focusing on book ratings and genres. The script should be able to filter books with ratings above a certain threshold and in specific genres. Additionally, the script should structure the scraped data in a tabular format using pandas for further analysis.\n", - "\n", - "**Expected Outcome**\n", - "\n", - "A function named `scrape_books` that takes two parameters: `min_rating` and `max_price`. The function should scrape book data from the \"Books to Scrape\" website and return a `pandas` DataFrame with the following columns:\n", - "\n", - "**Expected Outcome**\n", - "\n", - "- A function named `scrape_books` that takes two parameters: `min_rating` and `max_price`.\n", - "- The function should return a DataFrame with the following columns:\n", - " - **UPC**: The Universal Product Code (UPC) of the book.\n", - " - **Title**: The title of the book.\n", - " - **Price (£)**: The price of the book in pounds.\n", - " - **Rating**: The rating of the book (1-5 stars).\n", - " - **Genre**: The genre of the book.\n", - " - **Availability**: Whether the book is in stock or not.\n", - " - **Description**: A brief description or product description of the book (if available).\n", - " \n", - "You will execute this script to scrape data for books with a minimum rating of `4.0 and above` and a maximum price of `£20`. \n", - "\n", - "Remember to experiment with different ratings and prices to ensure your code is versatile and can handle various searches effectively!\n", - "\n", - "**Resources**\n", - "\n", - "- [Beautiful Soup Documentation](https://www.crummy.com/software/BeautifulSoup/bs4/doc/)\n", - "- [Pandas Documentation](https://pandas.pydata.org/pandas-docs/stable/index.html)\n", - "- [Books to Scrape](https://books.toscrape.com/)\n" - ] - }, - { - "cell_type": "markdown", - "id": "3519921d-5890-445b-9a33-934ed8ee378c", - "metadata": { - "id": "3519921d-5890-445b-9a33-934ed8ee378c" - }, - "source": [ - "**Hint**\n", - "\n", - "Your first mission is to familiarize yourself with the **Books to Scrape** website. Navigate to [Books to Scrape](http://books.toscrape.com/) and explore the available books to understand their layout and structure. \n", - "\n", - "Next, think about how you can set parameters for your data extraction:\n", - "\n", - "- **Minimum Rating**: Focus on books with a rating of 4.0 and above.\n", - "- **Maximum Price**: Filter for books priced up to £20.\n", - "\n", - "After reviewing the site, you can construct a plan for scraping relevant data. Pay attention to the details displayed for each book, including the title, price, rating, and availability. This will help you identify the correct HTML elements to target with your scraping script.\n", - "\n", - "Make sure to build your scraping URL and logic based on the patterns you observe in the HTML structure of the book listings!" - ] - }, - { - "cell_type": "markdown", - "id": "25a83a0d-a742-49f6-985e-e27887cbf922", - "metadata": { - "id": "25a83a0d-a742-49f6-985e-e27887cbf922" - }, - "source": [ - "\n", - "---\n", - "\n", - "**Best of luck! Immerse yourself in the world of books, and may the data be with you!**" - ] - }, - { - "cell_type": "markdown", - "id": "7b75cf0d-9afa-4eec-a9e2-befeac68b2a0", - "metadata": { - "id": "7b75cf0d-9afa-4eec-a9e2-befeac68b2a0" - }, - "source": [ - "**Important Note**:\n", - "\n", - "In the fast-changing online world, websites often update and change their structures. When you try this lab, the **Books to Scrape** website might differ from what you expect.\n", - "\n", - "If you encounter issues due to these changes, like new rules or obstacles preventing data extraction, don’t worry! Get creative.\n", - "\n", - "You can choose another website that interests you and is suitable for scraping data. Options like Wikipedia, The New York Times, or even library databases are great alternatives. The main goal remains the same: extract useful data and enhance your web scraping skills while exploring a source of information you enjoy. This is your opportunity to practice and adapt to different web environments!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40359eee-9cd7-4884-bfa4-83344c222305", - "metadata": { - "id": "40359eee-9cd7-4884-bfa4-83344c222305" - }, - "outputs": [], - "source": [ - "# Your solution goes here" - ] - } - ], - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/lab-web-scrapings.ipynb b/lab-web-scrapings.ipynb new file mode 100644 index 0000000..e168441 --- /dev/null +++ b/lab-web-scrapings.ipynb @@ -0,0 +1,905 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "intro", + "metadata": {}, + "source": [ + "# Lab: Web Scraping - Books to Scrape\n" + ] + }, + { + "cell_type": "markdown", + "id": "step1", + "metadata": {}, + "source": [ + "## Paso 1 - Importar librerías" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "imports", + "metadata": {}, + "outputs": [], + "source": [ + "# Instalamos las librerías necesarias si no están en el entorno\n", + "#!pip install requests beautifulsoup4\n", + "\n", + "# Librería para hacer peticiones HTTP\n", + "import requests\n", + "\n", + "# Librería para parsear (leer y navegar) el HTML de la página\n", + "from bs4 import BeautifulSoup\n", + "\n", + "# Librería para organizar los datos en un DataFrame\n", + "import pandas as pd" + ] + }, + { + "cell_type": "markdown", + "id": "step2", + "metadata": {}, + "source": [ + "## Paso 2 - Explorar la estructura HTML del sitio\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "explore", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Código de respuesta: 200\n", + "Libros encontrados en la primera página: 20\n" + ] + } + ], + "source": [ + "# Hacemos una petición de prueba a la primera página del catálogo\n", + "url_test = \"https://books.toscrape.com/catalogue/page-1.html\"\n", + "response_test = requests.get(url_test)\n", + "\n", + "print(f\"Código de respuesta: {response_test.status_code}\")\n", + "\n", + "# Parseamos el HTML\n", + "soup_test = BeautifulSoup(response_test.content, \"html.parser\")\n", + "\n", + "# Encontramos todos los libros de la página\n", + "books_test = soup_test.find_all('article', class_='product_pod')\n", + "print(f\"Libros encontrados en la primera página: {len(books_test)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "explore_one", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Título: A Light in the Attic\n", + "Precio: 51.77\n", + "Rating: 3\n", + "Link relativo: a-light-in-the-attic_1000/index.html\n" + ] + } + ], + "source": [ + "# Exploramos el primer libro del listado para verificar la extracción de datos básicos\n", + "first_book = books_test[0]\n", + "\n", + "# Título\n", + "title = first_book.find('h3').find('a')['title']\n", + "\n", + "# Precio: eliminamos el símbolo '£' y convertimos a float\n", + "price_text = first_book.find('p', class_='price_color').text\n", + "price = float(price_text.replace('£', '').replace('Â', '').strip())\n", + "\n", + "# Calificación: convertimos de palabra a número con un diccionario\n", + "rating_map = {'One': 1, 'Two': 2, 'Three': 3, 'Four': 4, 'Five': 5}\n", + "rating_word = first_book.find('p', class_='star-rating')['class'][1]\n", + "rating = rating_map[rating_word]\n", + "\n", + "# Link relativo a la página de detalle del libro\n", + "relative_link = first_book.find('h3').find('a')['href']\n", + "\n", + "print(f\"Título: {title}\")\n", + "print(f\"Precio: {price}\")\n", + "print(f\"Rating: {rating}\")\n", + "print(f\"Link relativo: {relative_link}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "explore_detail", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "URL de detalle: https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html\n", + "UPC: a897fe39b1053632\n", + "Availability: In stock (22 available)\n", + "Genre: Poetry\n", + "Description: It's hard to imagine a world without A Light in the Attic. This now-classic collection of poetry and...\n" + ] + } + ], + "source": [ + "# Exploramos la página de detalle del primer libro para ver los campos adicionales\n", + "\n", + "# Construimos la URL completa del detalle\n", + "# Los links del listado son relativos (ej: '../../../libro/index.html')\n", + "# La URL base del catálogo es: https://books.toscrape.com/catalogue/\n", + "base_url = \"https://books.toscrape.com/catalogue/\"\n", + "detail_url = base_url + relative_link.replace('../', '')\n", + "\n", + "print(f\"URL de detalle: {detail_url}\")\n", + "\n", + "# Hacemos la petición a la página de detalle\n", + "detail_response = requests.get(detail_url)\n", + "detail_soup = BeautifulSoup(detail_response.content, \"html.parser\")\n", + "\n", + "# Extraemos la tabla con UPC y Availability\n", + "table = detail_soup.find('table', class_='table table-striped')\n", + "rows = table.find_all('tr')\n", + "\n", + "# UPC: primera fila de la tabla\n", + "upc = rows[0].find('td').text\n", + "\n", + "# Availability: sexta fila de la tabla (índice 5)\n", + "availability = rows[5].find('td').text.strip()\n", + "\n", + "# Género: tercer elemento del breadcrumb (Home > Categoría > Título)\n", + "breadcrumb = detail_soup.find('ul', class_='breadcrumb')\n", + "genre = breadcrumb.find_all('li')[2].text.strip()\n", + "\n", + "# Descripción: del tag meta 'description'\n", + "desc_tag = detail_soup.find('meta', attrs={'name': 'description'})\n", + "description = desc_tag['content'].strip() if desc_tag else 'No description available'\n", + "\n", + "print(f\"UPC: {upc}\")\n", + "print(f\"Availability: {availability}\")\n", + "print(f\"Genre: {genre}\")\n", + "print(f\"Description: {description[:100]}...\")" + ] + }, + { + "cell_type": "markdown", + "id": "step3", + "metadata": {}, + "source": [ + "## Paso 3 - Funciones auxiliares\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "helper_detail", + "metadata": {}, + "outputs": [], + "source": [ + "def get_book_details(detail_url):\n", + " \n", + " \n", + " # Hacemos la petición a la página de detalle del libro\n", + " response = requests.get(detail_url)\n", + " soup = BeautifulSoup(response.content, \"html.parser\")\n", + " \n", + " # Extraemos la tabla con información técnica del libro\n", + " table = soup.find('table', class_='table table-striped')\n", + " rows = table.find_all('tr')\n", + " \n", + " # UPC está en la primera fila de la tabla\n", + " upc = rows[0].find('td').text\n", + " \n", + " # Availability está en la sexta fila (índice 5)\n", + " availability = rows[5].find('td').text.strip()\n", + " \n", + " # Género: tercer elemento del breadcrumb (Home > Género > Título)\n", + " breadcrumb = soup.find('ul', class_='breadcrumb')\n", + " genre = breadcrumb.find_all('li')[2].text.strip()\n", + " \n", + " # Descripción desde el meta tag\n", + " desc_tag = soup.find('meta', attrs={'name': 'description'})\n", + " description = desc_tag['content'].strip() if desc_tag else 'No description available'\n", + " \n", + " return {\n", + " 'UPC': upc,\n", + " 'Genre': genre,\n", + " 'Availability': availability,\n", + " 'Description': description\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "helper_page", + "metadata": {}, + "outputs": [], + "source": [ + "def scrape_page(page_url):\n", + " \n", + " \n", + " # Diccionario para convertir la calificación de texto a número\n", + " rating_map = {'One': 1, 'Two': 2, 'Three': 3, 'Four': 4, 'Five': 5}\n", + " \n", + " # Hacemos la petición HTTP a la página del catálogo\n", + " response = requests.get(page_url)\n", + " soup = BeautifulSoup(response.content, \"html.parser\")\n", + " \n", + " # Encontramos todos los artículos (libros) de la página\n", + " books = soup.find_all('article', class_='product_pod')\n", + " \n", + " # Lista donde guardaremos los datos básicos de cada libro\n", + " books_data = []\n", + " \n", + " for book in books:\n", + " \n", + " # Título desde el atributo 'title' del link\n", + " title = book.find('h3').find('a')['title']\n", + " \n", + " # Precio: limpiamos el símbolo y convertimos a float\n", + " price_text = book.find('p', class_='price_color').text\n", + " price = float(price_text.replace('£', '').replace('Â', '').strip())\n", + " \n", + " # Calificación: convertimos de texto a número\n", + " rating_word = book.find('p', class_='star-rating')['class'][1]\n", + " rating = rating_map[rating_word]\n", + " \n", + " # Link relativo a la página de detalle\n", + " relative_link = book.find('h3').find('a')['href']\n", + " \n", + " # Construimos la URL completa del detalle\n", + " detail_url = \"https://books.toscrape.com/catalogue/\" + relative_link.replace('../', '')\n", + " \n", + " books_data.append({\n", + " 'Title': title,\n", + " 'Price (£)': price,\n", + " 'Rating': rating,\n", + " 'detail_url': detail_url\n", + " })\n", + " \n", + " return books_data" + ] + }, + { + "cell_type": "markdown", + "id": "step4", + "metadata": {}, + "source": [ + "## Paso 4 - Función principal `scrape_books(min_rating, max_price)`\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "main_function", + "metadata": {}, + "outputs": [], + "source": [ + "def scrape_books(min_rating, max_price):\n", + " \n", + " \n", + " # Lista donde acumulamos los libros que pasan el filtro\n", + " filtered_books = []\n", + " \n", + " # El sitio tiene 50 páginas en el catálogo\n", + " total_pages = 50\n", + " \n", + " print(f\"Iniciando scraping con filtros: rating >= {min_rating}, precio <= {max_price}£\")\n", + " print(\"-\" * 60)\n", + " \n", + " # --- FASE 1: Recorrer el catálogo y filtrar libros candidatos ---\n", + " for page_num in range(1, total_pages + 1):\n", + " \n", + " # Construimos la URL de cada página del catálogo\n", + " page_url = f\"https://books.toscrape.com/catalogue/page-{page_num}.html\"\n", + " \n", + " # Extraemos los datos básicos de los libros de esta página\n", + " page_books = scrape_page(page_url)\n", + " \n", + " # Filtramos los libros que cumplen los criterios de rating y precio\n", + " for book in page_books:\n", + " if book['Rating'] >= min_rating and book['Price (£)'] <= max_price:\n", + " filtered_books.append(book)\n", + " \n", + " # Mostramos progreso cada 10 páginas\n", + " if page_num % 10 == 0:\n", + " print(f\"Páginas procesadas: {page_num}/{total_pages} | Libros filtrados hasta ahora: {len(filtered_books)}\")\n", + " \n", + " print(f\"\\nFase 1 completada. Libros que cumplen los criterios: {len(filtered_books)}\")\n", + " print(\"\\nFase 2: Obteniendo detalles de cada libro filtrado...\")\n", + " print(\"-\" * 60)\n", + " \n", + " # --- FASE 2: Visitar la página de detalle de cada libro filtrado ---\n", + " all_books_data = []\n", + " \n", + " for i, book in enumerate(filtered_books):\n", + " \n", + " # Visitamos la página de detalle para obtener UPC, Genre, Availability, Description\n", + " details = get_book_details(book['detail_url'])\n", + " \n", + " # Combinamos los datos básicos con los detalles en el orden de columnas esperado\n", + " all_books_data.append({\n", + " 'UPC': details['UPC'],\n", + " 'Title': book['Title'],\n", + " 'Price (£)': book['Price (£)'],\n", + " 'Rating': book['Rating'],\n", + " 'Genre': details['Genre'],\n", + " 'Availability': details['Availability'],\n", + " 'Description': details['Description']\n", + " })\n", + " \n", + " # Mostramos progreso cada 20 libros\n", + " if (i + 1) % 20 == 0:\n", + " print(f\"Detalles obtenidos: {i + 1}/{len(filtered_books)}\")\n", + " \n", + " # Creamos el DataFrame final a partir de la lista de diccionarios\n", + " df = pd.DataFrame(all_books_data)\n", + " \n", + " print(f\"\\nScraping completado. Total de libros en el DataFrame: {len(df)}\")\n", + " \n", + " return df" + ] + }, + { + "cell_type": "markdown", + "id": "step5", + "metadata": {}, + "source": [ + "## Paso 5 - Ejecutar la función y explorar resultados" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "run_scraper", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iniciando scraping con filtros: rating >= 3, precio <= 20£\n", + "------------------------------------------------------------\n", + "Páginas procesadas: 10/50 | Libros filtrados hasta ahora: 22\n", + "Páginas procesadas: 20/50 | Libros filtrados hasta ahora: 50\n", + "Páginas procesadas: 30/50 | Libros filtrados hasta ahora: 74\n", + "Páginas procesadas: 40/50 | Libros filtrados hasta ahora: 96\n", + "Páginas procesadas: 50/50 | Libros filtrados hasta ahora: 116\n", + "\n", + "Fase 1 completada. Libros que cumplen los criterios: 116\n", + "\n", + "Fase 2: Obteniendo detalles de cada libro filtrado...\n", + "------------------------------------------------------------\n", + "Detalles obtenidos: 20/116\n", + "Detalles obtenidos: 40/116\n", + "Detalles obtenidos: 60/116\n", + "Detalles obtenidos: 80/116\n", + "Detalles obtenidos: 100/116\n", + "\n", + "Scraping completado. Total de libros en el DataFrame: 116\n" + ] + } + ], + "source": [ + "# Ejecutamos la función con los parámetros de filtrado\n", + "# Calificación mínima: 3 estrellas | Precio máximo: 20£\n", + "df_books = scrape_books(min_rating=3, max_price=20)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "explore_results", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
UPCTitlePrice (£)RatingGenreAvailabilityDescription
0e72a5dfc7e9267b2The Coming Woman: A Novel Based on the Life of...17.933DefaultIn stock (19 available)\"If you have a heart, if you have a soul, Kare...
1ce6396b0f23f6eccSet Me Free17.465Young AdultIn stock (19 available)Aaron Ledbetter’s future had been planned out ...
26258a1f6a6dcfe50The Four Agreements: A Practical Guide to Pers...17.665SpiritualityIn stock (18 available)In The Four Agreements, don Miguel Ruiz reveal...
36be3beb0793a53e7Sophie's World15.945PhilosophyIn stock (18 available)A page-turning novel that is also an explorati...
4657fe5ead67a7767Untitled Collection: Sabbath Poems 201414.274PoetryIn stock (16 available)More than thirty-five years ago, when the weat...
57ae099f3898e0209Unicorn Tracks18.783FantasyIn stock (16 available)After a savage attack drives her from her home...
651653ef291ab7ddcThis One Summer19.494Sequential ArtIn stock (16 available)Every summer, Rose goes with her mom and dad t...
7709822d0b5bcb7f4Thirst17.275FictionIn stock (16 available)On a searing summer Friday, Eddie Chapman has ...
80da9aa9d24677fc0The Life-Changing Magic of Tidying Up: The Jap...16.773NonfictionIn stock (16 available)Despite constant efforts to declutter your hom...
90fa6dceead7ce47aPrincess Jellyfish 2-in-1 Omnibus, Vol. 01 (Pr...13.615Sequential ArtIn stock (16 available)THE LONG-AWAITED STORY OF FANGIRLS TAKING ON T...
\n", + "
" + ], + "text/plain": [ + " UPC Title \\\n", + "0 e72a5dfc7e9267b2 The Coming Woman: A Novel Based on the Life of... \n", + "1 ce6396b0f23f6ecc Set Me Free \n", + "2 6258a1f6a6dcfe50 The Four Agreements: A Practical Guide to Pers... \n", + "3 6be3beb0793a53e7 Sophie's World \n", + "4 657fe5ead67a7767 Untitled Collection: Sabbath Poems 2014 \n", + "5 7ae099f3898e0209 Unicorn Tracks \n", + "6 51653ef291ab7ddc This One Summer \n", + "7 709822d0b5bcb7f4 Thirst \n", + "8 0da9aa9d24677fc0 The Life-Changing Magic of Tidying Up: The Jap... \n", + "9 0fa6dceead7ce47a Princess Jellyfish 2-in-1 Omnibus, Vol. 01 (Pr... \n", + "\n", + " Price (£) Rating Genre Availability \\\n", + "0 17.93 3 Default In stock (19 available) \n", + "1 17.46 5 Young Adult In stock (19 available) \n", + "2 17.66 5 Spirituality In stock (18 available) \n", + "3 15.94 5 Philosophy In stock (18 available) \n", + "4 14.27 4 Poetry In stock (16 available) \n", + "5 18.78 3 Fantasy In stock (16 available) \n", + "6 19.49 4 Sequential Art In stock (16 available) \n", + "7 17.27 5 Fiction In stock (16 available) \n", + "8 16.77 3 Nonfiction In stock (16 available) \n", + "9 13.61 5 Sequential Art In stock (16 available) \n", + "\n", + " Description \n", + "0 \"If you have a heart, if you have a soul, Kare... \n", + "1 Aaron Ledbetter’s future had been planned out ... \n", + "2 In The Four Agreements, don Miguel Ruiz reveal... \n", + "3 A page-turning novel that is also an explorati... \n", + "4 More than thirty-five years ago, when the weat... \n", + "5 After a savage attack drives her from her home... \n", + "6 Every summer, Rose goes with her mom and dad t... \n", + "7 On a searing summer Friday, Eddie Chapman has ... \n", + "8 Despite constant efforts to declutter your hom... \n", + "9 THE LONG-AWAITED STORY OF FANGIRLS TAKING ON T... " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Revisamos las primeras filas del DataFrame resultante\n", + "df_books.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "check_structure", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dimensiones: (116, 7)\n", + "\n", + "Columnas: ['UPC', 'Title', 'Price (£)', 'Rating', 'Genre', 'Availability', 'Description']\n", + "\n", + "Tipos de datos:\n", + "UPC object\n", + "Title object\n", + "Price (£) float64\n", + "Rating int64\n", + "Genre object\n", + "Availability object\n", + "Description object\n", + "dtype: object\n", + "\n", + "Valores nulos:\n", + "UPC 0\n", + "Title 0\n", + "Price (£) 0\n", + "Rating 0\n", + "Genre 0\n", + "Availability 0\n", + "Description 0\n", + "dtype: int64\n" + ] + } + ], + "source": [ + "# Verificamos la estructura del DataFrame\n", + "print(f\"Dimensiones: {df_books.shape}\")\n", + "print(f\"\\nColumnas: {list(df_books.columns)}\")\n", + "print(f\"\\nTipos de datos:\")\n", + "print(df_books.dtypes)\n", + "print(f\"\\nValores nulos:\")\n", + "print(df_books.isnull().sum())" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "summary_stats", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Distribución de calificaciones:\n", + "Rating\n", + "3 41\n", + "4 33\n", + "5 42\n", + "Name: count, dtype: int64\n", + "\n", + "Rango de precios: 10.00£ - 19.69£\n", + "\n", + "Top 5 géneros más frecuentes:\n", + "Genre\n", + "Default 16\n", + "Sequential Art 13\n", + "Nonfiction 12\n", + "Fiction 9\n", + "Young Adult 7\n", + "Name: count, dtype: int64\n" + ] + } + ], + "source": [ + "# Estadísticas básicas del DataFrame final\n", + "print(\"Distribución de calificaciones:\")\n", + "print(df_books['Rating'].value_counts().sort_index())\n", + "\n", + "print(f\"\\nRango de precios: {df_books['Price (£)'].min():.2f}£ - {df_books['Price (£)'].max():.2f}£\")\n", + "\n", + "print(f\"\\nTop 5 géneros más frecuentes:\")\n", + "print(df_books['Genre'].value_counts().head())" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "final_df", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
UPCTitlePrice (£)RatingGenreAvailabilityDescription
0e72a5dfc7e9267b2The Coming Woman: A Novel Based on the Life of...17.933DefaultIn stock (19 available)\"If you have a heart, if you have a soul, Kare...
1ce6396b0f23f6eccSet Me Free17.465Young AdultIn stock (19 available)Aaron Ledbetter’s future had been planned out ...
26258a1f6a6dcfe50The Four Agreements: A Practical Guide to Pers...17.665SpiritualityIn stock (18 available)In The Four Agreements, don Miguel Ruiz reveal...
36be3beb0793a53e7Sophie's World15.945PhilosophyIn stock (18 available)A page-turning novel that is also an explorati...
4657fe5ead67a7767Untitled Collection: Sabbath Poems 201414.274PoetryIn stock (16 available)More than thirty-five years ago, when the weat...
........................
11129fc016c459aeb14The Edge of Reason (Bridget Jones #2)19.184Womens FictionIn stock (1 available)Monday 27 January“7:15 a.m. Hurrah! The wilder...
1123301af038a720587The Complete Maus (Maus #1-2)10.643Sequential ArtIn stock (1 available)Combined for the first time here are Maus I: A...
11318b4545a5ed15581The Communist Manifesto14.763Add a commentIn stock (1 available)A rousing call to arms whose influence is stil...
1143a9a7c5895e82646Sister Sable (The Mad Queen #1)13.333FantasyIn stock (1 available)THE FIRST TENET OF THE WIND: Do not get caught...
11519fec36a1dfb4c16A Spy's Devotion (The Regency Spies of London #1)16.975Historical FictionIn stock (1 available)In England’s Regency era, manners and elegance...
\n", + "

116 rows × 7 columns

\n", + "
" + ], + "text/plain": [ + " UPC Title \\\n", + "0 e72a5dfc7e9267b2 The Coming Woman: A Novel Based on the Life of... \n", + "1 ce6396b0f23f6ecc Set Me Free \n", + "2 6258a1f6a6dcfe50 The Four Agreements: A Practical Guide to Pers... \n", + "3 6be3beb0793a53e7 Sophie's World \n", + "4 657fe5ead67a7767 Untitled Collection: Sabbath Poems 2014 \n", + ".. ... ... \n", + "111 29fc016c459aeb14 The Edge of Reason (Bridget Jones #2) \n", + "112 3301af038a720587 The Complete Maus (Maus #1-2) \n", + "113 18b4545a5ed15581 The Communist Manifesto \n", + "114 3a9a7c5895e82646 Sister Sable (The Mad Queen #1) \n", + "115 19fec36a1dfb4c16 A Spy's Devotion (The Regency Spies of London #1) \n", + "\n", + " Price (£) Rating Genre Availability \\\n", + "0 17.93 3 Default In stock (19 available) \n", + "1 17.46 5 Young Adult In stock (19 available) \n", + "2 17.66 5 Spirituality In stock (18 available) \n", + "3 15.94 5 Philosophy In stock (18 available) \n", + "4 14.27 4 Poetry In stock (16 available) \n", + ".. ... ... ... ... \n", + "111 19.18 4 Womens Fiction In stock (1 available) \n", + "112 10.64 3 Sequential Art In stock (1 available) \n", + "113 14.76 3 Add a comment In stock (1 available) \n", + "114 13.33 3 Fantasy In stock (1 available) \n", + "115 16.97 5 Historical Fiction In stock (1 available) \n", + "\n", + " Description \n", + "0 \"If you have a heart, if you have a soul, Kare... \n", + "1 Aaron Ledbetter’s future had been planned out ... \n", + "2 In The Four Agreements, don Miguel Ruiz reveal... \n", + "3 A page-turning novel that is also an explorati... \n", + "4 More than thirty-five years ago, when the weat... \n", + ".. ... \n", + "111 Monday 27 January“7:15 a.m. Hurrah! The wilder... \n", + "112 Combined for the first time here are Maus I: A... \n", + "113 A rousing call to arms whose influence is stil... \n", + "114 THE FIRST TENET OF THE WIND: Do not get caught... \n", + "115 In England’s Regency era, manners and elegance... \n", + "\n", + "[116 rows x 7 columns]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# DataFrame final completo\n", + "df_books" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv310 (3.10.20.final.0)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.20" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}