{"id":240,"date":"2022-08-22T01:00:42","date_gmt":"2022-08-22T01:00:42","guid":{"rendered":"https:\/\/fersite.net\/?p=240"},"modified":"2022-10-12T01:37:21","modified_gmt":"2022-10-12T01:37:21","slug":"bautifulsoup-poderosa-libreria-para-hacer-webscrapping","status":"publish","type":"post","link":"https:\/\/fersite.net\/index.php\/2022\/08\/22\/bautifulsoup-poderosa-libreria-para-hacer-webscrapping\/","title":{"rendered":"BautifulSoup: Poderosa librer\u00eda para hacer webscrapping"},"content":{"rendered":"\n<p>Podemos decir que, a d\u00eda de hoy, Internet se a vuelto un enorme centro de informaci\u00f3n. En Internet tenemos acceso a enormes cantidad de informaci\u00f3n del tipo que podamos imaginar, lo cual hubiera sido inimaginable en el pasado. Es por lo anterior que el Internet supone una gran fuente de datos a la hora de querer realizar un an\u00e1lisis sobre alguna cosa o tema. Por ello se han desarrollado herramientas y bibliotecas que nos facilitan la tarea de la recolecci\u00f3n de datos desde internet y, en esta entrada, pasaremos a ver una de dichas bibliotecas que nos facilitan la tarea de la recolecci\u00f3n de datos desde internet.<\/p>\n\n\n\n<p>La herramienta a la que nos referimos que procederemos a ver en esta entrada, se trata de BeautifulSoup, una librer\u00eda, en lenguaje python, que sirve para la extracci\u00f3n de datos de archivos html y xml. Y bueno, para instalar dicha librer\u00eda es bien sencillo, ya que podemos instalarla desde pip:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash line-numbers\">$ pip install beautifulsoup4<\/code><\/pre>\n\n\n\n<p>Tambi\u00e9n requeriremos de la librer\u00eda request y para guardar datos podemos hacer uso de pandas, por lo cual vamos a instalarlas tambi\u00e9n:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash line-numbers\">$ pip install requests pandas<\/code><\/pre>\n\n\n\n<p>Ahora importamos nuestras librer\u00edas en un nuevo script de python:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">#Importamos las librer\u00edas necesarias\nimport pandas as pd\nimport requests\nfrom bs4 import BeautifulSoup as bso<\/code><\/pre>\n\n\n\n<p>Para trabajar, en esta ocasi\u00f3n, vamos a utilizar la pagina de cnn en espa\u00f1ol para extraer datos de ella, con ayuda de BeautifulSoup, para lo cual vemos que la url de resultados de t\u00e9rminos de b\u00fasqueda de la pagina pasa una frase o palabra a dicha url. Por lo anterior podemos buscar muchas cosas en la misma solamente cambiando el par\u00e1metro s en la url en cuesti\u00f3n. Es por lo anterior que utilizaremos el par\u00e1metro de b\u00fasqueda de ucrania, quedando la url como sigue:<\/p>\n\n\n\n<p><a href=\"https:\/\/cnnespanol.cnn.com\/?s=ucrania\">https:\/\/cnnespanol.cnn.com\/?s=ucrania<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"517\" src=\"https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-27-54-1024x517.png\" alt=\"\" class=\"wp-image-255\" srcset=\"https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-27-54-1024x517.png 1024w, https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-27-54-300x152.png 300w, https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-27-54-768x388.png 768w, https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-27-54.png 1223w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Ahora vamos a definir nuestra url en un string y realizamos una petici\u00f3n a la url para verificar que exista dicha url, y la respuesta la guardamos como un objeto de beautifulsoup, en caso de que no se haya podido encontrar la url salimos del programa:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">#Definimos nuestra URL con la que trabajaremos en un principio\nurl = 'https:\/\/cnnespanol.cnn.com\/?s=ucrania'\n\n#Hacemos petici\u00f3n a URL y creamos nuestra \"sopa\"\nprint('Tratando de realizar la petici\u00f3n a la URL...\\n')\ntry:\n    r = requests.get(url, verify=True)\n    soup = bso(r.text, 'lxml')\nexcept:\n    print('No se ha podido obtener la URL de la b\u00fasqueda o informaci\u00f3n de la misma.\\n')\n    exit()<\/code><\/pre>\n\n\n\n<p>Creamos un dataframe en pandas, con los nombres de las columnas a partir de una lista, donde vamos a vaciar la informaci\u00f3n que obtengamos de la pagina web:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">#Creamos un dataframe con solo los nombres de columnas\ncNames = ['T\u00edtulo', 'Autor', 'Fecha', 'Texto', 'URL']\n\ndf = pd.DataFrame(columns=cNames)<\/code><\/pre>\n\n\n\n<p>Para poder continuar necesitamos analizar un poco m\u00e1s a profundidad la pagina de cnn en espa\u00f1ol. Primeramente nos damos cuenta de que tiene una clase de enlaces llamada \u201cpage-numbers\u201d para enumerar las paginas que resultan de todos los art\u00edculos relacionados con el termino de b\u00fasqueda.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"464\" src=\"https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-29-49-1024x464.png\" alt=\"\" class=\"wp-image-253\" srcset=\"https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-29-49-1024x464.png 1024w, https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-29-49-300x136.png 300w, https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-29-49-768x348.png 768w, https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-29-49.png 1351w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Tambi\u00e9n podemos ver que existen enlaces con el termino \u201cvideo\u201d en ellas que no contienen mucha informaci\u00f3n y es m\u00e1s un v\u00eddeo, por lo cual no nos servir\u00e1n demasiado y tendremos que descartarlas.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-39-19-1024x506.png\" alt=\"\" class=\"wp-image-256\" srcset=\"https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-39-19-1024x506.png 1024w, https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-39-19-300x148.png 300w, https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-39-19-768x379.png 768w, https:\/\/fersite.net\/wp-content\/uploads\/2022\/10\/Captura-de-pantalla-2022-09-21-19-39-19.png 1351w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Estas paginas contienen el termino v\u00eddeo en la url, por lo que descartamos los enlaces que contengan dicho termino y no los analizaremos u obtendremos datos de ellos.<\/p>\n\n\n\n<p>Ahora podemos definir variables para hacer los loops por las paginas de nuestra pagina web a analizar y tratar de obtener el numero total de paginas de b\u00fasqueda en la pagina que vamos a scrapear; en caso de no poder obtener el numero de paginas salimos del programa:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">#Definimos algunas variables para hacer loops\ni = 0\nj = 0\n\nprint('Intentando obtener el n\u00famero total de p\u00e1ginas relacionadas a la b\u00fasqueda...\\n')\ntry:\n    tPages = int(soup.find_all(\"a\", class_=\"page-numbers\")[-2].get_text()) - 1\nexcept:\n    print('No se ha podido obtener el n\u00famero de p\u00e1ginas.\\n')\n    exit()<\/code><\/pre>\n\n\n\n<p>Iniciamos el primer bucle de nuestra aplicaci\u00f3n de scrapping y lo primero que vamos a hacer es revisar si ya se hizo el primer bucle, para en caso de ser as\u00ed, actualizar los datos de url, en caso de no poder actualizar los datos de la url salimos del programa:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">#Iniciamos el loop para las p\u00e1ginas\nwhile i &lt;= tPages:\n    \n    #Recargamos los datos de petici\u00f3n si el loop ya hizo su primer bucle\n    if i &gt; 0:\n        print('Intentando hacer petici\u00f3n a la URL de la siguiente p\u00e1gina...\\n')\n        try:\n            r = requests.get(url, verify=True)\n            soup = bso(r.text, 'lxml')\n        except:\n            print('No se ha podido completar la solicitud a la URL.\\n')\n            exit()<\/code><\/pre>\n\n\n\n<p>Comenzamos con el segundo bucle del programa, que se encuentra anidado dentro del primer bucle en esta ocasi\u00f3n. Lo primero que hacemos dentro de este segundo bucle es tratar de obtener la informaci\u00f3n de las url de los art\u00edculos de donde vamos a tratar de obtener los datos de la pagina web, ya que este bucle hace loop a trav\u00e9s de todos los art\u00edculos que se muestran en la pagina de resultados de la b\u00fasqueda:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\"> #Comenzamos bucle por los art\u00edculos de la p\u00e1gina\n    for j in soup.find_all('h2', class_='news__title'):\n        \n        #Hacemos petici\u00f3n a la p\u00e1gina del art\u00edculo\n        print('Intentando realizar petici\u00f3n a p\u00e1gina de art\u00edculo...\\n')\n        urld = j.find('a')['href']\n        \n        try:\n            r1 = requests.get(urld, verify=True)\n            soup1 = bso(r1.text, 'lxml')\n        except:\n            print('No se ha podido realizar la petici\u00f3n al art\u00edculo.\\n')\n            exit()<\/code><\/pre>\n\n\n\n<p>Si recordamos, ten\u00edamos que excluir las url que tuvieran el string \u201cvideo\u201d dentro de ellas, para lo cual utilizamos un if not, donde trataremos de obtener todos los datos que requiramos del articulo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">#Revisamos que el art\u00edculo no sea un v\u00eddeo\n        print('Revisando que no se trate de un art\u00edculo de v\u00eddeo...\\n')\n        if not \"video\" in urld:\n            \n            print('Extrayendo datos...\\n')\n            try:\n                #Obtenemos tanto el titulo del articulo como el autor de dicho articulo\n                title = soup1.find_all('h1', class_='storyfull__title')[0].get_text()\n                autor = soup1.find_all('p', class_='storyfull__authors')[0].find('a').get_text()\n            \n                #Obtenemos y damos formato a la fecha\n                fech = soup1.find_all('time', class_='storyfull__time')[0].get_text()\n                fech0 = fech.split(\") \", 1)[1]\n                fecha = fech0.replace(',','')<\/code><\/pre>\n\n\n\n<p>Dentro de este if not, definimos un tercer bucle para obtener el texto del articulo a trav\u00e9s de los tags de p\u00e1rrafo e irlos uniendo en un solo string:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">#Definimos variables para el loop para extraer el texto\n                k = 0\n                n = 0\n            \n                #Iniciamos con el loop para la extracci\u00f3n del texto\n                for k in soup1.find_all('p'):\n                \n                    if n == 0:\n                        text = k.get_text()\n                        n = 1\n                    else:\n                        text = text + '\\n' + k.get_text()<\/code><\/pre>\n\n\n\n<p>Por ultimo, dentro de ese mismo if not, creamos una nueva fila con los datos extra\u00eddos dentro de nuestro dataframe, y si no podemos obtener los datos en general salimos del programa:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">#A\u00f1adimos una nueva fila al dataframe con los datos extraidos de la pagina\n                df.loc[df.shape[0]] = [title, autor, fecha, text, urld]\n            \n            except:\n                print('No se ha podido obtener datos del articulo.\\n')\n                exit()<\/code><\/pre>\n\n\n\n<p>Con lo anterior terminamos el segundo bucle del programa, con lo cual solo nos resta terminar el primero. Para lo anterior solo tenemos que tratar de obtener la url de la siguiente pagina, y si no podemos, salir del programa, sin olvidarnos primero de escribir lo que tengamos en nuestro dataframe en un archivo de extensi\u00f3n xlsx:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">print('Intentando obtener URL de la siguiente p\u00e1gina...\\n')\n    #Actualizamos la url\n    try:\n        url = soup.find_all('a', class_=\"page-numbers\")[-1][\"href\"]\n    except:\n        print('No se ha podido obtener la nueva URL...\\n')\n        \n        print(\"Intentando escribir archivo de Excel con los datos que se pudieron obtener...\\n\")\n        if df.shape[0] &gt;= 1:\n            df.to_excel(\"cnn_ucrania.xlsx\")\n        \n        exit()\n    \n    i = i+1<\/code><\/pre>\n\n\n\n<p>Si completamos todos los bucles correctamente, o mejor dicho, si recorremos todas las paginas de resultado de la b\u00fasqueda, escribimos nuestro dataframe en un archivo xlsx y salimos del programa:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python line-numbers\">#Escribimos los datos a un archivo de excel \nprint(\"Intentando escribir archivo de excel con los datos...\\n\")   \ndf.to_excel(\"cnn_ucrania.xlsx\")<\/code><\/pre>\n\n\n\n<p>Despu\u00e9s de esto ya solo quedar\u00eda darle formato a nuestro archivo xlsx en cualquier editor de hoja de calculo o si se analizaran los datos con pandas, proceder al an\u00e1lisis.<\/p>\n\n\n\n<p>Para terminar la publicaci\u00f3n solo me resta compartir v\u00eddeo de la ejecuci\u00f3n del script y el resultado del mismo, as\u00ed como enlace al script completo en mi github:<\/p>\n\n\n\n<iframe loading=\"lazy\" width=\"847\" height=\"476\" src=\"https:\/\/www.youtube.com\/embed\/iumhgI8h67g\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe>\n\n\n\n<p>Enlace Github:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/Goshujinsama88\/cnn-beautifulsoup\">https:\/\/github.com\/Goshujinsama88\/cnn-beautifulsoup<\/a><\/p>\n\n\n\n<p><strong>Nota:<\/strong> La pagina de cnn en espa\u00f1ol tiene un error donde solo se cargan completamente bien las primeras 10 paginas del resultado de una b\u00fasqueda, por lo que a partir de la pagina 11, al no haber resultado de art\u00edculos, el script saldr\u00e1 de la ejecuci\u00f3n guardando lo que se contiene actualmente en el dataframe.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Podemos decir que, a d\u00eda de hoy, Internet se a vuelto un enorme centro de informaci\u00f3n. En Internet tenemos acceso a enormes cantidad de informaci\u00f3n del tipo que podamos imaginar, lo cual hubiera sido inimaginable en el pasado. Es por lo anterior que el Internet supone una gran fuente de datos a la hora de [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":281,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"_links":{"self":[{"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/posts\/240"}],"collection":[{"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/comments?post=240"}],"version-history":[{"count":33,"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/posts\/240\/revisions"}],"predecessor-version":[{"id":280,"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/posts\/240\/revisions\/280"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/media\/281"}],"wp:attachment":[{"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/media?parent=240"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/categories?post=240"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fersite.net\/index.php\/wp-json\/wp\/v2\/tags?post=240"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}