D: Web Scraping

Para introducir este tema haremos uso de una presentación (PDF).

image1

Scraping Básico

Descargando páginas web

El primer paso antes de hacer cualquier tipo de scraping es decidir que página queremos descargar y analizar. En nuestro caso vamos a obtener datos de la página del Instituto Carlos III sobre la evolución del COVID-19:

https://covid19.isciii.es/ - La reemplazamos por https://zerjio.com/temp/covid19.html

Lo primero que deberíamos hacer sería visualizar la página en nuestro navegador favorito (¿Firefox?) para comprobar el aspecto que tiene y la información que podemos descargar.

A continuación tendremos que emular el comportamiento del navegador para obtener la página: tendremos que hacer una petición HTTP para recuperar la página web. Lo haremos usando la biblioteca «requests».

Información y ejemplos sobre dicho módulo: https://www.w3schools.com/python/module_requests.asp

[1]:
import requests
from pprint import pprint

#respuesta = requests.get("https://covid19.isciii.es/")  # Utilizamo el metodo GET del protocolor HTTP
respuesta = requests.get("https://zerjio.com/temp/covid19.html")
[2]:
# Hemos obtenido un objeto de tipo "Response", donde estará toda la información que hemos obtenido de la página
# https://www.w3schools.com/python/ref_requests_response.asp

pprint(vars(respuesta))  # Podemos inspeccionar el objeto y ver que campos nos interesan. Particularmente:

{'_content': b' <html xmlns="http://www.w3.org/1999/xhtml">\n   <head>\n   \n '
             b'   <meta charset="UTF-8">\n\n    <script>\n    function cambiaD'
             b'atosConJavascript() {\n      table = document.getElementById('
             b'"tablaDatos");\n      \n      for (var i = 0, row; row = table'
             b'.rows[i]; i++) {\n        if (i > 0) {\n          for (var j ='
             b' 0, col; col = row.cells[j]; j++) {\n            if (j > 0) {'
             b'\n              col.innerHTML = Math.floor(Math.random() * 10'
             b'00);\n            }\n          }  \n        }\n      }\n    }'
             b'\n    </script>\n    <title>P\xc3\xa1gina del COVID 19 para solv'
             b'entar problema en el curso de Python, m\xc3\xb3dulo de web-scr'
             b'apping</title>\n   </head>\n   \n   <body onload="cambiaDatosCo'
             b'nJavascript()">\n     <h2>Esta p\xc3\xa1gina "simula" una p'
             b'\xc3\xa1gina que hab\xc3\xada en el direcci\xc3\xb3n https://c'
             b'ovid19.isciii.es/ . Aunque no se parece est\xc3\xa9ticamente t'
             b'iene los mismos elementos para que los ejemplos funcionen igual.'
             b'</h2>\n     \n     <p>\xc3\x9altima actualizaci\xc3\xb3n <span'
             b' id="fecha">10 de Marzo de 2020</span> a las\n     <span id="'
             b'hora">10:10</span></p>\n     \n     \n     <table style="border'
             b': 1px solid #000000;">\n      <tbody>\n        <tr><td>Dato 1<'
             b'/td><td>33</td></tr>\n        <tr><td>Dato 2</td><td>666</td>'
             b'</tr>\n        <tr><td>Dato 3</td><td>7777</td></tr>\n        '
             b'<tr><td>Dato 4</td><td>99</td></tr>\n      \n      </tbody>\n  '
             b'  </table>\n        \n    <p>La que viene a continuaci\xc3\xb3n '
             b'es la tabla de datos de verdad.</p>\n    \n    \n     <table st'
             b'yle="border: 1px solid #000000;" id="tablaDatos">\n       <th'
             b'ead>\n         <tr>\n           <th>Comunidad</th>\n           '
             b'<th>Contagiados</th>\n           <th>Fallecidos</th>\n        '
             b'   <th>Curados</th>\n        </tr>\n      </thead>\n      \n    '
             b'  <tbody>\n         <tr>\n           <td>Andaluc\xc3\xada</t'
             b'd>\n           <td>80</td>\n           <td></td>\n           <t'
             b'd></td>\n        </tr>\n        \n         <tr>\n           <td>'
             b'Arag\xc3\xb3n</td>\n           <td>50</td>\n           <td></td>'
             b'\n           <td></td>\n        </tr>\n        \n         <t'
             b'r>\n           <td>Principado de Asturias</td>\n           <td'
             b'>30</td>\n           <td></td>\n           <td></td>\n        <'
             b'/tr>\n        \n         <tr>\n           <td>Islas Baleares</t'
             b'd>\n           <td>10</td>\n           <td></td>\n           <t'
             b'd></td>\n        </tr>\n        \n         <tr>\n           <td>'
             b'Canarias</td>\n           <td>35</td>\n           <td></td>\n  '
             b'         <td></td>\n        </tr>\n        \n         <tr>\n    '
             b'       <td>Cantabria</td>\n           <td>12</td>\n           '
             b'<td></td>\n           <td></td>\n        </tr>\n        \n      '
             b'   <tr>\n           <td>Castilla y Le\xc3\xb3n</td>\n           '
             b'<td>60</td>\n           <td></td>\n           <td></td>\n      '
             b'  </tr>\n        \n         <tr>\n           <td>Catalu'
             b'\xc3\xb1a</td>\n           <td>110</td>\n           <td></td'
             b'>\n           <td></td>\n        </tr>\n        \n         <'
             b'tr>\n           <td>Galicia</td>\n           <td>20</td>\n     '
             b'      <td></td>\n           <td></td>\n        </tr>\n        \n'
             b'         <tr>\n           <td>C. Valenciana</td>\n           <'
             b'td>62</td>\n           <td></td>\n           <td></td>\n       '
             b' </tr>\n        \n         <tr>\n           <td>Extremadura</td'
             b'>\n           <td>7</td>\n           <td></td>\n           <td>'
             b'</td>\n        </tr>\n        \n         <tr>\n           <td>Co'
             b'munidad de Madrid</td>\n           <td>760</td>\n           <t'
             b'd></td>\n           <td></td>\n        </tr>\n        \n        '
             b' <tr>\n           <td>Regi\xc3\xb3n de Murcia</td>\n           <'
             b'td>7</td>\n           <td></td>\n           <td></td>\n        '
             b'</tr>\n        \n         <tr>\n           <td>Comunidad Foral '
             b'de Navarra</td>\n           <td>9</td>\n           <td></td>\n '
             b'          <td></td>\n        </tr>\n        \n         <tr>\n   '
             b'        <td>Pa\xc3\xads Vasco</td>\n           <td>190</td>\n   '
             b'        <td></td>\n           <td></td>\n        </tr>\n       '
             b' \n         <tr>\n           <td>La Rioja</td>\n           <td>'
             b'130</td>\n           <td></td>\n           <td></td>\n        <'
             b'/tr>\n        \n         <tr>\n           <td>Ceuta</td>\n      '
             b'     <td>0</td>\n           <td></td>\n           <td></td>\n  '
             b'      </tr>\n        \n         <tr>\n           <td>Melilla</t'
             b'd>\n           <td>0</td>\n           <td></td>\n           <td'
             b'></td>\n        </tr>\n        \n      </tbody>\n    </table'
             b'>\n     \n     <p>Vaya faena nos han hecho los de la <span'
             b'>p\xc3\xa1gina</span>...</p>\n     \n     \n     \n   \n   </bo'
             b'dy>\n </html>\n',
 '_content_consumed': True,
 '_next': None,
 'connection': <requests.adapters.HTTPAdapter object at 0x7f22580ee9a0>,
 'cookies': <RequestsCookieJar[]>,
 'elapsed': datetime.timedelta(microseconds=188306),
 'encoding': 'ISO-8859-1',
 'headers': {'Date': 'Thu, 20 Apr 2023 10:29:30 GMT', 'Server': 'Apache/2.4.18 (Ubuntu)', 'Last-Modified': 'Mon, 11 May 2020 10:04:22 GMT', 'ETag': '"1079-5a55c78929580-gzip"', 'Accept-Ranges': 'bytes', 'Vary': 'Accept-Encoding', 'Content-Encoding': 'gzip', 'Content-Length': '1086', 'Keep-Alive': 'timeout=5, max=100', 'Connection': 'Keep-Alive', 'Content-Type': 'text/html'},
 'history': [],
 'raw': <urllib3.response.HTTPResponse object at 0x7f22580c0520>,
 'reason': 'OK',
 'request': <PreparedRequest [GET]>,
 'status_code': 200,
 'url': 'https://zerjio.com/temp/covid19.html'}
[3]:
print(respuesta.status_code)   # El código de estado: 200 Es el código de que todo ha ido bien.
                               # Lista de códigos de estados posibles: https://www.restapitutorial.com/httpstatuscodes.html

print(respuesta.reason)        # Una descripción textual del código de respuesta


print(respuesta.elapsed)       # Tiempo que se ha tardado desde que se hizo la petición hasta que se obtuvo la respuesta


print(respuesta.encoding)      # La codificación del documento (juego de caracteres usado)

200
OK
0:00:00.188306
ISO-8859-1
[4]:
print(respuesta.content)     # Y lo que probablemente nos interesa más: el CONTENIDO de la página.
                             # El "problema" es que es una cadena "binaria".
b' <html xmlns="http://www.w3.org/1999/xhtml">\n   <head>\n   \n    <meta charset="UTF-8">\n\n    <script>\n    function cambiaDatosConJavascript() {\n      table = document.getElementById("tablaDatos");\n      \n      for (var i = 0, row; row = table.rows[i]; i++) {\n        if (i > 0) {\n          for (var j = 0, col; col = row.cells[j]; j++) {\n            if (j > 0) {\n              col.innerHTML = Math.floor(Math.random() * 1000);\n            }\n          }  \n        }\n      }\n    }\n    </script>\n    <title>P\xc3\xa1gina del COVID 19 para solventar problema en el curso de Python, m\xc3\xb3dulo de web-scrapping</title>\n   </head>\n   \n   <body onload="cambiaDatosConJavascript()">\n     <h2>Esta p\xc3\xa1gina "simula" una p\xc3\xa1gina que hab\xc3\xada en el direcci\xc3\xb3n https://covid19.isciii.es/ . Aunque no se parece est\xc3\xa9ticamente tiene los mismos elementos para que los ejemplos funcionen igual.</h2>\n     \n     <p>\xc3\x9altima actualizaci\xc3\xb3n <span id="fecha">10 de Marzo de 2020</span> a las\n     <span id="hora">10:10</span></p>\n     \n     \n     <table style="border: 1px solid #000000;">\n      <tbody>\n        <tr><td>Dato 1</td><td>33</td></tr>\n        <tr><td>Dato 2</td><td>666</td></tr>\n        <tr><td>Dato 3</td><td>7777</td></tr>\n        <tr><td>Dato 4</td><td>99</td></tr>\n      \n      </tbody>\n    </table>\n        \n    <p>La que viene a continuaci\xc3\xb3n es la tabla de datos de verdad.</p>\n    \n    \n     <table style="border: 1px solid #000000;" id="tablaDatos">\n       <thead>\n         <tr>\n           <th>Comunidad</th>\n           <th>Contagiados</th>\n           <th>Fallecidos</th>\n           <th>Curados</th>\n        </tr>\n      </thead>\n      \n      <tbody>\n         <tr>\n           <td>Andaluc\xc3\xada</td>\n           <td>80</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Arag\xc3\xb3n</td>\n           <td>50</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Principado de Asturias</td>\n           <td>30</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Islas Baleares</td>\n           <td>10</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Canarias</td>\n           <td>35</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Cantabria</td>\n           <td>12</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Castilla y Le\xc3\xb3n</td>\n           <td>60</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Catalu\xc3\xb1a</td>\n           <td>110</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Galicia</td>\n           <td>20</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>C. Valenciana</td>\n           <td>62</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Extremadura</td>\n           <td>7</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Comunidad de Madrid</td>\n           <td>760</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Regi\xc3\xb3n de Murcia</td>\n           <td>7</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Comunidad Foral de Navarra</td>\n           <td>9</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Pa\xc3\xads Vasco</td>\n           <td>190</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>La Rioja</td>\n           <td>130</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Ceuta</td>\n           <td>0</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n         <tr>\n           <td>Melilla</td>\n           <td>0</td>\n           <td></td>\n           <td></td>\n        </tr>\n        \n      </tbody>\n    </table>\n     \n     <p>Vaya faena nos han hecho los de la <span>p\xc3\xa1gina</span>...</p>\n     \n     \n     \n   \n   </body>\n </html>\n'
[5]:
html = respuesta.content.decode("UTF-8")   # Lo podemos codificar a una cadena "normal"

print(html)
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>

    <meta charset="UTF-8">

    <script>
    function cambiaDatosConJavascript() {
      table = document.getElementById("tablaDatos");

      for (var i = 0, row; row = table.rows[i]; i++) {
        if (i > 0) {
          for (var j = 0, col; col = row.cells[j]; j++) {
            if (j > 0) {
              col.innerHTML = Math.floor(Math.random() * 1000);
            }
          }
        }
      }
    }
    </script>
    <title>Página del COVID 19 para solventar problema en el curso de Python, módulo de web-scrapping</title>
   </head>

   <body onload="cambiaDatosConJavascript()">
     <h2>Esta página "simula" una página que había en el dirección https://covid19.isciii.es/ . Aunque no se parece estéticamente tiene los mismos elementos para que los ejemplos funcionen igual.</h2>

     <p>Última actualización <span id="fecha">10 de Marzo de 2020</span> a las
     <span id="hora">10:10</span></p>


     <table style="border: 1px solid #000000;">
      <tbody>
        <tr><td>Dato 1</td><td>33</td></tr>
        <tr><td>Dato 2</td><td>666</td></tr>
        <tr><td>Dato 3</td><td>7777</td></tr>
        <tr><td>Dato 4</td><td>99</td></tr>

      </tbody>
    </table>

    <p>La que viene a continuación es la tabla de datos de verdad.</p>


     <table style="border: 1px solid #000000;" id="tablaDatos">
       <thead>
         <tr>
           <th>Comunidad</th>
           <th>Contagiados</th>
           <th>Fallecidos</th>
           <th>Curados</th>
        </tr>
      </thead>

      <tbody>
         <tr>
           <td>Andalucía</td>
           <td>80</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Aragón</td>
           <td>50</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Principado de Asturias</td>
           <td>30</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Islas Baleares</td>
           <td>10</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Canarias</td>
           <td>35</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Cantabria</td>
           <td>12</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Castilla y León</td>
           <td>60</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Cataluña</td>
           <td>110</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Galicia</td>
           <td>20</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>C. Valenciana</td>
           <td>62</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Extremadura</td>
           <td>7</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Comunidad de Madrid</td>
           <td>760</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Región de Murcia</td>
           <td>7</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Comunidad Foral de Navarra</td>
           <td>9</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>País Vasco</td>
           <td>190</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>La Rioja</td>
           <td>130</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Ceuta</td>
           <td>0</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Melilla</td>
           <td>0</td>
           <td></td>
           <td></td>
        </tr>

      </tbody>
    </table>

     <p>Vaya faena nos han hecho los de la <span>página</span>...</p>




   </body>
 </html>

[6]:
# Podemos guardar el código HTML a un fichero local para poder inspeccionarlo con más tranquilidad

text_file = open("salidas/covid-19.html", "w")
text_file.write(html)
text_file.close()
[7]:
# También puede ser interesante usar el iterador que te permite recorrer el contenido obtenido línea a línea
for num, linea in enumerate(respuesta.iter_lines(), start=1):
  print("Línea %d: %s" % (num, linea.decode("UTF-8")) )
Línea 1:  <html xmlns="http://www.w3.org/1999/xhtml">
Línea 2:    <head>
Línea 3:
Línea 4:     <meta charset="UTF-8">
Línea 5:
Línea 6:     <script>
Línea 7:     function cambiaDatosConJavascript() {
Línea 8:       table = document.getElementById("tablaDatos");
Línea 9:
Línea 10:       for (var i = 0, row; row = table.rows[i]; i++) {
Línea 11:         if (i > 0) {
Línea 12:           for (var j = 0, col; col = row.cells[j]; j++) {
Línea 13:             if (j > 0) {
Línea 14:               col.innerHTML = Math.floor(Math.random() * 1000);
Línea 15:             }
Línea 16:           }
Línea 17:         }
Línea 18:       }
Línea 19:     }
Línea 20:     </script>
Línea 21:     <title>Página del COVID 19 para solventar problema en el curso de Python, módulo de web-scrapping</title>
Línea 22:    </head>
Línea 23:
Línea 24:    <body onload="cambiaDatosConJavascript()">
Línea 25:      <h2>Esta página "simula" una página que había en el dirección https://covid19.isciii.es/ . Aunque no se parece estéticamente tiene los mismos elementos para que los ejemplos funcionen igual.</h2>
Línea 26:
Línea 27:      <p>Última actualización <span id="fecha">10 de Marzo de 2020</span> a las
Línea 28:      <span id="hora">10:10</span></p>
Línea 29:
Línea 30:
Línea 31:      <table style="border: 1px solid #000000;">
Línea 32:       <tbody>
Línea 33:         <tr><td>Dato 1</td><td>33</td></tr>
Línea 34:         <tr><td>Dato 2</td><td>666</td></tr>
Línea 35:         <tr><td>Dato 3</td><td>7777</td></tr>
Línea 36:         <tr><td>Dato 4</td><td>99</td></tr>
Línea 37:
Línea 38:       </tbody>
Línea 39:     </table>
Línea 40:
Línea 41:     <p>La que viene a continuación es la tabla de datos de verdad.</p>
Línea 42:
Línea 43:
Línea 44:      <table style="border: 1px solid #000000;" id="tablaDatos">
Línea 45:        <thead>
Línea 46:          <tr>
Línea 47:            <th>Comunidad</th>
Línea 48:            <th>Contagiados</th>
Línea 49:            <th>Fallecidos</th>
Línea 50:            <th>Curados</th>
Línea 51:         </tr>
Línea 52:       </thead>
Línea 53:
Línea 54:       <tbody>
Línea 55:          <tr>
Línea 56:            <td>Andalucía</td>
Línea 57:            <td>80</td>
Línea 58:            <td></td>
Línea 59:            <td></td>
Línea 60:         </tr>
Línea 61:
Línea 62:          <tr>
Línea 63:            <td>Aragón</td>
Línea 64:            <td>50</td>
Línea 65:            <td></td>
Línea 66:            <td></td>
Línea 67:         </tr>
Línea 68:
Línea 69:          <tr>
Línea 70:            <td>Principado de Asturias</td>
Línea 71:            <td>30</td>
Línea 72:            <td></td>
Línea 73:            <td></td>
Línea 74:         </tr>
Línea 75:
Línea 76:          <tr>
Línea 77:            <td>Islas Baleares</td>
Línea 78:            <td>10</td>
Línea 79:            <td></td>
Línea 80:            <td></td>
Línea 81:         </tr>
Línea 82:
Línea 83:          <tr>
Línea 84:            <td>Canarias</td>
Línea 85:            <td>35</td>
Línea 86:            <td></td>
Línea 87:            <td></td>
Línea 88:         </tr>
Línea 89:
Línea 90:          <tr>
Línea 91:            <td>Cantabria</td>
Línea 92:            <td>12</td>
Línea 93:            <td></td>
Línea 94:            <td></td>
Línea 95:         </tr>
Línea 96:
Línea 97:          <tr>
Línea 98:            <td>Castilla y León</td>
Línea 99:            <td>60</td>
Línea 100:            <td></td>
Línea 101:            <td></td>
Línea 102:         </tr>
Línea 103:
Línea 104:          <tr>
Línea 105:            <td>Cataluña</td>
Línea 106:            <td>110</td>
Línea 107:            <td></td>
Línea 108:            <td></td>
Línea 109:         </tr>
Línea 110:
Línea 111:          <tr>
Línea 112:            <td>Galicia</td>
Línea 113:            <td>20</td>
Línea 114:            <td></td>
Línea 115:            <td></td>
Línea 116:         </tr>
Línea 117:
Línea 118:          <tr>
Línea 119:            <td>C. Valenciana</td>
Línea 120:            <td>62</td>
Línea 121:            <td></td>
Línea 122:            <td></td>
Línea 123:         </tr>
Línea 124:
Línea 125:          <tr>
Línea 126:            <td>Extremadura</td>
Línea 127:            <td>7</td>
Línea 128:            <td></td>
Línea 129:            <td></td>
Línea 130:         </tr>
Línea 131:
Línea 132:          <tr>
Línea 133:            <td>Comunidad de Madrid</td>
Línea 134:            <td>760</td>
Línea 135:            <td></td>
Línea 136:            <td></td>
Línea 137:         </tr>
Línea 138:
Línea 139:          <tr>
Línea 140:            <td>Región de Murcia</td>
Línea 141:            <td>7</td>
Línea 142:            <td></td>
Línea 143:            <td></td>
Línea 144:         </tr>
Línea 145:
Línea 146:          <tr>
Línea 147:            <td>Comunidad Foral de Navarra</td>
Línea 148:            <td>9</td>
Línea 149:            <td></td>
Línea 150:            <td></td>
Línea 151:         </tr>
Línea 152:
Línea 153:          <tr>
Línea 154:            <td>País Vasco</td>
Línea 155:            <td>190</td>
Línea 156:            <td></td>
Línea 157:            <td></td>
Línea 158:         </tr>
Línea 159:
Línea 160:          <tr>
Línea 161:            <td>La Rioja</td>
Línea 162:            <td>130</td>
Línea 163:            <td></td>
Línea 164:            <td></td>
Línea 165:         </tr>
Línea 166:
Línea 167:          <tr>
Línea 168:            <td>Ceuta</td>
Línea 169:            <td>0</td>
Línea 170:            <td></td>
Línea 171:            <td></td>
Línea 172:         </tr>
Línea 173:
Línea 174:          <tr>
Línea 175:            <td>Melilla</td>
Línea 176:            <td>0</td>
Línea 177:            <td></td>
Línea 178:            <td></td>
Línea 179:         </tr>
Línea 180:
Línea 181:       </tbody>
Línea 182:     </table>
Línea 183:
Línea 184:      <p>Vaya faena nos han hecho los de la <span>página</span>...</p>
Línea 185:
Línea 186:
Línea 187:
Línea 188:
Línea 189:    </body>
Línea 190:  </html>

Bajando ficheros binarios

Normalmente las páginas web están escritas en HTML que es tipo de fichero de texto (string) fácil de manejar. Sin embargo en ocasiones querremos bajar ficheros que estén en otros formatos que no sean texto (binarios), como son los formatos PDF, EXE, ZIP, JPG.

En una sección anterior del curso se planteó un script para bajar el BOP de Granada y luego sacar información del PDF. A continuación vamos a «refinar» el script que se creó simplificando y aprovechando alguna información que nos ofrece el protocolo HTTP.

[8]:
import datetime
import requests

baseURL="https://bop2.dipgra.es/opencms/opencms/portal/DescargaPDFBoletin?fecha="

fecha = datetime.datetime.now()
#fecha = datetime.datetime(2020, 5, 6)  # Año, mes, día, para una fecha que no sea hoy
print(fecha)

fechaFormateada = fecha.strftime("%d/%m/%Y")  # Formateamos la fecha, https://www.programiz.com/python-programming/datetime/strftime
print(fechaFormateada)

url = baseURL + fechaFormateada
print(url)
2023-04-20 12:29:30.915929
20/04/2023
https://bop2.dipgra.es/opencms/opencms/portal/DescargaPDFBoletin?fecha=20/04/2023
[9]:
respuesta = requests.get(url)  # hacemos la petición y se descargan los contenidos
[10]:
print("Cabeceras recibidas: " + str(respuesta.headers) + "\n") # Inspeccionamos las cabeceras que hemos recibido

# Nos interesa ver que "Content-Type" ya nos indica que es un fichero que puede abrirse con una aplicación
# que maneje PDFs
# Además en alguna ocasión podemos recibir que el tamaño del contenido descargado: "Content-Length"

print("Tipo de información descargada: " + respuesta.headers['Content-Type'] + "\n")

tamanioDescargado = 0
if "Content-Length" in respuesta.headers:
    tamanioDescargado = int(respuesta.headers['Content-Length'])
else:
    tamanioDescargado = len(respuesta.content)  # Si no nos lo dice la cabecera podemos mirar el tamaño del
                                                # contenido descargado


print("Tamaño descargado: " + str(tamanioDescargado) + " bytes\n")

# Además nos damos cuenta que la aplicación web nos sugiere un nombre para el fichero que vamos a descargar:

print("Campo que nos sugiere el nombre del fichero descargado: " + respuesta.headers['Content-disposition'] + "\n")

# En la aplicación web que sirve los BOP de Granada siempre devuelve una respuesta válida (código 200)
# aunque no exista el PDF de la fecha pedida. En una aplicación bien hecha devolvería un codigo de error distinto
# (NOT FOUND o similar). En cualquier caso nosotros nos hemos dado cuenta que si el contenido devuelto es 0 bytes
# es que ese día no hay BOP.

if tamanioDescargado == 0:
    print("En la fecha indicada no hay BOP (o es una fecha incorrecta).\n")
else:

    filename = "salidas/" + respuesta.headers['Content-disposition'][20:] # Saco el nombre del fichero truncando la cadena

    myfile = open(filename,'wb')
    myfile.write(respuesta.content)
    myfile.close()

    print("Fichero " + filename + " grabado en disco.\n")
Cabeceras recibidas: {'Date': 'Thu, 20 Apr 2023 10:25:00 GMT', 'Content-disposition': 'attachment;filename=Boletin_20230420.pdf', 'Content-Type': 'application/pdf', 'Keep-Alive': 'timeout=5, max=100', 'Connection': 'Keep-Alive', 'Transfer-Encoding': 'chunked'}

Tipo de información descargada: application/pdf

Tamaño descargado: 563625 bytes

Campo que nos sugiere el nombre del fichero descargado: attachment;filename=Boletin_20230420.pdf

Fichero salidas/Boletin_20230420.pdf grabado en disco.

Solucionando un problema común: requests.exceptions.SSLError

Al intentar hacer scraping a algunas páginas web puede que nos salte una excepción del tipo requests.exceptions.SSLError. Ese problema viene derivado de una mala configuración del servidor web (no es problema nuestro): Básicamente no tiene actualizado el certificado digital necesario para establecer una conexión segura. En realidad la explicación es más técnica, pero basta con saber eso para reconocer el error. La solución es «fácil»: solo tenemos que decirle al módulo requests que no haga comprobaciones del certificado digital. Con eso podremos proceder con la descarga, aunque en teoría es «peligroso», porque no podemos tener certeza de que el contenido que bajemos sea legítimo (podrían haber crackeado la web que consultamos, cosa no muy común). Para conseguir solucionar esto solo hay que añadir el parámetro verify=False en nuestra llamada requests.get(...). Eso sí, cuando desactivemos la comprobación de certificados nos aparecerá un Warning avisándonos de los «peligros» de no poder comprobarlos. Por ejemplo:

[11]:
respuesta = requests.get("https://zerjio.com/temp/covid19.html", verify=False)
/home/zerjillo/anaconda3/envs/cursoAstronomia2/lib/python3.8/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host 'zerjio.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
  warnings.warn(

Parseando páginas web: trabajar con el HTML

Dentro de la cadena del código HTML podemos buscar la información que nos interese recuperar. Podemos hacerlo de múltiples maneras. Empezaremos con algunas muy básicas y rudimentarias e iremos poco a poco mejorando la manera de acceder.

Opción 1: Buscaremos a lo «bestia» con las funciones de búsqueda de cadenas básicas: str.find()

https://www.journaldev.com/23666/python-string-find

Revisando el código de la página encontramos la fecha de actualización de la misma:

...
<span style="font-size:0.9vw"> [Actualizado a <span id="fecha">10 de Marzo de 2020</span> a las <span id="hora">10:10</span> horas]<br>Basada en  la notificación diaria de casos  agregados de COVID-19 al Ministerio de Sanidad
...

Y nos percatamos que la fecha en formato texto va encerrada entre: <span id="fecha"> y </span>. Busquémosla

[12]:
import requests                                         # Estas lineas las hemos copiado del anterior Notebook
respuesta = requests.get("https://zerjio.com/temp/covid19.html")  # Esta página reemplaza la anterior
html = respuesta.content.decode("UTF-8")                #    https://covid19.isciii.es/


cadenaBuscada = '<span id="fecha">'
posicionInicio = html.find(cadenaBuscada) + len(cadenaBuscada)
posicionFinal = html.find('</span>', posicionInicio)    # El segundo parámetro de find() es la posición desde
                                                        # donde empezar a buscar el primer parámetro

fecha = html[posicionInicio:posicionFinal]

print(fecha)

# Vamos a hacer lo mismo con la hora

cadenaBuscada = '<span id="hora">'
posicionInicio = html.find(cadenaBuscada) + len(cadenaBuscada)
posicionFinal = html.find('</span>', posicionInicio)

hora = html[posicionInicio:posicionFinal]

print(hora)
10 de Marzo de 2020
10:10

Opción 2: Usando expresiones regulares

Las expresiones regulares nos permiten mucha expresividad para localizar elementos dentro de una cadena. Sin embargo en ocasiones su uso puede ser complejo en un documento HTML. Información interesante sobre las expresiones regulares:

https://docs.python.org/3/library/re.html

https://www.w3schools.com/python/python_regex.asp

https://www.guru99.com/python-regular-expressions-complete-tutorial.html

https://www.programiz.com/python-programming/regex

Herramienta online para testear expresiones regulares (seleccionar lenguaje Python):

https://regex101.com/

[13]:
# Recuperemos la fecha y hora con expresiones regulares.
# Usaremos el módulo "regex":
#   > conda install -c conda-forge regex

import regex as re

fecha = re.search(r'<span id=\"fecha\">(.*)</span>', html).group(1)
hora = re.search(r'<span id="hora">(.*)</span>', html).group(1)

print("%s - %s" % (fecha, hora))

10 de Marzo de 2020 - 10:10
[14]:
# Cuidado con las busquedas "exhaustivas".
# Por ejemplo, podemos estar tentados a hacer una búsqueda de todas las fechas, por si hay más de una:

fecha = re.search(r'<span id="fecha">(.*)</span>', html, re.DOTALL).group(1)

print(fecha)
10 de Marzo de 2020</span> a las
     <span id="hora">10:10</span></p>


     <table style="border: 1px solid #000000;">
      <tbody>
        <tr><td>Dato 1</td><td>33</td></tr>
        <tr><td>Dato 2</td><td>666</td></tr>
        <tr><td>Dato 3</td><td>7777</td></tr>
        <tr><td>Dato 4</td><td>99</td></tr>

      </tbody>
    </table>

    <p>La que viene a continuación es la tabla de datos de verdad.</p>


     <table style="border: 1px solid #000000;" id="tablaDatos">
       <thead>
         <tr>
           <th>Comunidad</th>
           <th>Contagiados</th>
           <th>Fallecidos</th>
           <th>Curados</th>
        </tr>
      </thead>

      <tbody>
         <tr>
           <td>Andalucía</td>
           <td>80</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Aragón</td>
           <td>50</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Principado de Asturias</td>
           <td>30</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Islas Baleares</td>
           <td>10</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Canarias</td>
           <td>35</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Cantabria</td>
           <td>12</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Castilla y León</td>
           <td>60</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Cataluña</td>
           <td>110</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Galicia</td>
           <td>20</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>C. Valenciana</td>
           <td>62</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Extremadura</td>
           <td>7</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Comunidad de Madrid</td>
           <td>760</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Región de Murcia</td>
           <td>7</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Comunidad Foral de Navarra</td>
           <td>9</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>País Vasco</td>
           <td>190</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>La Rioja</td>
           <td>130</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Ceuta</td>
           <td>0</td>
           <td></td>
           <td></td>
        </tr>

         <tr>
           <td>Melilla</td>
           <td>0</td>
           <td></td>
           <td></td>
        </tr>

      </tbody>
    </table>

     <p>Vaya faena nos han hecho los de la <span>página

Opción 3: Usando un parser DOM

Document Object Model es una interfaz que permite manejar ficheros XML (y derivados, como HTML) mediante objetos que se encuentran en memoria formando una estructura de árbol (replicando la estructura de las etiquetas). Las interfaces DOM son normalmente mucho más cómodas e intuitivas de usar que otras alternativas, aunque no funcionan bien para ficheros muy grandes. A continuación veremos unos pocos ejemplos de bibliotecas y herramientas que funcionan con este tipo de interfaz.

MiniDOM

Ejemplo de uso de minidom (no es bueno para HTML que suele no estar bien formado, solo para XML). Por eso vamos a usar un fichero RSS de noticias

Documentacion de la biblioteca: https://docs.python.org/3.3/library/xml.dom.minidom.html

[15]:
import xml.dom.minidom

url = 'http://ep00.epimg.net/rss/elpais/portada.xml'
rss = requests.get(url).content.decode("UTF-8")

dom = xml.dom.minidom.parseString(rss)

# Obtenemos todas las etiquetas "item"
items = dom.getElementsByTagName("item")

# Para cada etiqueta mostraremos el título de la noticia asociada y la url de la imagen asociada (si la tiene)
for item in items:
    titulo = item.getElementsByTagName("title")

    print(" + " + titulo[0].firstChild.nodeValue)   # Detalle "peliagudo", el texto de dentro de una etiqueta
                                                    # es considerado un hijo. De hecho una etiqueta puede tener
                                                    # varios hijos de "texto". Ejemplo:
                                                    # <etiquetaConTexto>
                                                    #   Esto es texto
                                                    #   <etiquetaHijo />
                                                    #   Más texto
                                                    # </etiquetaConTexto>
                                                    #
                                                    # El elemento "etiquetaConTexto" tiene tres hijos:
                                                    #    + Hijo 1: de tipo texto: "\n  Esto es texto\n  "
                                                    #    + Hijo 2: etiqueta "etiquetaHijo"
                                                    #    + Hijo 3: de tipo texto: "\n  Más text\n  "

# En este ejemplo, las urls de las imágenes asociadas a un item están en la etiqueta "enclosure", atributo "url":

    enclosures = item.getElementsByTagName("enclosure")

    for e in enclosures:
        print("   - " + e.getAttribute('url'))
 + Un periodista que viajó a Milán para cubrir el Valencia-Atalanta, positivo por coronavirus
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582795523_075914_1582795972_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582795523_075914_1582795972_noticia_normal.jpg
 + Mapa de la expansión y claves para entender el coronavirus de Wuhan
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582712658_011286_1582742390_miniatura_normal.png
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582712658_011286_1583145456_portadilla_normal.png
 + El turismo italiano se resiente; España mira expectante
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582751511_964259_1582759340_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582751511_964259_1582759340_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/f6f986136f7d70e226ac5f38df9fa88a_video_1800.mp4
 + “La magnitud del problema del coronavirus no será diferente a una gripe”
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582744701_468958_1582744844_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582744701_468958_1582797813_noticia_normal.jpg
 + Seis italianos encerrados por partida doble
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582745125_354016_1582747646_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582745125_354016_1582747646_noticia_normal.jpg
 + Trump nombra al vicepresidente Pence a cargo de la crisis del coronavirus
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582762594_400436_1582763694_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582762594_400436_1582763694_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/202002279361360_1582792664_video_1800.mp4
 + El coronavirus de Wuhan | Japón cerrará todos sus centros escolares hasta fin de marzo
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582772469_101682_1582773414_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582772469_101682_1582786463_noticia_normal.jpg
 + El Gobierno salva el primer escollo de los Presupuestos gracias a ERC
   - https://ep00.epimg.net/politica/imagenes/2020/02/27/actualidad/1582790343_062149_1582797637_miniatura_normal.jpg
   - https://ep00.epimg.net/politica/imagenes/2020/02/27/actualidad/1582790343_062149_1582807919_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/bd6f863b8c606b83aa4a190909cad1d2_video_1800.mp4
 + La mesa de diálogo se reunirá mensualmente y los acuerdos serán “en el marco de la seguridad jurídica”
   - https://ep00.epimg.net/politica/imagenes/2020/02/26/actualidad/1582729437_069559_1582743257_miniatura_normal.jpg
   - https://ep00.epimg.net/politica/imagenes/2020/02/26/actualidad/1582729437_069559_1582743257_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/9265b903ba4c44f3771b64a42fe06095_video_1800.mp4
 + Así avanzan las encuestas en Galicia y País Vasco
   - https://ep00.epimg.net/politica/imagenes/2020/02/26/actualidad/1582705222_037590_1582705381_noticia_normal.jpg
 + Ciudadanos pagaba un sueldo a un vocal de la Junta Electoral Central que resolvió recursos del partido
   - https://ep00.epimg.net/politica/imagenes/2019/11/17/actualidad/1574018167_006384_1574018434_miniatura_normal.jpg
   - https://ep00.epimg.net/politica/imagenes/2019/11/17/actualidad/1574018167_006384_1574018434_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/20200227164217541_1582818245_video_1800.mp4
 + 50 antiguos ministros y dirigentes europeos se pronuncian contra el plan de Trump para Palestina
   - https://ep00.epimg.net/internacional/imagenes/2020/02/26/actualidad/1582750435_447092_1582751809_miniatura_normal.jpg
   - https://ep00.epimg.net/internacional/imagenes/2020/02/26/actualidad/1582750435_447092_1582751809_noticia_normal.jpg
 + Plácido Domingo anula sus representaciones en el Real antes de que el teatro las cancelara
   - https://ep00.epimg.net/cultura/imagenes/2020/02/27/actualidad/1582789321_585769_1582794632_miniatura_normal.jpg
   - https://ep00.epimg.net/cultura/imagenes/2020/02/27/actualidad/1582789321_585769_1582790092_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/86e4da93f1d390d4fd81392d9f903c91_video_1800.mp4
 + Así gestionan nuestros vecinos europeos la expansión de Airbnb
   - https://ep00.epimg.net/internacional/imagenes/2020/02/21/sonar_europe/1582302332_702790_1582307413_miniatura_normal.jpg
   - https://ep00.epimg.net/internacional/imagenes/2020/02/21/sonar_europe/1582302332_702790_1582307413_noticia_normal.jpg
 + La Fiscalía se querella contra una mujer por tuitear un vídeo falso de menores migrantes
   - https://ep00.epimg.net/ccaa/imagenes/2018/11/24/madrid/1543077391_578679_1543078301_miniatura_normal.jpg
   - https://ep00.epimg.net/ccaa/imagenes/2018/11/24/madrid/1543077391_578679_1543078301_noticia_normal.jpg
 + China desvela de qué está hecha la cara oculta de la Luna
   - https://ep00.epimg.net/elpais/imagenes/2020/02/25/ciencia/1582651238_235155_1582741504_miniatura_normal.jpg
   - https://ep00.epimg.net/elpais/imagenes/2020/02/25/ciencia/1582651238_235155_1582741504_noticia_normal.jpg
 + Especial EL PAÍS en Arco | Muntadas: “La sociedad ha degradado las palabras”
   - https://ep00.epimg.net/elpais/imagenes/2020/02/27/actualidad/1582803771_531979_1582803808_portada_normal.jpg
 + La historia de Duralex, una vajilla tan irrompible como nuestra nostalgia
   - https://ep00.epimg.net/elpais/imagenes/2020/02/27/portada/1582804176_187015_1582804289_miniatura_normal.jpg
   - https://ep00.epimg.net/verne/imagenes/2020/02/25/articulo/1582628993_071991_1582631824_noticia_normal.jpg
 + ¿Por qué los mosquitos se ceban con nuestros tobillos?
   - https://ep00.epimg.net/elpais/imagenes/2020/02/27/ciencia/1582797400_512615_1582797833_miniatura_normal.jpg
   - https://ep00.epimg.net/elpais/imagenes/2020/02/27/ciencia/1582797400_512615_1582797833_noticia_normal.jpg
 + Consumo da la razón a los clientes de Fnac que compraron un móvil 575 euros más barato por un error
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582796975_880373_1582799022_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582796975_880373_1582800034_noticia_normal.jpg
 + Javier Bardem: “Hoy hay algún Hernán Cortés en el Congreso”
   - https://ep00.epimg.net/cultura/imagenes/2020/02/26/actualidad/1582734306_817032_1582749328_miniatura_normal.jpg
   - https://ep00.epimg.net/cultura/imagenes/2020/02/26/actualidad/1582734306_817032_1582749299_noticia_normal.jpg

DOM parser lxml.etree

Esta API es más sencilla y está más adaptada a Python:

  • Los elementos son listas (con lo que podemos acceder a sus hijos por índices)

  • Permite fácilmente modificar los elementos del DOM (lo mismo no es muy útil si estamos solo obteniendo información, pero vete tu a saber).

  • Los atributos de un elemento son un diccionario que está en elemento.attrib, con lo que acceder a ellos es muy «natural».

Tutorial: https://lxml.de/tutorial.html

Para instalar el módulo:

> conda install -c anaconda lxml
[16]:
# Repetimos el ejercicio anterior pero con el parser DOM lxml.etree
from lxml import etree

tree = etree.parse(url)

raiz = tree.getroot()

# Iteramos por todos los descendientes de raiz que sean "item"

for item in raiz.iter("item"):
    for hijo in item.iter("title"):
        print(" + " + hijo.text)

    for hijo in item.iter("enclosure"):
        print("   - " + hijo.attrib['url'])
 + Un periodista que viajó a Milán para cubrir el Valencia-Atalanta, positivo por coronavirus
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582795523_075914_1582795972_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582795523_075914_1582795972_noticia_normal.jpg
 + Mapa de la expansión y claves para entender el coronavirus de Wuhan
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582712658_011286_1582742390_miniatura_normal.png
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582712658_011286_1583145456_portadilla_normal.png
 + El turismo italiano se resiente; España mira expectante
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582751511_964259_1582759340_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582751511_964259_1582759340_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/f6f986136f7d70e226ac5f38df9fa88a_video_1800.mp4
 + “La magnitud del problema del coronavirus no será diferente a una gripe”
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582744701_468958_1582744844_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582744701_468958_1582797813_noticia_normal.jpg
 + Seis italianos encerrados por partida doble
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582745125_354016_1582747646_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/26/actualidad/1582745125_354016_1582747646_noticia_normal.jpg
 + Trump nombra al vicepresidente Pence a cargo de la crisis del coronavirus
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582762594_400436_1582763694_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582762594_400436_1582763694_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/202002279361360_1582792664_video_1800.mp4
 + El coronavirus de Wuhan | Japón cerrará todos sus centros escolares hasta fin de marzo
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582772469_101682_1582773414_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582772469_101682_1582786463_noticia_normal.jpg
 + El Gobierno salva el primer escollo de los Presupuestos gracias a ERC
   - https://ep00.epimg.net/politica/imagenes/2020/02/27/actualidad/1582790343_062149_1582797637_miniatura_normal.jpg
   - https://ep00.epimg.net/politica/imagenes/2020/02/27/actualidad/1582790343_062149_1582807919_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/bd6f863b8c606b83aa4a190909cad1d2_video_1800.mp4
 + La mesa de diálogo se reunirá mensualmente y los acuerdos serán “en el marco de la seguridad jurídica”
   - https://ep00.epimg.net/politica/imagenes/2020/02/26/actualidad/1582729437_069559_1582743257_miniatura_normal.jpg
   - https://ep00.epimg.net/politica/imagenes/2020/02/26/actualidad/1582729437_069559_1582743257_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/9265b903ba4c44f3771b64a42fe06095_video_1800.mp4
 + Así avanzan las encuestas en Galicia y País Vasco
   - https://ep00.epimg.net/politica/imagenes/2020/02/26/actualidad/1582705222_037590_1582705381_noticia_normal.jpg
 + Ciudadanos pagaba un sueldo a un vocal de la Junta Electoral Central que resolvió recursos del partido
   - https://ep00.epimg.net/politica/imagenes/2019/11/17/actualidad/1574018167_006384_1574018434_miniatura_normal.jpg
   - https://ep00.epimg.net/politica/imagenes/2019/11/17/actualidad/1574018167_006384_1574018434_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/20200227164217541_1582818245_video_1800.mp4
 + 50 antiguos ministros y dirigentes europeos se pronuncian contra el plan de Trump para Palestina
   - https://ep00.epimg.net/internacional/imagenes/2020/02/26/actualidad/1582750435_447092_1582751809_miniatura_normal.jpg
   - https://ep00.epimg.net/internacional/imagenes/2020/02/26/actualidad/1582750435_447092_1582751809_noticia_normal.jpg
 + Plácido Domingo anula sus representaciones en el Real antes de que el teatro las cancelara
   - https://ep00.epimg.net/cultura/imagenes/2020/02/27/actualidad/1582789321_585769_1582794632_miniatura_normal.jpg
   - https://ep00.epimg.net/cultura/imagenes/2020/02/27/actualidad/1582789321_585769_1582790092_noticia_normal.jpg
   - https://elpaisfs.prisasd.com/elpaistop/multimedia/20202/27/86e4da93f1d390d4fd81392d9f903c91_video_1800.mp4
 + Así gestionan nuestros vecinos europeos la expansión de Airbnb
   - https://ep00.epimg.net/internacional/imagenes/2020/02/21/sonar_europe/1582302332_702790_1582307413_miniatura_normal.jpg
   - https://ep00.epimg.net/internacional/imagenes/2020/02/21/sonar_europe/1582302332_702790_1582307413_noticia_normal.jpg
 + La Fiscalía se querella contra una mujer por tuitear un vídeo falso de menores migrantes
   - https://ep00.epimg.net/ccaa/imagenes/2018/11/24/madrid/1543077391_578679_1543078301_miniatura_normal.jpg
   - https://ep00.epimg.net/ccaa/imagenes/2018/11/24/madrid/1543077391_578679_1543078301_noticia_normal.jpg
 + China desvela de qué está hecha la cara oculta de la Luna
   - https://ep00.epimg.net/elpais/imagenes/2020/02/25/ciencia/1582651238_235155_1582741504_miniatura_normal.jpg
   - https://ep00.epimg.net/elpais/imagenes/2020/02/25/ciencia/1582651238_235155_1582741504_noticia_normal.jpg
 + Especial EL PAÍS en Arco | Muntadas: “La sociedad ha degradado las palabras”
   - https://ep00.epimg.net/elpais/imagenes/2020/02/27/actualidad/1582803771_531979_1582803808_portada_normal.jpg
 + La historia de Duralex, una vajilla tan irrompible como nuestra nostalgia
   - https://ep00.epimg.net/elpais/imagenes/2020/02/27/portada/1582804176_187015_1582804289_miniatura_normal.jpg
   - https://ep00.epimg.net/verne/imagenes/2020/02/25/articulo/1582628993_071991_1582631824_noticia_normal.jpg
 + ¿Por qué los mosquitos se ceban con nuestros tobillos?
   - https://ep00.epimg.net/elpais/imagenes/2020/02/27/ciencia/1582797400_512615_1582797833_miniatura_normal.jpg
   - https://ep00.epimg.net/elpais/imagenes/2020/02/27/ciencia/1582797400_512615_1582797833_noticia_normal.jpg
 + Consumo da la razón a los clientes de Fnac que compraron un móvil 575 euros más barato por un error
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582796975_880373_1582799022_miniatura_normal.jpg
   - https://ep00.epimg.net/sociedad/imagenes/2020/02/27/actualidad/1582796975_880373_1582800034_noticia_normal.jpg
 + Javier Bardem: “Hoy hay algún Hernán Cortés en el Congreso”
   - https://ep00.epimg.net/cultura/imagenes/2020/02/26/actualidad/1582734306_817032_1582749328_miniatura_normal.jpg
   - https://ep00.epimg.net/cultura/imagenes/2020/02/26/actualidad/1582734306_817032_1582749299_noticia_normal.jpg

Opción 4: Usando XPath para localizar elementos fácilmente

XPath (XML Path Language) es un lenguaje que permite construir expresiones que recorren y procesan un documento XML. Permite buscar y seleccionar teniendo en cuenta la estructura jerárquica del XML.

[17]:
# Replicaremos el ejemplo de la gráfica de casos de COVID-19 de manera mucho más simple
from lxml import etree
from io import StringIO, BytesIO
import matplotlib.pyplot as plt

parserHTML = etree.HTMLParser()

# Recuerda que html se inicializó mucho antes con el contenido de la página https://covid19.isciii.es/
tree = etree.parse(StringIO(html), parserHTML)

tablas = tree.xpath('//table')  # Nos devuelve todos los elementos "table" en cualquier sitio del documento
                                # En nuestro caso habrá devuelto 2 tablas

# Nos interesa la segunda tabla de la página
tabla = tablas[1]

comunidades = []
casos = []

filas = tabla.xpath('tbody/tr')  # Buscamos todos los <tr> que estén dentro de <tbody> en la tabla

for f in filas:
    comunidades.append(f[0].text)
    casos.append(int(f[1].text))


# Creamos el gráfico de barras
plt.bar(comunidades, casos)
# Rotation of the bars names
plt.xticks(rotation=90)
# Show graphic
plt.show()
_images/D-1-webScraping_26_0.png

Ejercicio D.1: Obtener un campo del Sloan Digital Sky Survey (SDSS)

El SDSS tiene multitud de herramientas que permiten la obtención de datos e imágenes del cielo. Por ejemplo en https://dr12.sdss.org/fields/ podemos descargar campos concretos utilizando las coordenadas del cielo que nos interesan. Jugando un poco con el formulario de esa página «descubrimos» que podemos obtener el campo mediante el uso de la siguiente URL que tiene como parámetros la ascension recta (ra) y la declinación (dec):

https://dr12.sdss.org/fields/raDec?ra=250.423475&dec=36.461319

Programemos una función para descargar la imagen que corresponde a cualquier coordenada que nos interese:

[18]:
# Tu código aquí

# descargaSDSS(250.423475, 36.461319, "m13.jpg")

# print("Descarga realizada")

Ejecicio D.2: Astronomy Picture of the Day (APOD) (desde la página oficial)

Queremos hacer un script que descargue la imagen astronómica del día y la ponga de fondo de escritorio.

La función de poner como fondo de escritorio depende del sistema operativo, por lo que habrá que descomentar la función setWallpaper que corresponda en el código de más abajo.

Para conseguir nuestro objetivo tendremos que descargar la página del APOD y analizar el código HTML para dar con la URL de la imagen que queremos descargar.

[19]:
#import dbus       # conda install -c conda-forge dbus-python   SOLO PARA Linux / KDE

# Para Linux con KDE
#def setWallpaper(filepath):
#    plugin = 'org.kde.image'
#    jscript = """
#    var allDesktops = desktops();
#    print (allDesktops);
#    for (i=0;i<allDesktops.length;i++) {
#        d = allDesktops[i];
#        d.wallpaperPlugin = "%s";
#        d.currentConfigGroup = Array("Wallpaper", "%s", "General");
#        d.writeConfig("Image", "file://%s")
#    }
#    """
#    bus = dbus.SessionBus()
#    plasma = dbus.Interface(bus.get_object('org.kde.plasmashell', '/PlasmaShell'), dbus_interface='org.kde.PlasmaShell')
#    plasma.evaluateScript(jscript % (plugin, plugin, filepath))

# Para Linux con Gnome
# def setWallpaper(filepath):
#     os.system("gsettings set org.gnome.desktop.background picture-uri file://" + filepath)

# Para Windows
# import ctypes
# ctypes.windll.user32.SystemParametersInfoW(20,0,"Path_wallpaper", 0)

# Para Mac
# Ni idea, habría que buscarlo

#setWallpaper("/home/zerjillo/esfera.jpg")   # Cambiar la direccion a la ruta absoluta de una imagen

# Tu código aquí

Haciendo peticiones http más «complejas»

Hasta ahora las peticiones que hemos realizado para hacer scraping son simples llamadas GET a URLs concretas. Sin embargo en algunas ocasiones (por ejemplo cuando nos encontremos páginas en las que tenemos que interaccionar con formularios) o cuando queramos interactuar con una API es posible que necesitemos hacer peticiones con otro verbo http diferente o incluso mandar parámetros.

La siguiente página:

https://www.mscbs.gob.es/buscador/iniciar.do

nos permite hacer búsquedas en la web del Ministerio de Sanidad, Consumo y Bienestar Social. Si quisieramos scrapear la página de búsquedas podemos inspeccionar el código HTML de la misma y localizar el formulario:

<form id="formBuscador" method="post" action="iniciar.do">
...
    <input type="text" name="search" value="" id="searchBsq" style="width: 20em;" class="campo_texto" aria-label="Término o términos de la consulta">
    <input type="submit" value="Buscar" class="boton">
    <input type="radio" name="buscar" value="msc" id="msc" checked="checked"><span class="negrita"><label for="msc">En el Ministerio de Sanidad, Consumo y Bienestar Social</label></span>
    <input type="radio" name="buscar" value="internet" id="internet"><span class="negrita"><label for="internet">En Internet</label></span>
...
</form>

Vemos que la petición que se realizar es con el método POST y que hay varios campos que se pueden rellenar: search (el campo de búsqueda en sí) y buscar (que es un radio button con dos opciones: msc o internet). Además la petición del formulario se va a hacer a la misma página: action="iniciar.do".

[20]:
# Ejemplo de búsqueda en la página descrita anteriormente

import requests

url = "https://www.mscbs.gob.es/buscador/iniciar.do"

parametros = {
    'search': 'COVID',  # Buscaremos el término 'COVID'
    'buscar': 'msc'     # Dentro de la página del ministerio
}

respuesta = requests.post(url, parametros)

html = respuesta.content.decode("utf-8")

print(html)

<!DOCTYPE html>









<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es" lang="es">
        <head>
                <!--  Establece las cabeceras -->
                <meta charset="utf-8">
                <meta http-equiv="X-UA-Compatible" content="IE=edge">
                <meta name="viewport" content="width=device-width, initial-scale=1">

                <title>
                        Ministerio de Sanidad  - Buscador

                </title>

                        <meta name="Keywords" content="Ministerio de Sanidad, Buscador"/>



                        <meta name="description" content="Buscador"/>



                <!-- CSS -->
                <link href="../diseno/css/bootstrap.min.css" rel="stylesheet">
                <link href="../diseno/css/ministerio-sanidad.css" rel="stylesheet">
                <link href="../diseno/css/ministerio-sanidad-general.css" rel="stylesheet">
                <link href="../diseno/css/jasny-bootstrap.min.css" rel="stylesheet">
                <link href="../diseno/css/font-awesome.min.css" rel="stylesheet">
                <link href="../diseno/css/formularios.css" rel="stylesheet">
                <link href="../diseno/css/mapa-web.css" rel="stylesheet">
                <link href="../diseno/css/buscador-holmes.css" rel="stylesheet">
                <link href="../diseno/jscalendar/calendar-brown.css" rel="stylesheet">

                <noscript><link rel="stylesheet" href="css/nojs-estilos.css"></noscript>
                <!--[if IE 8 ]> <html class="ie8" lang="es"> <![endif]-->
                <!--[if lt IE 9]><script src="js/html5shiv.min.js"></script><![endif]-->

                <script src="../diseno/js/jquery.min.js"></script>
                <script src="../diseno/js/utilidades.js"></script>
            <script src="../diseno/js/multidiomaEscenarioDinamico.js"></script>
        </head>
        <body>








<header>

<div class="float-panel" data-top="0" data-scroll="10">

<!--Inicio CABECERA-->
<div class="container-fluid cabecera">

  <div class="col-xs-12 col-sm-5 col-md-6 col-lg-6">
    <div class="logo-ministerio"><a title="Ministerio de Sanidad - Gobierno de España" href="/"><img alt="Ministerio de Sanidad - Gobierno de España" src="../diseno/img/logo_ministerio.jpg"></a></div>
    <div class="nombre-ministerio">Ministerio <span>de Sanidad</span></div>
  </div>


  <div class="col-sm-7 col-md-6 col-lg-6 hidden-xs text-right navCanalesIdioma">
        <ul class="siguenos">
                <li><a href="../rss/cargar.do" title="RSS"><img src="../diseno/img/ico-rss.png" alt="" /><span class="sr-only">RSS</span></a></li>
                <li><a title="Facebook. Se abrirá en una ventana nueva" target="_blank" href="https://www.facebook.com/MinSanidad/"><img src="../diseno/img/ico-facebook.png" alt="" /><span class="sr-only">Facebook</span></a></li>
                <li><a title="Twitter. Se abrirá en una ventana nueva" target="_blank" href="http://twitter.com/sanidadgob"><img src="../diseno/img/ico-twitter.png" alt="" /><span class="sr-only">Twitter</span></a></li>
                <li><a title="Youtube. Se abrirá en una ventana nueva" target="_blank" href="http://www.youtube.com/user/ministeriosyps"><img src="../diseno/img/ico-youtube.png" alt="" /><span class="sr-only">Youtube</span></a></li>
                <li><a title="Suscripción a listas" href="https://listas.msssi.es"><img src="../diseno/img/ico-suscripcionlistas.png" alt="" /><span class="sr-only">Suscripción a listas</span></a></li>
                <li><a title="Webs temáticas" href="../websTematicas/home.htm"><img src="../diseno/img/ico-webstematicas.png" alt="" /><span class="sr-only">Webs temáticas</span></a></li>
        </ul>

      <ul class="nav navlist navbar-right navlistIdioma">
        <li class="language dropdown">
          <a href="/home.htm" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-menu-down" aria-hidden="true">&nbsp;</span>Castellano</a>
          <ul class="dropdown-menu">
                <li><a href="/buscador/iniciar.do" title="Bienvenidos: acceso a la web en español">Castellano</a></li>
                        <li><a href="/ca/buscador/iniciar.do" title="Benvinguts: Acceso a la web en catalán"><span lang ="ca">Català</span></a></li>
                        <li><a href="/eu/buscador/iniciar.do" title="Ongi etorri: Acceso a la web en euskera"><span lang ="eu">Euskara</span></a></li>
                        <li><a href="/gl/buscador/iniciar.do" title="Benvidos: Acceso a la web en gallego"><span lang ="gl">Galego</span></a></li>
                        <li><a href="/va/buscador/iniciar.do" title="Benvinguts: Acceso a la web en valenciano"><span lang ="ca-valencia">Valencià</span></a></li>
                        <li><a href="/en/buscador/iniciar.do" title="Welcome: Acceso a la web en inglés"><span lang ="en">English</span></a></li>
                        <li><a href="/fr/buscador/iniciar.do" title="Bienvenue: Acceso a la web en francés" ><span lang ="fr">Français</span></a></li>
          </ul>
        </li>
      </ul>
  </div>

  <div class="col-sm-7 col-md-6 col-lg-6 hidden-xs text-right pull-right">

    <ul class="accesos">
                <li><a href="../servCiudadanos/home.htm">Servicios al ciudadano</a></li>
                <li><a href="https://sede.mscbs.gob.es/" target="_blank" title="Se abrirá en una ventana nueva" class="sede"><strong>Sede</strong> Electrónica</a></li>
        </ul>

    <!--Buscador-->
        <div class="buscador">
                <form role="search" class="search" method="post" action="../buscador/iniciar.do">
                        <label for="buscar" class="sr-only">Buscar</label>
                        <input id="buscar" type="text" name="search" />
                        <button type="submit"><span class="glyphicon glyphicon-search" aria-hidden="true">&nbsp;</span></button>
                </form>
        </div>
        <!--Fin buscador-->

    <div class="servicios">
                <a href="../servCiudadanos/home.htm" title="Servicios al ciudadano"><img src="../diseno/img/ico-servicios.png" alt="Servicios al ciudadano" /></a>
        </div>

  </div>

</div>
<!--Fin CABECERA-->


<!--Menú PRINCIPAL-->
  <nav class="navbar navbar-default menu-categorias">

    <script src="../diseno/general/js/menuIzqDesplegable.js"></script>

        <!--Menú CATEGORIAS PRINCIPALES-->
        <div class="collapse navbar-collapse" id="categorias">
                <ul class="nav navbar-nav navlist" role="menu">

                                <li class="dropdown">
                                        <a href="../organizacion/portada/home.htm" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span>Organización Institucional</span></a>
                                </li>


                                <li class="dropdown">
                                        <a href="../sanidad/portada/home.htm" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span>Sanidad</span></a>
                                </li>


                                <li class="dropdown">
                                        <a href="../ssi/portada/home.htm" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span>Servicios Sociales e Igualdad</span></a>
                                </li>


                                <li class="dropdown">
                                        <a href="../normativa/proyectos/home.htm" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span>Proyectos normativos</span></a>
                                </li>

                </ul>
        </div>
        <script src="../diseno/general/js/categorias.js"></script>
  </nav>
  <!--FIN MENU CATEGORIAS PRINCIPALES-->

</div>

</header>

                <!-- INICIO CONTENIDO-->
                <div class="container-fluid contenido">






<ol class="breadcrumb">
    <li>
        <a href="../home.htm">
            <i class="fa fa-home" aria-hidden="true">&nbsp;</i><span class="sr-only">Inicio</span>
        </a>
    </li>
</ol>
<script src="../diseno/null/js/migasDinamico.js"></script>












<script type="text/javascript">
$(function() {
   $("#searchBsq").attr("aria-label", "Término o términos de la consulta");
});
</script>

<!--Contenido principal-->
<section class="col-sm-8 col-md-12 informacion" role="main">
        <h1>Buscador</h1>
        <form id="formBuscador" method="post" action="iniciar.do">
                <div id="alma_capa">
                                        <ul class="lista_pes">
                                                <li><a class="actual" href="iniciar.do">Búsqueda sencilla</a></li>
                                                <li><a href="iniciar.do?metodo=avanzada">Búsqueda avanzada</a></li>
                                        </ul>
                                <div id="alma_cab">
                                        <div class="centro_sencilla">
                                                <input type="text" name="search" value="COVID" id="searchBsq" style="width: 20em;" class="campo_texto" />
                                                <input type="submit" value="Buscar" class="boton" />
                                                <input type="radio" name="buscar" value="msc" id="msc" checked="checked"/><span class="negrita"><label for="msc">En el Ministerio de Sanidad</label></span>
                                                <input type="radio" name="buscar" value="internet" id="internet" /><span class="negrita"><label for="internet">En Internet</label></span>
                                        </div>
                                </div><!-- alma_cab -->



                                                        <div id="alma_federados">
                                                                <ul class="enLinea">

                                                                                <li><a href="iniciar.do?search=COVID&amp;ser=INGESA&amp;byDate="><span class="negrita">
                                                                                        INGESA
                                                                                        </span>(37)</a>
                                                                                </li>

                                                                                <li><a href="iniciar.do?search=COVID&amp;ser=PNSD&amp;byDate="><span class="negrita">
                                                                                        PNSD
                                                                                        </span>(393)</a>
                                                                                </li>

                                                                </ul>
                                                        </div>



                                                <div id="alma_resultados"><!-- Aqui van los resultados -->
                                                        <div id="titulo_res">865 resultados en MSSSI para COVID</div>
                                                        <div id="titulo_time"><!-- En segs--></div>
                                                        <div id="alma_orden">

                                                                        <span>[Orden por relevancia]</span>
                                                                        <a href="iniciar.do?search=COVID&amp;ser=MSSSI&amp;byDate=true">[Orden por fecha]</a>


                                                        </div><!-- alma_orden -->


                                                        <div class='capaCentroBuscador'>
                                                                <ol start="1">


                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/home.htm" target="_blank">[Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Enfermedad por nuevo coronavirus, <em>COVID</em>
-19]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/home.htm" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/home.htm
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p>Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Enfermedad por nuevo coronavirus, <em>COVID</em>
-19 6 Ministerio de Sanidad, Consumo y Bienestar Social RSS Facebook Twitter Youtube Suscripci&oacute;n a listas Webs tem&aacute;ticas &nbsp; Castellano Castellano Catal&agrave; Euskara Galego Valenci&agrave; English Fran&ccedil;ais Servicios al ciudadano Sede Electr&oacute;nica Buscar &nbsp; Organizaci&oacute;n Institucional Sanidad Servicios Sociales e Igualdad Proyectos normativos &nbsp; Inicio Sanidad Profesionales &nbsp; &nbsp; &nbsp; Men&uacute; Sanidad Ciudadanos</p>
                                                                                        <p class="fecha">Fecha: 20/04/2023</p>
                                                                                         </li>



                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ccd/ccaa.htm" target="_blank">[Ministerio de Sanidad - Profesionales - Mapa del estado de la expedici&oacute;n del Certificado <em>COVID</em>
 Digital de la UE en Comunidades Aut&oacute;nomas]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ccd/ccaa.htm" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/ccd/ccaa.htm
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p>Ministerio de Sanidad - Profesionales - Mapa del estado de la expedici&oacute;n del Certificado <em>COVID</em>
 Digital de la UE en Comunidades Aut&oacute;nomas 7 Ministerio de Sanidad RSS Facebook Twitter Youtube Suscripci&oacute;n a listas Webs tem&aacute;ticas &nbsp; Castellano Castellano Catal&agrave; Euskara Galego Valenci&agrave; English Fran&ccedil;ais Servicios al ciudadano Sede Electr&oacute;nica Buscar &nbsp; Organizaci&oacute;n Institucional Sanidad Servicios Sociales e Igualdad Proyectos normativos &nbsp; Inicio Sanidad Profesionales &nbsp; &nbsp; &nbsp; Men&uacute; Sanidad Ciudadanos</p>
                                                                                        <p class="fecha">Fecha: 01/04/2023</p>
                                                                                         </li>



                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/home.htm" target="_blank">[Ministerio de Sanidad]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/home.htm" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/home.htm
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p>Ministerio de Sanidad Ministerio de Sanidad &nbsp; Castellano Castellano Catal&agrave; Euskara Galego Valenci&agrave; English Fran&ccedil;ais Servicios al ciudadano Sede Electr&oacute;nica Buscar Organizaci&oacute;n Institucional Sanidad Servicios Sociales Proyectos normativos Agenda de actos Calendar Anterior Llama a la vida. Llama al 024 Informaci&oacute;n sobre la l&iacute;nea de atenci&oacute;n a la conducta suicida. Informaci&oacute;n CORONAVIRUS Informaci&oacute;n sobre el brote de neumon&iacute;a causada por un nuevo coronavirus (<em>COVID</em>
-19). Bajas temperaturas</p>
                                                                                        <p class="fecha">Fecha: 21/03/2023</p>
                                                                                         </li>



                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ciudadania.htm" target="_blank">[Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Información para la ciudadanía - Coronavirus]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ciudadania.htm" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/ciudadania.htm
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p> accesible Vacunaci&oacute;n <em>COVID</em>
-19 PDF accesible &iquest;Sabes qu&eacute; es la <em>COVID</em>
 persistente o Long <em>COVID</em>
? 12.03.2021 PDF accesible Siguiente Tel&eacute;fonos de informaci&oacute;n Enlaces a las Consejer&iacute;as de Sanidad de las Comunidades Aut&oacute;nomas Reuniones Comit&eacute; T&eacute;cnico <em>COVID</em>
-19 Materiales en otros idiomas English &ndash; Ingl&eacute;s Fran&ccedil;ais &ndash; Franc&eacute;s &Oslash;&sect;&Ugrave;&bdquo;&Oslash;&sup1;&Oslash;&plusmn;&Oslash;&uml;&Ugrave;Š&Oslash;&copy; &ndash; &Aacute;rabe Rom&acirc;n&Auml;&fnof; &ndash; Rumano &auml;&cedil;&shy;&aelig;&ndash;&Dagger; - Chino &ETH;&nbsp;&Ntilde;&fnof;&Ntilde;&Ntilde;&ETH;&ordm;&ETH;&cedil;&ETH;&sup1; &ndash; Ruso &ETH;&lsquo;&Ntilde;Š&ETH;&raquo;&ETH;&sup3;&ETH;&deg;&Ntilde;&euro;&Ntilde;&ETH;&ordm;&ETH;&cedil; &ndash; B&uacute;lgaro Deutsche &ndash; Alem&aacute;n &Oslash;&sect;&Oslash;&plusmn;&Oslash;&macr;&Ugrave;&circ; &ndash; Urdu Preguntas frecuentes Informaci&oacute;n sobre el coronavirus</p>
                                                                                        <p class="fecha">Fecha: 14/02/2023</p>
                                                                                         </li>



                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/spth.htm" target="_blank">[Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Viajes y <em>COVID</em>
-19]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/spth.htm" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/spth.htm
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p>Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Viajes y <em>COVID</em>
-19 6 Ministerio de Sanidad, Consumo y Bienestar Social RSS Facebook Twitter Youtube Suscripci&oacute;n a listas Webs tem&aacute;ticas &nbsp; Castellano Castellano Catal&agrave; Euskara Galego Valenci&agrave; English Fran&ccedil;ais Servicios al ciudadano Sede Electr&oacute;nica Buscar &nbsp; Organizaci&oacute;n Institucional Sanidad Servicios Sociales e Igualdad Proyectos normativos &nbsp; Inicio Sanidad Profesionales &nbsp; &nbsp; &nbsp; Men&uacute; Sanidad Ciudadanos Profesionales Biblioteca y</p>
                                                                                        <p class="fecha">Fecha: 14/02/2023</p>
                                                                                         </li>



                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/servCiudadanos/ayudas/pes/Actualizacion_PES_MDSA2030_2020.pdf" target="_blank">[Presentación de PowerPoint]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/servCiudadanos/ayudas/pes/Actualizacion_PES_MDSA2030_2020.pdf" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/servCiudadanos/ayudas/pes/ Actualizacion_PES_MDSA2030_2020.pdf
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p> MSCBS debe ser actualizado anualmente de acuerdo con la informaci&oacute;n relevante disponible. La actualizaci&oacute;n debe realizarse antes del 30 de abril de cada ejercicio presupuestario pero debido a la especial que estamos atravesando situaci&oacute;n derivada de la pandemia <em>COVID</em>
-19 la emisi&oacute;n de la actualizaci&oacute;n ha tenido lugar m&aacute;s tarde de lo fecha legalmente establecida. MINISTERIO DE DERECHOS SOCIALES Y AGENDA 2030 2 MINISTERIO DEL INTERIOR TRABAJO Y PRESTACIONES PENITENCIARIAS Los objetivos estrat&eacute;gicos</p>
                                                                                        <p class="fecha">Fecha: 19/04/2023</p>
                                                                                         </li>



                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/organizacion/recuTransResilencia/home.htm" target="_blank">[Ministerio de Sanidad - Ministerio - Plan de Recuperación, Transformación y Resiliencia]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/organizacion/recuTransResilencia/home.htm" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/organizacion/recuTransResilencia/home.htm
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p> del <em>COVID</em>
-19, as&iacute; como para preparar al pa&iacute;s para afrontar los retos del futuro. As&iacute;, el PRTR se articula en cuatro bloques: Espa&ntilde;a ecol&oacute;gica, transici&oacute;n digital, igualdad de g&eacute;nero y, por &uacute;ltimo, cohesi&oacute;n social y territorial . Estas cuatro directrices se reparten en 10 pol&iacute;ticas palanca, que a su vez se articulan en 30 componentes. Dentro de las diez pol&iacute;ticas palanca que conforman el PRTR, el Ministerio de Sanidad contribuye a &ldquo;Una administraci&oacute;n para el siglo XXI&rdquo;, &ldquo;Pacto por la ciencia y la</p>
                                                                                        <p class="fecha">Fecha: 26/03/2023</p>
                                                                                         </li>



                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID_persistente.pdf" target="_blank">[&iquest;Sabes qu&eacute; es la <em>COVID</em>
 persistente o &quot;Long <em>COVID</em>
&quot;?]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID_persistente.pdf" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/documentos/COVID_persistente.pdf
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p>&iquest;Sabes qu&eacute; es la <em>COVID</em>
 persistente o &quot;Long <em>COVID</em>
&quot;? &iquest;Sabes qu&eacute; es la <em>COVID</em>
 persistente o &ldquo;Long <em>COVID</em>
&rdquo;?  Es un s&iacute;ndrome que se caracteriza por la persistencia de s&iacute;ntomas de <em>COVID</em>
-19 semanas o meses despu&eacute;s de la infecci&oacute;n inicial, o por la aparici&oacute;n de los s&iacute;ntomas tras un tiempo sin ellos.  Su aparici&oacute;n no est&aacute; relacionada con la gravedad de la infecci&oacute;n inicial, por lo que puede afectar tanto a pacientes leves como a graves hospitalizados. Afecta a personas de cualquier edad, aunque parece</p>
                                                                                        <p class="fecha">Fecha: 03/03/2023</p>
                                                                                         </li>



                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID-19_Como_identificare_tus_contactos_estrechos.pdf" target="_blank">[Cómo identificar a tus contactos estrechos]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID-19_Como_identificare_tus_contactos_estrechos.pdf" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/documentos/COVID-19_Como_identificare_tus_contactos_estrechos.pdf
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p>C&oacute;mo identificar a tus contactos estrechos . C&Oacute;MO IDENTIFICAR A TUS CONTACTOS ESTRECHOS SI TIENES <em>COVID</em>
-19: Qu&eacute;date en casa y sigue las recomendaciones para el aislamiento domiciliario Haz saber a tus contactos estrechos que tienes <em>COVID</em>
-19, para que puedan ponerse en cuarentena en casa y contactar con su centro de salud &iquest;QUI&Eacute;NES SON TUS CONTACTOS ESTRECHOS? Son todas las personas que hayan estado en contacto contigo desde 48 horas antes de iniciar los s&iacute;ntomas o, si no tienes s&iacute;ntomas, de la</p>
                                                                                        <p class="fecha">Fecha: 03/03/2023</p>
                                                                                         </li>



                                                                                        <li>
                                                                                                <p>
                                                                                                        <a href="https://www.mscbs.gob.es/profesionales/saludPublica/prevPromocion/vacunaciones/covid19/vacunasCovid19.htm" target="_blank">[Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Salud p&uacute;blica - Prevenci&oacute;n de la salud - Vacunaciones - Programa vacunaci&oacute;n - Vacunas <em>COVID</em>
-19]</a>
                                                                                                </p>
                                                                                                <p class="sinMargen">
                                                                                                <a href="https://www.mscbs.gob.es/profesionales/saludPublica/prevPromocion/vacunaciones/covid19/vacunasCovid19.htm" target="_blank"><span class="negrita">
                                                                                                        https://www.mscbs.gob.es/profesionales/saludPublica/prevPromocion/ vacunaciones/covid19/vacunasCovid19.htm
                                                                                                </span></a>
                                                                                        </p>
                                                                                                <p>Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Salud p&uacute;blica - Prevenci&oacute;n de la salud - Vacunaciones - Programa vacunaci&oacute;n - Vacunas <em>COVID</em>
-19 6 Ministerio de Sanidad, Consumo y Bienestar Social RSS Facebook Twitter Youtube Suscripci&oacute;n a listas Webs tem&aacute;ticas &nbsp; Castellano Castellano Catal&agrave; Euskara Galego Valenci&agrave; English Fran&ccedil;ais Servicios al ciudadano Sede Electr&oacute;nica Buscar &nbsp; Organizaci&oacute;n Institucional Sanidad Servicios Sociales e Igualdad Proyectos normativos &nbsp; Inicio</p>
                                                                                        <p class="fecha">Fecha: 22/03/2023</p>
                                                                                         </li>


                                                                </ol>
                                                        </div><!-- alma_resultados -->
                                                    <div id="almapie">

                                                                                                <span class="page_select">[0]</span>

                                                                                                <a href="iniciar.do?search=COVID&amp;sIndex=10&amp;ser=MSSSI&amp;byDate=">[10]</a>

                                                                                                <a href="iniciar.do?search=COVID&amp;sIndex=20&amp;ser=MSSSI&amp;byDate=">[20]</a>

                                                                                                <a href="iniciar.do?search=COVID&amp;sIndex=30&amp;ser=MSSSI&amp;byDate=">[30]</a>

                                                                                                <a href="iniciar.do?search=COVID&amp;sIndex=40&amp;ser=MSSSI&amp;byDate=">[40]</a>

                                                        </div><!-- alma_pie -->
                                        </div><!-- alma_resultados -->


                </div>
        </form>
</section>

                        <a href="#" class="scrolltop"><i class="fa fa-angle-up" aria-hidden="true"><span class="sr-only">Subir</span></i></a>
                </div>
                <!--FIN CONTENIDO-->

                <!--PIE-->
                <footer class="container-fluid">
                        <div class="text-center"><p>© Ministerio de Sanidad</p></div>
                        <div class="text-center">
                                <ul>
                                        <li><a href="/guiaNav/home.htm">Guía de navegación</a></li>
                                        <li><a href="/avisoLegal/home.htm">Aviso Legal</a></li>
                                        <li><a href="/accesibilidad/home.htm">Accesibilidad</a></li>
                                        <li><a href="/mapaWeb/cargar.do">Mapa Web</a></li>
                                        <li><a href="/contactar/home.htm">Contactar</a></li>
                                </ul>
                        </div>
                </footer>

                <div id="barraUtil">
                        <div id="barraUtilidades">
                                <ul class="utilL">
                                        <li><a id="enlaceTraducir" target="_blank" href="http://translate.google.com/?hl=es" title="Se abrirá en una ventana nueva"><img src="../diseno/img/util_traducir.gif" alt=""/><span class="hidden-xs">Traducir</span></a></li>
                                        <li id="enlaceCompartir"><a target="_blank" href="../servicios/compartir.do" title="Se abrirá en una ventana nueva"><img src="../diseno/img/util_compartir.gif" alt=""/><span class="hidden-xs">Compartir</span></a></li>
                                        <li id="gustar"><a href="https://www.facebook.com" target="_blank" title="Se abrirá en una ventana nueva"><img src="../diseno/img/util_facebook.gif" alt="Compartir en facebook"/><span class="hidden-xs">Me gusta</span></a></li>
                                <li><a href="../servicios/descargarPdf.do" title="Se abrirá en una ventana nueva"><img src="../diseno/img/util_descargarPdf.gif" alt=""/><span class="hidden-xs">Descargar página actual en pdf</span></a></li>
                                </ul>
                                <ul class="utilR">
                                        <li><a href="../websTematicas/home.htm"><img src="../diseno/img/util_webTematicas.gif" alt=""/><span class="hidden-xs">Web temáticas</span></a></li>
                                        <li><a href="../servicios/gestionServicios.do"><img src="../diseno/img/util_personalizaServ.gif" alt=""/><span class="hidden-xs">Personaliza tus servicios</span></a></li>
                                        <li><a href="../rss/cargar.do"><img src="../diseno/img/util_rss.gif" alt=""/><span class="hidden-xs">RSS</span></a></li>
                                        <li class="sinborde" id="ocultarBarra" style="display:none;"><a href="javascript:void(0)">
                                        <img src="../diseno/img/util_ocultar.gif" alt="Barra expandida, pulse para ocultarla" title="Barra expandida, pulse para ocultarla"/></a></li>
                                </ul>
                        </div><!--barraUtilidades-->
                        <p style="display:none;" id='barraMostrar'><a href="javascript:void(0)"><img  src='../diseno/img/util_mostrar.gif' alt="Barra oculta, pulse para expandirla" title="Barra oculta, pulse para expandirla"/></a></p>
                </div><!--barraUtil-->

                <script src="../diseno/js/barraUtil.js"></script>
                <script src="../diseno/js/bootstrap.min.js"></script>
                <script src="../diseno/js/jasny-bootstrap.min.js"></script>
                <script src="../diseno/js/float-panel.js"></script>
                <script src="../diseno/js/main.js"></script>

        </body>
</html>

Beautiful Soup

Es otro parseador de páginas web muy utilizado por tener también una sintaxis sencilla y potente:

https://www.crummy.com/software/BeautifulSoup/bs4/doc/

Para instalarlo:

> conda install -c anaconda beautifulsoup4

Veamos un ejemplo sobre como podemos obtener de la página anterior los títulos de los documentos que ha devuelto la búsqueda que hemos hecho.

[21]:
from bs4 import BeautifulSoup

soup = BeautifulSoup(html, 'html.parser')
[22]:
print(soup.prettify())  # Simplemente nos permite ver el documento mejor formatado (indentaciones, espacios en blanco...)

<!DOCTYPE html>
<html lang="es" xml:lang="es" xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <!--  Establece las cabeceras -->
  <meta charset="utf-8"/>
  <meta content="IE=edge" http-equiv="X-UA-Compatible"/>
  <meta content="width=device-width, initial-scale=1" name="viewport"/>
  <title>
   Ministerio de Sanidad  - Buscador
  </title>
  <meta content="Ministerio de Sanidad, Buscador" name="Keywords">
   <meta content="Buscador" name="description">
    <!-- CSS -->
    <link href="../diseno/css/bootstrap.min.css" rel="stylesheet"/>
    <link href="../diseno/css/ministerio-sanidad.css" rel="stylesheet"/>
    <link href="../diseno/css/ministerio-sanidad-general.css" rel="stylesheet"/>
    <link href="../diseno/css/jasny-bootstrap.min.css" rel="stylesheet"/>
    <link href="../diseno/css/font-awesome.min.css" rel="stylesheet"/>
    <link href="../diseno/css/formularios.css" rel="stylesheet"/>
    <link href="../diseno/css/mapa-web.css" rel="stylesheet"/>
    <link href="../diseno/css/buscador-holmes.css" rel="stylesheet"/>
    <link href="../diseno/jscalendar/calendar-brown.css" rel="stylesheet"/>
    <noscript>
     <link href="css/nojs-estilos.css" rel="stylesheet"/>
    </noscript>
    <!--[if IE 8 ]> <html class="ie8" lang="es"> <![endif]-->
    <!--[if lt IE 9]><script src="js/html5shiv.min.js"></script><![endif]-->
    <script src="../diseno/js/jquery.min.js">
    </script>
    <script src="../diseno/js/utilidades.js">
    </script>
    <script src="../diseno/js/multidiomaEscenarioDinamico.js">
    </script>
   </meta>
  </meta>
 </head>
 <body>
  <header>
   <div class="float-panel" data-scroll="10" data-top="0">
    <!--Inicio CABECERA-->
    <div class="container-fluid cabecera">
     <div class="col-xs-12 col-sm-5 col-md-6 col-lg-6">
      <div class="logo-ministerio">
       <a href="/" title="Ministerio de Sanidad - Gobierno de España">
        <img alt="Ministerio de Sanidad - Gobierno de España" src="../diseno/img/logo_ministerio.jpg"/>
       </a>
      </div>
      <div class="nombre-ministerio">
       Ministerio
       <span>
        de Sanidad
       </span>
      </div>
     </div>
     <div class="col-sm-7 col-md-6 col-lg-6 hidden-xs text-right navCanalesIdioma">
      <ul class="siguenos">
       <li>
        <a href="../rss/cargar.do" title="RSS">
         <img alt="" src="../diseno/img/ico-rss.png">
          <span class="sr-only">
           RSS
          </span>
         </img>
        </a>
       </li>
       <li>
        <a href="https://www.facebook.com/MinSanidad/" target="_blank" title="Facebook. Se abrirá en una ventana nueva">
         <img alt="" src="../diseno/img/ico-facebook.png"/>
         <span class="sr-only">
          Facebook
         </span>
        </a>
       </li>
       <li>
        <a href="http://twitter.com/sanidadgob" target="_blank" title="Twitter. Se abrirá en una ventana nueva">
         <img alt="" src="../diseno/img/ico-twitter.png"/>
         <span class="sr-only">
          Twitter
         </span>
        </a>
       </li>
       <li>
        <a href="http://www.youtube.com/user/ministeriosyps" target="_blank" title="Youtube. Se abrirá en una ventana nueva">
         <img alt="" src="../diseno/img/ico-youtube.png"/>
         <span class="sr-only">
          Youtube
         </span>
        </a>
       </li>
       <li>
        <a href="https://listas.msssi.es" title="Suscripción a listas">
         <img alt="" src="../diseno/img/ico-suscripcionlistas.png"/>
         <span class="sr-only">
          Suscripción a listas
         </span>
        </a>
       </li>
       <li>
        <a href="../websTematicas/home.htm" title="Webs temáticas">
         <img alt="" src="../diseno/img/ico-webstematicas.png"/>
         <span class="sr-only">
          Webs temáticas
         </span>
        </a>
       </li>
      </ul>
      <ul class="nav navlist navbar-right navlistIdioma">
       <li class="language dropdown">
        <a aria-expanded="false" aria-haspopup="true" class="dropdown-toggle" data-toggle="dropdown" href="/home.htm" role="button">
         <span aria-hidden="true" class="glyphicon glyphicon-menu-down">
         </span>
         Castellano
        </a>
        <ul class="dropdown-menu">
         <li>
          <a href="/buscador/iniciar.do" title="Bienvenidos: acceso a la web en español">
           Castellano
          </a>
         </li>
         <li>
          <a href="/ca/buscador/iniciar.do" title="Benvinguts: Acceso a la web en catalán">
           <span lang="ca">
            Català
           </span>
          </a>
         </li>
         <li>
          <a href="/eu/buscador/iniciar.do" title="Ongi etorri: Acceso a la web en euskera">
           <span lang="eu">
            Euskara
           </span>
          </a>
         </li>
         <li>
          <a href="/gl/buscador/iniciar.do" title="Benvidos: Acceso a la web en gallego">
           <span lang="gl">
            Galego
           </span>
          </a>
         </li>
         <li>
          <a href="/va/buscador/iniciar.do" title="Benvinguts: Acceso a la web en valenciano">
           <span lang="ca-valencia">
            Valencià
           </span>
          </a>
         </li>
         <li>
          <a href="/en/buscador/iniciar.do" title="Welcome: Acceso a la web en inglés">
           <span lang="en">
            English
           </span>
          </a>
         </li>
         <li>
          <a href="/fr/buscador/iniciar.do" title="Bienvenue: Acceso a la web en francés">
           <span lang="fr">
            Français
           </span>
          </a>
         </li>
        </ul>
       </li>
      </ul>
     </div>
     <div class="col-sm-7 col-md-6 col-lg-6 hidden-xs text-right pull-right">
      <ul class="accesos">
       <li>
        <a href="../servCiudadanos/home.htm">
         Servicios al ciudadano
        </a>
       </li>
       <li>
        <a class="sede" href="https://sede.mscbs.gob.es/" target="_blank" title="Se abrirá en una ventana nueva">
         <strong>
          Sede
         </strong>
         Electrónica
        </a>
       </li>
      </ul>
      <!--Buscador-->
      <div class="buscador">
       <form action="../buscador/iniciar.do" class="search" method="post" role="search">
        <label class="sr-only" for="buscar">
         Buscar
        </label>
        <input id="buscar" name="search" type="text"/>
        <button type="submit">
         <span aria-hidden="true" class="glyphicon glyphicon-search">
         </span>
        </button>
       </form>
      </div>
      <!--Fin buscador-->
      <div class="servicios">
       <a href="../servCiudadanos/home.htm" title="Servicios al ciudadano">
        <img alt="Servicios al ciudadano" src="../diseno/img/ico-servicios.png"/>
       </a>
      </div>
     </div>
    </div>
    <!--Fin CABECERA-->
    <!--Menú PRINCIPAL-->
    <nav class="navbar navbar-default menu-categorias">
     <script src="../diseno/general/js/menuIzqDesplegable.js">
     </script>
     <!--Menú CATEGORIAS PRINCIPALES-->
     <div class="collapse navbar-collapse" id="categorias">
      <ul class="nav navbar-nav navlist" role="menu">
       <li class="dropdown">
        <a aria-expanded="false" aria-haspopup="true" class="dropdown-toggle" data-toggle="dropdown" href="../organizacion/portada/home.htm" role="button">
         <span>
          Organización Institucional
         </span>
        </a>
       </li>
       <li class="dropdown">
        <a aria-expanded="false" aria-haspopup="true" class="dropdown-toggle" data-toggle="dropdown" href="../sanidad/portada/home.htm" role="button">
         <span>
          Sanidad
         </span>
        </a>
       </li>
       <li class="dropdown">
        <a aria-expanded="false" aria-haspopup="true" class="dropdown-toggle" data-toggle="dropdown" href="../ssi/portada/home.htm" role="button">
         <span>
          Servicios Sociales e Igualdad
         </span>
        </a>
       </li>
       <li class="dropdown">
        <a aria-expanded="false" aria-haspopup="true" class="dropdown-toggle" data-toggle="dropdown" href="../normativa/proyectos/home.htm" role="button">
         <span>
          Proyectos normativos
         </span>
        </a>
       </li>
      </ul>
     </div>
     <script src="../diseno/general/js/categorias.js">
     </script>
    </nav>
    <!--FIN MENU CATEGORIAS PRINCIPALES-->
   </div>
  </header>
  <!-- INICIO CONTENIDO-->
  <div class="container-fluid contenido">
   <ol class="breadcrumb">
    <li>
     <a href="../home.htm">
      <i aria-hidden="true" class="fa fa-home">
      </i>
      <span class="sr-only">
       Inicio
      </span>
     </a>
    </li>
   </ol>
   <script src="../diseno/null/js/migasDinamico.js">
   </script>
   <script type="text/javascript">
    $(function() {
   $("#searchBsq").attr("aria-label", "Término o términos de la consulta");
});
   </script>
   <!--Contenido principal-->
   <section class="col-sm-8 col-md-12 informacion" role="main">
    <h1>
     Buscador
    </h1>
    <form action="iniciar.do" id="formBuscador" method="post">
     <div id="alma_capa">
      <ul class="lista_pes">
       <li>
        <a class="actual" href="iniciar.do">
         Búsqueda sencilla
        </a>
       </li>
       <li>
        <a href="iniciar.do?metodo=avanzada">
         Búsqueda avanzada
        </a>
       </li>
      </ul>
      <div id="alma_cab">
       <div class="centro_sencilla">
        <input class="campo_texto" id="searchBsq" name="search" style="width: 20em;" type="text" value="COVID"/>
        <input class="boton" type="submit" value="Buscar"/>
        <input checked="checked" id="msc" name="buscar" type="radio" value="msc"/>
        <span class="negrita">
         <label for="msc">
          En el Ministerio de Sanidad
         </label>
        </span>
        <input id="internet" name="buscar" type="radio" value="internet"/>
        <span class="negrita">
         <label for="internet">
          En Internet
         </label>
        </span>
       </div>
      </div>
      <!-- alma_cab -->
      <div id="alma_federados">
       <ul class="enLinea">
        <li>
         <a href="iniciar.do?search=COVID&amp;ser=INGESA&amp;byDate=">
          <span class="negrita">
           INGESA
          </span>
          (37)
         </a>
        </li>
        <li>
         <a href="iniciar.do?search=COVID&amp;ser=PNSD&amp;byDate=">
          <span class="negrita">
           PNSD
          </span>
          (393)
         </a>
        </li>
       </ul>
      </div>
      <div id="alma_resultados">
       <!-- Aqui van los resultados -->
       <div id="titulo_res">
        865 resultados en MSSSI para COVID
       </div>
       <div id="titulo_time">
        <!-- En segs-->
       </div>
       <div id="alma_orden">
        <span>
         [Orden por relevancia]
        </span>
        <a href="iniciar.do?search=COVID&amp;ser=MSSSI&amp;byDate=true">
         [Orden por fecha]
        </a>
       </div>
       <!-- alma_orden -->
       <div class="capaCentroBuscador">
        <ol start="1">
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/home.htm" target="_blank">
            [Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Enfermedad por nuevo coronavirus,
            <em>
             COVID
            </em>
            -19]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/home.htm" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/home.htm
            </span>
           </a>
          </p>
          <p>
           Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Enfermedad por nuevo coronavirus,
           <em>
            COVID
           </em>
           -19 6 Ministerio de Sanidad, Consumo y Bienestar Social RSS Facebook Twitter Youtube Suscripción a listas Webs temáticas   Castellano Castellano Català Euskara Galego Valencià English Français Servicios al ciudadano Sede Electrónica Buscar   Organización Institucional Sanidad Servicios Sociales e Igualdad Proyectos normativos   Inicio Sanidad Profesionales       Menú Sanidad Ciudadanos
          </p>
          <p class="fecha">
           Fecha: 20/04/2023
          </p>
         </li>
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ccd/ccaa.htm" target="_blank">
            [Ministerio de Sanidad - Profesionales - Mapa del estado de la expedición del Certificado
            <em>
             COVID
            </em>
            Digital de la UE en Comunidades Autónomas]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ccd/ccaa.htm" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/ccd/ccaa.htm
            </span>
           </a>
          </p>
          <p>
           Ministerio de Sanidad - Profesionales - Mapa del estado de la expedición del Certificado
           <em>
            COVID
           </em>
           Digital de la UE en Comunidades Autónomas 7 Ministerio de Sanidad RSS Facebook Twitter Youtube Suscripción a listas Webs temáticas   Castellano Castellano Català Euskara Galego Valencià English Français Servicios al ciudadano Sede Electrónica Buscar   Organización Institucional Sanidad Servicios Sociales e Igualdad Proyectos normativos   Inicio Sanidad Profesionales       Menú Sanidad Ciudadanos
          </p>
          <p class="fecha">
           Fecha: 01/04/2023
          </p>
         </li>
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/home.htm" target="_blank">
            [Ministerio de Sanidad]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/home.htm" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/home.htm
            </span>
           </a>
          </p>
          <p>
           Ministerio de Sanidad Ministerio de Sanidad   Castellano Castellano Català Euskara Galego Valencià English Français Servicios al ciudadano Sede Electrónica Buscar Organización Institucional Sanidad Servicios Sociales Proyectos normativos Agenda de actos Calendar Anterior Llama a la vida. Llama al 024 Información sobre la línea de atención a la conducta suicida. Información CORONAVIRUS Información sobre el brote de neumonía causada por un nuevo coronavirus (
           <em>
            COVID
           </em>
           -19). Bajas temperaturas
          </p>
          <p class="fecha">
           Fecha: 21/03/2023
          </p>
         </li>
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ciudadania.htm" target="_blank">
            [Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Información para la ciudadanía - Coronavirus]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ciudadania.htm" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/ciudadania.htm
            </span>
           </a>
          </p>
          <p>
           accesible Vacunación
           <em>
            COVID
           </em>
           -19 PDF accesible ¿Sabes qué es la
           <em>
            COVID
           </em>
           persistente o Long
           <em>
            COVID
           </em>
           ? 12.03.2021 PDF accesible Siguiente Teléfonos de información Enlaces a las Consejerías de Sanidad de las Comunidades Autónomas Reuniones Comité Técnico
           <em>
            COVID
           </em>
           -19 Materiales en otros idiomas English – Inglés Français – Francés العربية – Árabe Română – Rumano 中文 - Chino РуÑÑкий – Ruso БългарÑки – Búlgaro Deutsche – Alemán اردو – Urdu Preguntas frecuentes Información sobre el coronavirus
          </p>
          <p class="fecha">
           Fecha: 14/02/2023
          </p>
         </li>
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/spth.htm" target="_blank">
            [Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Viajes y
            <em>
             COVID
            </em>
            -19]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/spth.htm" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/spth.htm
            </span>
           </a>
          </p>
          <p>
           Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Viajes y
           <em>
            COVID
           </em>
           -19 6 Ministerio de Sanidad, Consumo y Bienestar Social RSS Facebook Twitter Youtube Suscripción a listas Webs temáticas   Castellano Castellano Català Euskara Galego Valencià English Français Servicios al ciudadano Sede Electrónica Buscar   Organización Institucional Sanidad Servicios Sociales e Igualdad Proyectos normativos   Inicio Sanidad Profesionales       Menú Sanidad Ciudadanos Profesionales Biblioteca y
          </p>
          <p class="fecha">
           Fecha: 14/02/2023
          </p>
         </li>
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/servCiudadanos/ayudas/pes/Actualizacion_PES_MDSA2030_2020.pdf" target="_blank">
            [Presentación de PowerPoint]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/servCiudadanos/ayudas/pes/Actualizacion_PES_MDSA2030_2020.pdf" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/servCiudadanos/ayudas/pes/ Actualizacion_PES_MDSA2030_2020.pdf
            </span>
           </a>
          </p>
          <p>
           MSCBS debe ser actualizado anualmente de acuerdo con la información relevante disponible. La actualización debe realizarse antes del 30 de abril de cada ejercicio presupuestario pero debido a la especial que estamos atravesando situación derivada de la pandemia
           <em>
            COVID
           </em>
           -19 la emisión de la actualización ha tenido lugar más tarde de lo fecha legalmente establecida. MINISTERIO DE DERECHOS SOCIALES Y AGENDA 2030 2 MINISTERIO DEL INTERIOR TRABAJO Y PRESTACIONES PENITENCIARIAS Los objetivos estratégicos
          </p>
          <p class="fecha">
           Fecha: 19/04/2023
          </p>
         </li>
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/organizacion/recuTransResilencia/home.htm" target="_blank">
            [Ministerio de Sanidad - Ministerio - Plan de Recuperación, Transformación y Resiliencia]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/organizacion/recuTransResilencia/home.htm" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/organizacion/recuTransResilencia/home.htm
            </span>
           </a>
          </p>
          <p>
           del
           <em>
            COVID
           </em>
           -19, así como para preparar al país para afrontar los retos del futuro. Así, el PRTR se articula en cuatro bloques: España ecológica, transición digital, igualdad de género y, por último, cohesión social y territorial . Estas cuatro directrices se reparten en 10 políticas palanca, que a su vez se articulan en 30 componentes. Dentro de las diez políticas palanca que conforman el PRTR, el Ministerio de Sanidad contribuye a “Una administración para el siglo XXI”, “Pacto por la ciencia y la
          </p>
          <p class="fecha">
           Fecha: 26/03/2023
          </p>
         </li>
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID_persistente.pdf" target="_blank">
            [¿Sabes qué es la
            <em>
             COVID
            </em>
            persistente o "Long
            <em>
             COVID
            </em>
            "?]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID_persistente.pdf" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/documentos/COVID_persistente.pdf
            </span>
           </a>
          </p>
          <p>
           ¿Sabes qué es la
           <em>
            COVID
           </em>
           persistente o "Long
           <em>
            COVID
           </em>
           "? ¿Sabes qué es la
           <em>
            COVID
           </em>
           persistente o “Long
           <em>
            COVID
           </em>
           ”?  Es un síndrome que se caracteriza por la persistencia de síntomas de
           <em>
            COVID
           </em>
           -19 semanas o meses después de la infección inicial, o por la aparición de los síntomas tras un tiempo sin ellos.  Su aparición no está relacionada con la gravedad de la infección inicial, por lo que puede afectar tanto a pacientes leves como a graves hospitalizados. Afecta a personas de cualquier edad, aunque parece
          </p>
          <p class="fecha">
           Fecha: 03/03/2023
          </p>
         </li>
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID-19_Como_identificare_tus_contactos_estrechos.pdf" target="_blank">
            [Cómo identificar a tus contactos estrechos]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID-19_Como_identificare_tus_contactos_estrechos.pdf" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/ alertasActual/nCov/documentos/COVID-19_Como_identificare_tus_contactos_estrechos.pdf
            </span>
           </a>
          </p>
          <p>
           Cómo identificar a tus contactos estrechos . CÓMO IDENTIFICAR A TUS CONTACTOS ESTRECHOS SI TIENES
           <em>
            COVID
           </em>
           -19: Quédate en casa y sigue las recomendaciones para el aislamiento domiciliario Haz saber a tus contactos estrechos que tienes
           <em>
            COVID
           </em>
           -19, para que puedan ponerse en cuarentena en casa y contactar con su centro de salud ¿QUIÉNES SON TUS CONTACTOS ESTRECHOS? Son todas las personas que hayan estado en contacto contigo desde 48 horas antes de iniciar los síntomas o, si no tienes síntomas, de la
          </p>
          <p class="fecha">
           Fecha: 03/03/2023
          </p>
         </li>
         <li>
          <p>
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/prevPromocion/vacunaciones/covid19/vacunasCovid19.htm" target="_blank">
            [Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Salud pública - Prevención de la salud - Vacunaciones - Programa vacunación - Vacunas
            <em>
             COVID
            </em>
            -19]
           </a>
          </p>
          <p class="sinMargen">
           <a href="https://www.mscbs.gob.es/profesionales/saludPublica/prevPromocion/vacunaciones/covid19/vacunasCovid19.htm" target="_blank">
            <span class="negrita">
             https://www.mscbs.gob.es/profesionales/saludPublica/prevPromocion/ vacunaciones/covid19/vacunasCovid19.htm
            </span>
           </a>
          </p>
          <p>
           Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Salud pública - Prevención de la salud - Vacunaciones - Programa vacunación - Vacunas
           <em>
            COVID
           </em>
           -19 6 Ministerio de Sanidad, Consumo y Bienestar Social RSS Facebook Twitter Youtube Suscripción a listas Webs temáticas   Castellano Castellano Català Euskara Galego Valencià English Français Servicios al ciudadano Sede Electrónica Buscar   Organización Institucional Sanidad Servicios Sociales e Igualdad Proyectos normativos   Inicio
          </p>
          <p class="fecha">
           Fecha: 22/03/2023
          </p>
         </li>
        </ol>
       </div>
       <!-- alma_resultados -->
       <div id="almapie">
        <span class="page_select">
         [0]
        </span>
        <a href="iniciar.do?search=COVID&amp;sIndex=10&amp;ser=MSSSI&amp;byDate=">
         [10]
        </a>
        <a href="iniciar.do?search=COVID&amp;sIndex=20&amp;ser=MSSSI&amp;byDate=">
         [20]
        </a>
        <a href="iniciar.do?search=COVID&amp;sIndex=30&amp;ser=MSSSI&amp;byDate=">
         [30]
        </a>
        <a href="iniciar.do?search=COVID&amp;sIndex=40&amp;ser=MSSSI&amp;byDate=">
         [40]
        </a>
       </div>
       <!-- alma_pie -->
      </div>
      <!-- alma_resultados -->
     </div>
    </form>
   </section>
   <a class="scrolltop" href="#">
    <i aria-hidden="true" class="fa fa-angle-up">
     <span class="sr-only">
      Subir
     </span>
    </i>
   </a>
  </div>
  <!--FIN CONTENIDO-->
  <!--PIE-->
  <footer class="container-fluid">
   <div class="text-center">
    <p>
     © Ministerio de Sanidad
    </p>
   </div>
   <div class="text-center">
    <ul>
     <li>
      <a href="/guiaNav/home.htm">
       Guía de navegación
      </a>
     </li>
     <li>
      <a href="/avisoLegal/home.htm">
       Aviso Legal
      </a>
     </li>
     <li>
      <a href="/accesibilidad/home.htm">
       Accesibilidad
      </a>
     </li>
     <li>
      <a href="/mapaWeb/cargar.do">
       Mapa Web
      </a>
     </li>
     <li>
      <a href="/contactar/home.htm">
       Contactar
      </a>
     </li>
    </ul>
   </div>
  </footer>
  <div id="barraUtil">
   <div id="barraUtilidades">
    <ul class="utilL">
     <li>
      <a href="http://translate.google.com/?hl=es" id="enlaceTraducir" target="_blank" title="Se abrirá en una ventana nueva">
       <img alt="" src="../diseno/img/util_traducir.gif"/>
       <span class="hidden-xs">
        Traducir
       </span>
      </a>
     </li>
     <li id="enlaceCompartir">
      <a href="../servicios/compartir.do" target="_blank" title="Se abrirá en una ventana nueva">
       <img alt="" src="../diseno/img/util_compartir.gif"/>
       <span class="hidden-xs">
        Compartir
       </span>
      </a>
     </li>
     <li id="gustar">
      <a href="https://www.facebook.com" target="_blank" title="Se abrirá en una ventana nueva">
       <img alt="Compartir en facebook" src="../diseno/img/util_facebook.gif"/>
       <span class="hidden-xs">
        Me gusta
       </span>
      </a>
     </li>
     <li>
      <a href="../servicios/descargarPdf.do" title="Se abrirá en una ventana nueva">
       <img alt="" src="../diseno/img/util_descargarPdf.gif"/>
       <span class="hidden-xs">
        Descargar página actual en pdf
       </span>
      </a>
     </li>
    </ul>
    <ul class="utilR">
     <li>
      <a href="../websTematicas/home.htm">
       <img alt="" src="../diseno/img/util_webTematicas.gif"/>
       <span class="hidden-xs">
        Web temáticas
       </span>
      </a>
     </li>
     <li>
      <a href="../servicios/gestionServicios.do">
       <img alt="" src="../diseno/img/util_personalizaServ.gif"/>
       <span class="hidden-xs">
        Personaliza tus servicios
       </span>
      </a>
     </li>
     <li>
      <a href="../rss/cargar.do">
       <img alt="" src="../diseno/img/util_rss.gif"/>
       <span class="hidden-xs">
        RSS
       </span>
      </a>
     </li>
     <li class="sinborde" id="ocultarBarra" style="display:none;">
      <a href="javascript:void(0)">
       <img alt="Barra expandida, pulse para ocultarla" src="../diseno/img/util_ocultar.gif" title="Barra expandida, pulse para ocultarla"/>
      </a>
     </li>
    </ul>
   </div>
   <!--barraUtilidades-->
   <p id="barraMostrar" style="display:none;">
    <a href="javascript:void(0)">
     <img alt="Barra oculta, pulse para expandirla" src="../diseno/img/util_mostrar.gif" title="Barra oculta, pulse para expandirla"/>
    </a>
   </p>
  </div>
  <!--barraUtil-->
  <script src="../diseno/js/barraUtil.js">
  </script>
  <script src="../diseno/js/bootstrap.min.js">
  </script>
  <script src="../diseno/js/jasny-bootstrap.min.js">
  </script>
  <script src="../diseno/js/float-panel.js">
  </script>
  <script src="../diseno/js/main.js">
  </script>
 </body>
</html>

[23]:
# Buscamos un <div class="capaCentroBuscador">

cuadroResultados = soup.find('div', {"class": "capaCentroBuscador"})

# De ese cuadro extraemos todos los <li> (cada uno tiene un resultado de la búsqueda)

resultados = cuadroResultados.find_all('li')

for r in resultados:
    # Dentro de cada <li> tenemos un <p> con un <a href="URL_DE_LA_NOTICIA">
    urlNoticia = r.p.a['href']

    tituloNoticia = r.p.a.text

    print(tituloNoticia)
    print(urlNoticia)
[Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Enfermedad por nuevo coronavirus, COVID
-19]
https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/home.htm
[Ministerio de Sanidad - Profesionales - Mapa del estado de la expedición del Certificado COVID
 Digital de la UE en Comunidades Autónomas]
https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ccd/ccaa.htm
[Ministerio de Sanidad]
https://www.mscbs.gob.es/home.htm
[Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Información para la ciudadanía - Coronavirus]
https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/ciudadania.htm
[Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Viajes y COVID
-19]
https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/spth.htm
[Presentación de PowerPoint]
https://www.mscbs.gob.es/servCiudadanos/ayudas/pes/Actualizacion_PES_MDSA2030_2020.pdf
[Ministerio de Sanidad - Ministerio - Plan de Recuperación, Transformación y Resiliencia]
https://www.mscbs.gob.es/organizacion/recuTransResilencia/home.htm
[¿Sabes qué es la COVID
 persistente o "Long COVID
"?]
https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID_persistente.pdf
[Cómo identificar a tus contactos estrechos]
https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID-19_Como_identificare_tus_contactos_estrechos.pdf
[Ministerio de Sanidad, Consumo y Bienestar Social - Profesionales - Salud pública - Prevención de la salud - Vacunaciones - Programa vacunación - Vacunas COVID
-19]
https://www.mscbs.gob.es/profesionales/saludPublica/prevPromocion/vacunaciones/covid19/vacunasCovid19.htm

Trabajando con otros formatos: CSV

Sacar información de un documento CSV en principio debe ser mucho más fácil ya que es un documento que normalmente separa los datos de una tabla usando el salto de línea \n como delimitador de fila y la coma ortográfica , como separador de columnas. Sin embargo el uso de " dentro de los campos puede dificultar la división de cada línea en distintos campos. Por suerte ya tenemos funciones preparadas que son capaces de entender este formato y convertirlo a un formato más amigable para Python.

[24]:
# Este ejemplo ha sido adaptado (Noviembre 2020) pues las columnas del fichero CSV
# han cambiado desde que se creó por primera vez

# Finalmente el fichero han dejado de servirlo en el mismo formato y la misma URL, con lo que usaremos
# una copia alojada en https://zerjio.com/temp/covid-19.csv

import requests
from io import StringIO
import csv
import matplotlib.pyplot as plt

#url="https://covid19.isciii.es/resources/serie_historica_acumulados.csv"   # Ha dejado de funcionar!!

url="https://zerjio.com/temp/covid-19.csv"

respuesta = requests.get(url)

contenidoCSV = respuesta.content.decode("iso-8859-1")   # Lo podemos codificar a una cadena "normal"

#print(contenidoCSV)

results = []
f = StringIO(contenidoCSV)
reader = csv.reader(f, delimiter=',')

fila = 0

for row in reader: # each row is a list
    if fila != 0:  # Ignoramos la primera fila porque es el nombre de las columnas
        results.append(row)

    fila += 1

results = results[:-8]  # Eliminamos las 8 ultimas filas porque es un texto que no tiene que ver con los datos


#print(results)

# Ahora que tenemos los datos vamos a hacer unas gráficas

# Recorremos los resultados creando estructuras de datos más comodas para los gráficos

ccaa = []
fechas = []
casosTotales = {}
pcr = {}
testAC = {}
hospitalizados = {}
uci = {}
fallecidos = {}


for r in results:
    comunidad = r[0]
    fecha = r[1]
    t = 0 if not r[2] else int(r[2])
    p = 0 if not r[3] else int(r[3])
    a = 0 if not r[4] else int(r[4])
    h = 0 if not r[5] else int(r[5])
    u = 0 if not r[6] else int(r[6])
    f = 0 if not r[7] else int(r[7])

    if not comunidad in ccaa:
        ccaa.append(comunidad)

        casosTotales[comunidad] = []
        pcr[comunidad] = []
        testAC[comunidad] = []
        hospitalizados[comunidad] = []
        uci[comunidad] = []
        fallecidos[comunidad] = []

    if not fecha in fechas:
        fechas.append(fecha)

    casosTotales[comunidad].append(t)
    pcr[comunidad].append(p)
    testAC[comunidad].append(a)
    hospitalizados[comunidad].append(h)
    uci[comunidad].append(u)
    fallecidos[comunidad].append(f)


variableGrafica = uci
variableTexto = "UCI"
comunidadResaltada = "AN"

#print(ccaa)
#print(fechas)
#print(fallecidos['AN'])

plt.style.use('seaborn-darkgrid')
my_dpi=96
plt.figure(figsize=(1000/my_dpi, 800/my_dpi), dpi=my_dpi)

for comunidad in ccaa:
   plt.plot(fechas, variableGrafica[comunidad], marker='', color='grey', linewidth=1, alpha=0.4)

plt.plot(fechas, variableGrafica[comunidadResaltada], marker='', color='orange', linewidth=4, alpha=0.7)


plt.title(variableTexto + " de " + comunidadResaltada + " frente a las demás CC.AA.", loc='left', fontsize=12, fontweight=0, color='orange')
plt.xlabel("Fecha")
plt.ylabel("")
plt.xticks(range(0, len(fechas), 1), fechas, rotation=90)


plt.show()
<ipython-input-24-09a042ff9893>:92: MatplotlibDeprecationWarning: The seaborn styles shipped by Matplotlib are deprecated since 3.6, as they no longer correspond to the styles shipped by seaborn. However, they will remain available as 'seaborn-v0_8-<style>'. Alternatively, directly use the seaborn API instead.
  plt.style.use('seaborn-darkgrid')
_images/D-1-webScraping_38_1.png

Trabajando con otros formatos: JSON

Este formato se ha popularizado sobretodo para APIs de Internet. Por ejemplo, el servicio gratuito Nominatim permite hacer búsquedas de latitudes y longitudes a partir de consultas de texto habituales. Por ejemplo, podemos encontrar la latitud y la longitud de un edificio o de una calle:

https://nominatim.org/release-docs/develop/

Tal y como se nos dice en la documentación, para hacer una consulta tenemos varios parámetros que le podemos pasar a la petición de la página:

https://nominatim.openstreetmaps.org/search

Por ahora los parámetros que nos interesan son q donde pondremos la descripción textual de la calle o edificio que buscamos y format con el valor json para que nos de la información en este formato.

[25]:
import requests
import json

url = "https://nominatim.openstreetmaps.org/search"

parametros = {
    'q': 'Facultad de Ciencias',
    'format': 'json'
}

respuesta = requests.get(url, parametros)

textoJson = respuesta.content.decode("utf-8")

#print(textoJson)

resultados = json.loads(textoJson)

for r in resultados:
    print("+ Nombre: " + r['display_name'])
    print("  + Coordenadas: " + "(" + r['lat'] + ", " + r['lon'] + ")")
    print("  + Tipo: " + r['type'])
    print("  + Clase: " + r['class'])
+ Nombre: Facultad de Ciencias, Plaza Nácere Hayek, San Honorato, San Cristóbal de La Laguna, Santa Cruz de Tenerife, Canarias, 38200, España
  + Coordenadas: (28.4818431, -16.3208461)
  + Tipo: university
  + Clase: amenity
+ Nombre: Facultad de Ciencias, 31, Bulevar Louis Pasteur, Teatinos-Universidad, Málaga, Málaga-Costa del Sol, Málaga, Andalucía, 29071, España
  + Coordenadas: (36.71533125, -4.472711280293506)
  + Tipo: house
  + Clase: place
+ Nombre: Facultad de Ciencias, 7, Paseo de Belén, San Pedro Regalado, Valladolid, Castilla y León, 47011, España
  + Coordenadas: (41.6635038, -4.705784440176778)
  + Tipo: university
  + Clase: building
+ Nombre: Facultad de Ciencias, Andalucia, La Floresta, Mariscal Sucre, Quito, Pichincha, 123456, Ecuador
  + Coordenadas: (-0.21095894999999998, -78.4899637931295)
  + Tipo: school
  + Clase: building
+ Nombre: Facultad de Ciencias, Plaza de Misael Bañuelos, Hospital del Rey, Burgos, Castilla y León, 09001, España
  + Coordenadas: (42.3426977, -3.7272172)
  + Tipo: restaurant
  + Clase: amenity
+ Nombre: Facultad de Ciencias, Plaza de Misael Bañuelos, Hospital del Rey, Burgos, Castilla y León, 09001, España
  + Coordenadas: (42.34271885, -3.726733639282485)
  + Tipo: university
  + Clase: building
+ Nombre: Facultad de Ciencias, Calle 36, Distrito IV, Alcalá de Henares, Área metropolitana de Madrid y Corredor del Henares, Comunidad de Madrid, 28805, España
  + Coordenadas: (40.5056615, -3.337250445381085)
  + Tipo: university
  + Clase: amenity
+ Nombre: Facultad de Ciencias, Avenida de los Castros, La Pereda, Cueto, Santander, Cantabria, 39005, España
  + Coordenadas: (43.4711753, -3.8004343)
  + Tipo: bicycle_rental
  + Clase: amenity
+ Nombre: Facultad de Ciencias, Avenida de los Castros, La Pereda, Cueto, Santander, Cantabria, 39005, España
  + Coordenadas: (43.471458299999995, -3.801531424547149)
  + Tipo: university
  + Clase: building
+ Nombre: Facultad de Ciencias, Avenida de los Castros, La Pereda, Cueto, Santander, Cantabria, 39005, España
  + Coordenadas: (43.471226, -3.80102)
  + Tipo: bicycle_parking
  + Clase: amenity

Obviamente Python tiene tambien mecanismos sencillos para convertir una estructura de datos como listas o diccionarios en una cadena de texto tipo JSON que podemos grabar en un archivo:

[26]:
import json

planetas = [
    {'nombre': 'Mercurio',
     'distanciaSol': 0.39,
     'terrestre': True
    },
    {'nombre': 'Venus',
     'distanciaSol': 0.72,
     'terrestre': True
    },
    {'nombre': 'Tierra',
     'distanciaSol':1.0,
     'terrestre': True
    },
    {'nombre': 'Marte',
     'distanciaSol': 1.52,
     'terrestre': True
    },
    {'nombre': 'Júpiter',
     'distanciaSol': 5.2,
     'terrestre': False
    },
    {'nombre': 'Saturno',
     'distanciaSol': 9.54,
     'terrestre': False
    },
    {'nombre': 'Urano',
     'distanciaSol': 19.19,
     'terrestre': False
    },
    {'nombre': 'Neptuno',
     'distanciaSol': 30.06,
     'terrestre': False
    },
]

print(planetas)

with open('salidas/planetas.json', 'w') as fichero:
    json.dump(planetas, fichero)
[{'nombre': 'Mercurio', 'distanciaSol': 0.39, 'terrestre': True}, {'nombre': 'Venus', 'distanciaSol': 0.72, 'terrestre': True}, {'nombre': 'Tierra', 'distanciaSol': 1.0, 'terrestre': True}, {'nombre': 'Marte', 'distanciaSol': 1.52, 'terrestre': True}, {'nombre': 'Júpiter', 'distanciaSol': 5.2, 'terrestre': False}, {'nombre': 'Saturno', 'distanciaSol': 9.54, 'terrestre': False}, {'nombre': 'Urano', 'distanciaSol': 19.19, 'terrestre': False}, {'nombre': 'Neptuno', 'distanciaSol': 30.06, 'terrestre': False}]

Posteriormente podemos cargar ese fichero también muy fácilmente:

[27]:
import json

with open('salidas/planetas.json') as fichero:
    planetas2 = json.load(fichero)

print(planetas2)
[{'nombre': 'Mercurio', 'distanciaSol': 0.39, 'terrestre': True}, {'nombre': 'Venus', 'distanciaSol': 0.72, 'terrestre': True}, {'nombre': 'Tierra', 'distanciaSol': 1.0, 'terrestre': True}, {'nombre': 'Marte', 'distanciaSol': 1.52, 'terrestre': True}, {'nombre': 'Júpiter', 'distanciaSol': 5.2, 'terrestre': False}, {'nombre': 'Saturno', 'distanciaSol': 9.54, 'terrestre': False}, {'nombre': 'Urano', 'distanciaSol': 19.19, 'terrestre': False}, {'nombre': 'Neptuno', 'distanciaSol': 30.06, 'terrestre': False}]

Ejercicio D.3: APOD, pero usando la API de la NASA

La NASA nos ofrece APIs que nos permiten acceder a mucha de la información que ofrecen. Normalmente hay que obtener una clave de la API para esos menesteres. En este caso para acceder a la API básica del APOD no hace falta nada, porque con esta url: https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY nos devuelve el JSON con la información pertinente del APOD.

Este método además de más simple es más robusto, puesto que la API probablemente no cambie en mucho tiempo, mientras que el HTML de la página del APOD si puede cambiar. Consigamos lo mismo que el ejercicio anterior del APOD pero a partir de la información que obtenemos de la API de la NASA.

[28]:
# Tu código aquí

Creando un Robot

Hasta ahora todo lo que hemos realizado es hacer llamadas HTTP a una única dirección y parsear su contenido. Pero sería mucho más útil si pudieramos hacer un robot algo más complejo que navegue por varias páginas de un sitio web extrayendo información.

La aproximación más fácil es cuando podemos determinar con claridad las páginas que queremos explorar. Por ejemplo en la página del Ministerio de Sanidad, Consumo y Bienestar Social, solo obtuvimos 10 resultados de la página de búsqueda, cuando había más resultados que se podían encontrar en páginas sucesivas. Mejoremos nuestro script convirtiéndolo en un robot que obtenga todos los resultados que se ofrezcan allí. Inspeccionando la página de resultados de la búsqueda comprobamos que podemos obtener otra página con diferentes añadiendo el parámetro sIndex.

[29]:
import requests
from bs4 import BeautifulSoup
import time

# Función adaptada del código anterior que buscaba solo en la primera página
def buscarDocsEnMSCBS(busqueda, sIndex=0):
    url = "https://www.mscbs.gob.es/buscador/iniciar.do"

    print("Buscando %s (%d)" %(busqueda, sIndex))

    parametros = {
        'search': busqueda,
        'buscar': 'msc',
        'sIndex': sIndex
    }

    respuesta = requests.post(url, parametros)

    html = respuesta.content.decode("utf-8")
    soup = BeautifulSoup(html, 'html.parser')

    # Averiguamos el número total de resultados sacándolo de <div id="titulo_res">
    div = soup.find(id="titulo_res")

    if (div == None):
        return {"nResultados": 0, "articulos": []}  # No hemos encontrado nada

    nResultados = int(div.text.split(" ")[0])

    # Como en el ejemplo anterior obtenemos los títulos y URLs de los resultados

    cuadroResultados = soup.find('div', {"class": "capaCentroBuscador"})

    resultados = cuadroResultados.find_all('li')

    articulos = []

    for r in resultados:
        # Dentro de cada <li> tenemos un <p> con un <a href="URL_DE_LA_NOTICIA">
        urlNoticia = r.p.a['href']

        tituloNoticia = r.p.a.text

        articulos.append({'titulo': tituloNoticia, 'url': urlNoticia})

    return {"nResultados": nResultados, "articulos": articulos}

print("Introduzca lo que quiera buscar en el MSCBS:")
busqueda = input()
print("")

resultados = buscarDocsEnMSCBS(busqueda)

nResultados = resultados['nResultados']
articulos = resultados['articulos']

# Ahora tenemos que hacer búsquedas sucesivas para seguir obteniendo de 10 en 10
# el resto de artículos

for sIndex in range(10, nResultados, 10):
    time.sleep(1.0)      # Somo educados: no mandamos sucesivas peticiones sin dejar un poco
                    # de tiempo (1 segundo)

    resultados = buscarDocsEnMSCBS(busqueda, sIndex)

    articulos += resultados['articulos']


print("Se han encontrado %d artículos:" %(nResultados))
indice = 1

for a in articulos:
    print(str(indice) + " - " + a['titulo'])
    print("   " + a['url'])
    indice += 1
Introduzca lo que quiera buscar en el MSCBS:
 omicron

Buscando omicron (0)
Buscando omicron (10)
Buscando omicron (20)
Buscando omicron (30)
Se han encontrado 31 artículos:
1 - [Ministerio de Sanidad, Consumo y Bienestar Social - Notas prensa - COVID-19]
   https://www.mscbs.gob.es/profesionales/cargarNotas.do
2 - [Notas de prensa]
   https://www.mscbs.gob.es/gabinete/notap_rss.do
3 - [Ministerio de Sanidad, Consumo y Bienestar Social - Gabinete de Prensa]
   https://www.mscbs.gob.es/gabinete/inicial.do
4 - [https://www.mscbs.gob.es/gabinetePrensa/ucrania/docs/Guia_de_actuacion_desplazados-Ucrania_21.03.2022.pdf]
   https://www.mscbs.gob.es/gabinetePrensa/ucrania/docs/Guia_de_actuacion_desplazados-Ucrania_21.03.2022.pdf
5 - [https://www.mscbs.gob.es/profesionales/saludPublica/prevPromocion/vacunaciones/covid19/docs/Recomendaciones_vacunacion_Otono_Invierno_Covid.pdf]
   https://www.mscbs.gob.es/profesionales/saludPublica/prevPromocion/vacunaciones/covid19/docs/Recomendaciones_vacunacion_Otono_Invierno_Covid.pdf
6 - [Ministerio de Sanidad - Notas prensa - COVID-19]
   https://www.mscbs.gob.es/profesionales/cargarNotas.do?time=1640991600000
7 - [Ministerio de Sanidad, Consumo y Bienestar Social - Gabinete de Prensa - Notas de Prensa]
   https://www.mscbs.gob.es/gabinete/notasPrensa.do?metodo=detalle&id=5603
8 - [https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID-19_Adaptacion_Estrategia_Vigilancia_Centros_Penitenciarios.pdf]
   https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/COVID-19_Adaptacion_Estrategia_Vigilancia_Centros_Penitenciarios.pdf
9 - [https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/19.04.2022_Mascarillas_interiores.pdf]
   https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/19.04.2022_Mascarillas_interiores.pdf
10 - [https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/Nueva_estrategia_vigilancia_y_control.pdf]
   https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov/documentos/Nueva_estrategia_vigilancia_y_control.pdf
11 - [https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/docs/20220617_InformeAlertaHepatitis.pdf]
   https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/docs/20220617_InformeAlertaHepatitis.pdf

Robots recursivos

En el ejemplo anterior teníamos muy claro la página que teníamos que visitar y el número de consultas que teníamos que hacer, pero en ocasiones querremos que nuestro robot rastree una web búscando el mismo la información que nos interese. Lo que se llama comúnmente una araña.

En el siguiente ejemplo vamos a hacer una araña que rastree un sitio de imágenes de fondo de pantalla y que vaya rastreándolo de manera recursiva, siguiendo todos los enlaces que vaya encontrando y descargando todas las imágenes que encuentre.

Pondremos ciertos límites a nuestro robot, como por ejemplo la profundidad máxima que permitiremos en las búsquedas (cuantos «saltos» como máximo podremos hacer desde la página inicial) y evitaremos también que nuestro robot se salga del sitio web que queremos explorar (siguiendo enlaces a páginas externas).

[30]:
from urllib.parse import urlparse    # Utilizadas para parsear más fácilmente las URLs que encontremos.
from urllib.parse import urlunparse  #
from urllib.parse import urljoin     #

import requests
from bs4 import BeautifulSoup
import time
import shutil                        # Una utilidad que permite copiar ficheros facilmente
import os
import errno


retardoEntrePeticiones = 0.5

# A esta función se le pasa una URL padre "completa" (con esquema y netloc:
# http://example.com...)
# y una "hija" que probablemente se habrá sacado de un <a href="">. La hija probablemente
# no tenga ni esquema ni netloc. Esta función se asegura de devolver la hija pero
# con el esquema y el netloc correspondientes. Si la hija ya tiene netloc la devolverá tal cual.
# Ejemplos:
#  Padre: "https://etsiit.ugr.es/pages/calendario_academico"
#  Hija: "horariosnuevocurso"
#  Devuelve: "https://etsiit.ugr.es/pages/calendario_academico/horariosnuevocurso"
#
#  Padre: "https://etsiit.ugr.es/pages/escuela"
#  Hija: "/pages/docencia"
#  Devuelve: https://etsiit.ugr.es/pages/docencia
#
#  Padre: "https://etsiit.ugr.es/pages/escuela"
#  Hija: "https://www.ugr.es"
#  Devuelve: "https://www.ugr.es"
#
# Ojo, no está probada con parámetros y similares

def getURLCompleta(padreURL, hijaURL):
    hija = urlparse(hijaURL)
    padre = urlparse(padreURL)

    netloc = hija.netloc
    scheme = hija.scheme
    path = hija.path

    if (netloc == ''):   # Si no tiene netloc, se lo añadimos
        netloc = padre.netloc
        scheme = padre.scheme

        path = urljoin(padre.path, hija.path)

    return urlunparse((scheme, netloc, path, hija.params, hija.query, hija.fragment))

# Devuelve True si las dos urls son del mismo servidor
def sonDelMismoServidor(url1, url2):
    url1p = urlparse(url1)
    url2p = urlparse(url2)

    server1 = url1p.netloc.split(".")[-2:]
    server2 = url2p.netloc.split(".")[-2:]

    if server1[0]==server2[0] and server1[1]==server2[1]:
        return True

    return False

# Crea todos los directorios necesarios en el path de un fichero
def crearDirectoriosDeUnPath(path):
    if not os.path.exists(os.path.dirname(path)):
        try:
            os.makedirs(os.path.dirname(path))
        except OSError as exc: # Guard against race condition
            if exc.errno != errno.EEXIST:
                raise




def descargaImagen(url, directorio):
    urlp = urlparse(url)

    nombreFichero = directorio + urlp.netloc + urlp.path
    #print(nombreFichero)

    if not os.path.exists(nombreFichero):  # Si ya existe es que lo descargamos en una ejecución anterior
        time.sleep(retardoEntrePeticiones)   # Dejamos un tiempo por educación

        crearDirectoriosDeUnPath(nombreFichero)

        print("Descargando imagen %s" % (url))

        resp = requests.get(url, stream=True)

        local_file = open(nombreFichero, 'wb')
    # Set decode_content value to True, otherwise the downloaded image file's size will be zero.
        resp.raw.decode_content = True
    # Copy the response stream raw data to local image file.
        shutil.copyfileobj(resp.raw, local_file)
    # Remove the image url response object.
        del resp

        return True

    return False

# Preguntamos la web a escrapear y otros parámetros
print("Inserte la URL a scrapear:")
url = input("Por ejemplo: https://wallpaperscraft.com/ ")
if url == '': url = "https://wallpaperscraft.com/"

print("\nMáximo nivel de profundidad en la búsqueda:")
profundidadMaxima = input("Por ejemplo: 1 ")
if profundidadMaxima == '':
    profundidadMaxima = 1
else:
    profundidadMaxima = int(profundidadMaxima)

print("\nDirectorio donde guardar las imágenes:")
directorioImagenes = input("Por ejemplo: salidas/imagenes/ ")
if directorioImagenes == '':
    directorioImagenes =  "salidas/imagenes/"


# Creamos arrays para las URLs que todavía tenemos que parsear y las que ya hemos visitado
# Las visitadas las apuntamos para evitar descargar dos veces la misma URL.
urlsPendientes = [{'url' : url, 'profundidad' : 1}]
urlsVisitadas = []
imagenesDescargadas = 0

# Mientras que queden URLs por parsear
while(len(urlsPendientes) > 0):
    x = urlsPendientes.pop()

    urlActual = x['url']
    profundidadActual = x['profundidad']

    urlsVisitadas.append(urlActual)  # Para no visitar de nuevo esta misma URL

    time.sleep(retardoEntrePeticiones)    # Dejamos un tiempo por educación

    print(f"Descargando página {urlActual}")

    respuesta = requests.get(urlActual)

    try:
        html = respuesta.content.decode("utf-8")
        soup = BeautifulSoup(html, 'html.parser')


    # Si no hemos llegado al nivel de profundidad maximo, buscamos todos los enlaces y los mete
        if profundidadActual < profundidadMaxima:
            enlaces = soup.findAll('a')

            for enlace in enlaces:
                urlEnlace = enlace['href']

                if urlEnlace != '':
                    urlEnlace = getURLCompleta(urlActual, urlEnlace)

                    if sonDelMismoServidor(urlActual, urlEnlace) and not urlEnlace in urlsVisitadas and not urlEnlace in urlsPendientes:
                        #print(urlEnlace)
                        urlsPendientes.append({'url': urlEnlace, 'profundidad': profundidadActual + 1})


    # Descargamos las imágenes
        imagenes = soup.findAll('img')

        imagenesEstaPagina = len(imagenes)
        i = 0

        for imagen in imagenes:
            i += 1
            urlImagen = imagen['src']

            if urlImagen != '':
                urlImagen = getURLCompleta(urlActual, urlImagen)

                if not urlImagen in urlsVisitadas:
                    if descargaImagen(urlImagen, directorioImagenes):
                        print(f"Quedan {len(urlsPendientes)} páginas, imagenes descargadas {imagenesDescargadas} ({i}/{imagenesEstaPagina})")

                        imagenesDescargadas += 1

                    urlsVisitadas.append(urlImagen)


        del respuesta
    except Exception as e:
        print(e)
        print("Problema parseando página ¿binario?")
Inserte la URL a scrapear:
Por ejemplo: https://wallpaperscraft.com/

Máximo nivel de profundidad en la búsqueda:
Por ejemplo: 1

Directorio donde guardar las imágenes:
Por ejemplo: salidas/imagenes/
Descargando página https://wallpaperscraft.com/
Descargando imagen https://wallpaperscraft.com/public/img/general/logo.png?v=97b14d53
Quedan 0 páginas, imagenes descargadas 0 (1/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/building_construction_construction_crane_908845_300x168.jpg
Quedan 0 páginas, imagenes descargadas 1 (2/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/pier_gazebo_sea_908840_300x168.jpg
Quedan 0 páginas, imagenes descargadas 2 (3/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/koala_wild_animal_leaves_908832_300x168.jpg
Quedan 0 páginas, imagenes descargadas 3 (4/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/sea_waves_splashes_908825_300x168.jpg
Quedan 0 páginas, imagenes descargadas 4 (5/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/coffee_beans_macro_908814_300x168.jpg
Quedan 0 páginas, imagenes descargadas 5 (6/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/house_windows_night_908802_300x168.jpg
Quedan 0 páginas, imagenes descargadas 6 (7/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/mallard_duck_duck_bird_908798_300x168.jpg
Quedan 0 páginas, imagenes descargadas 7 (8/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/church_tower_statue_908780_300x168.jpg
Quedan 0 páginas, imagenes descargadas 8 (9/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/planet_craters_spots_908776_300x168.jpg
Quedan 0 páginas, imagenes descargadas 9 (10/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/pattern_fractal_circles_908759_300x168.jpg
Quedan 0 páginas, imagenes descargadas 10 (11/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/tulips_buds_leaves_908734_300x168.jpg
Quedan 0 páginas, imagenes descargadas 11 (12/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/boats_ships_sails_908715_300x168.jpg
Quedan 0 páginas, imagenes descargadas 12 (13/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/bud_willow_fluff_908688_300x168.jpg
Quedan 0 páginas, imagenes descargadas 13 (14/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/road_lights_arch_908667_300x168.jpg
Quedan 0 páginas, imagenes descargadas 14 (15/29)
Descargando imagen https://images.wallpaperscraft.com/image/single/guinean_turaco_bird_green_908645_300x168.jpg
Quedan 0 páginas, imagenes descargadas 15 (16/29)
Descargando imagen https://wallpaperscraft.com/public/img/general/icons/tiktok.svg
Quedan 0 páginas, imagenes descargadas 16 (17/29)
Descargando imagen https://wallpaperscraft.com/public/img/general/icons/app.svg
Quedan 0 páginas, imagenes descargadas 17 (18/29)
Descargando imagen https://wallpaperscraft.com/public/img/general/icons/google.svg
Quedan 0 páginas, imagenes descargadas 18 (19/29)
Descargando imagen https://wallpaperscraft.com/public/img/general/icons/app-logo.svg
Quedan 0 páginas, imagenes descargadas 19 (21/29)
Descargando imagen https://wallpaperscraft.com/public/img/general/app1.png
Quedan 0 páginas, imagenes descargadas 20 (25/29)
Descargando imagen https://wallpaperscraft.com/public/img/general/app2.png
Quedan 0 páginas, imagenes descargadas 21 (26/29)
Descargando imagen https://wallpaperscraft.com/public/img/general/app3.png
Quedan 0 páginas, imagenes descargadas 22 (27/29)
Descargando imagen https://wallpaperscraft.com/public/img/general/app4.png
Quedan 0 páginas, imagenes descargadas 23 (28/29)
Descargando imagen https://wallpaperscraft.com/public/img/general/app5.png
Quedan 0 páginas, imagenes descargadas 24 (29/29)

Aclaraciones sobre los robots…

Los ejemplos que hemos hecho son básicos, pero un programa robot que rastree una página (o conjunto de páginas) grande debería incluir algunos mecanismos más para ahorrarnos tiempo en caso de fallo. Por ejemplo:

  • Debería guardar las páginas que ya se han visitado en una base de datos (o fichero) de tal manera que podamos continuar el programa en caso de interrupción

  • Podría programarse con varias hebras para hacer consultas simultáneas (ya que es probable que la mayoría del tiempo esté descargando datos, no extrayendo la información

  • Debería preocuparse mucho más por la «educación» a la hora de descargar: evitar la saturación del servidor que estamos escrapeando, hacer casos a los términos legales de la web revisadas y su fichero `robots.txt <https://developers.google.com/search/docs/advanced/robots/intro>`__, etc.


Ejercicio D.4: Bibliografía sobre los objetos del catálogo Messier

¿Podemos saber cuantos artículos científicos hay sobre cada uno de los objetos del catálogo Messier? Existe una base de datos llamada SIMBAD que permite obtener información bibliográfica sobre objetos celestes (galaxias, cúmulos, nebulosas…).

En este ejercicio se trata de hacer consultas automáticas a dicha base de datos para cada uno de los 110 objetos del catálogo Messier y graficarlos para hacernos una idea de que objeto de ese catálogo ha tenido más «interés» por parte de la comunidad científica y la evolución de dicho «interés».

Antes de ejercutar el código puede ser una buena idea ver el tipo de páginas de resultados que obtenemos cuando hacemos una de estas búsquedas, como por ejemplo los resultados para el objeto M 3:

http://simbad.u-strasbg.fr/simbad/sim-id?output.format=ASCII&Ident=M3


JavaScript, AJAX y páginas dinámicas

Hoy en día muchísimos servidores no mandan la información que tienen que mostrar las páginas directamente embebida en el código HTML, sino que usan JavaScript (y en muchas ocasiones AJAX) para mostrar esa información de manera dinámica. De hecho, en muchos casos esa información puede aparecer o desaparecer de la página según sea el comportamiento del usuario del navegador. Ejemplos sencillos son la página https://slashdot.org, o la página https://twitter.com/explore. Esas páginas van cargando su contenido según el usuario va haciendo scroll hacia abajo.

Una página que ya conocemos a la que le pasa esto es https://covid19.isciii.es/. Si con nuestro navegador (con JavaScript activado) veremos que los datos de la tabla que parseabamos en ejemplos anteriores no son iguales a los que nos muestra el navegador. De hecho con Python solo cargabamos unos datos estáticos de un día pasado (vete tu a saber cual), mientras que con el navegador los datos de esa tabla aparecen actualizados cada día. Para poder solucionar este problema deberíamos ser capaces de interpretar el código JavaScript de las páginas que escrapeamos, pero eso está totalmente fuera de la funcionalidad de requests, de Beautiful Soup, etc.

En este último ejemplo vamos a aprender a manejar la herramienta Selenium. Esta herramienta en origen estaba pensada para poder comprobar el funcionamiento de páginas web (cuando se están desarrollando) y poder implementar pruebas automáticas. Lo que hace esta herramienta es «encapsular» un navegador de nuestra elección (los más usuales están disponibles) de tal manera que podemos manejar dicho navegador programáticamente (a través de código) en vez de con movimientos de ratón, clicks y pulsaciones de teclas como estamos acostumbrados normalmente. Además Selenium nos permitirá acceder al DOM de los documentos que haya cargado el navegador (incluso después de que se haya ejecutado código JavaScript. A continuación veremos algunos ejemplos de como usar Selenium para hacer scraping de este tipo de páginas.

Instalación de Selenium y el driver de nuestro navegador

Para poder manejar Selenium y el driver para Firefox debemos instalar los siguientes paquetes: selenium y geckodriver. Con conda lo tenemos fácil:

> conda install -c conda-forge selenium
> conda install -c conda-forge geckodriver

Si queremos instalar un driver para otro navegador distinto podemos consultar como hacerlo en la documentación u otros tutoriales:

[31]:
from selenium import webdriver
from lxml import etree
from io import StringIO, BytesIO
import matplotlib.pyplot as plt

# Utilizamos Selenium para obtener el código de la página, pero en este caso la información
# que devolverá incluirá los cambios que se hayan realizado en el DOM tras la ejecución del código
# JavaScript de la página
driver = webdriver.Firefox()
driver.get("https://zerjio.com/temp/covid19.html")

html = driver.page_source

#driver.close()

# print(html)

# A partir de aquí el código es muy muy parecido al del segundo notebook

parserHTML = etree.HTMLParser()
tree = etree.parse(StringIO(html), parserHTML)

tablas = tree.xpath('//table')

tabla = tablas[1]

comunidades = []
casos = []

filas = tabla.xpath('tbody/tr')

for f in filas:
    comunidades.append(f[0].text)
    casos.append(int(f[1].text))

plt.bar(comunidades, casos)
plt.xticks(rotation=90)
plt.show()
_images/D-1-webScraping_54_0.png

Interaccionando con páginas web

Selenium nos permite interaccionar con las páginas web tal y como lo haríamos como si estuvieramos navegando manualmente con el navegador. Para ello tiene funciones que nos permiten seleccionar elementos de una página (o de otras partes del interfaz del navegador como pestañas, etc), mandar pulsaciones de teclas, clicks de ratón, etc.

[32]:
# Ejemplo de búsqueda en un formulario. Este ejemplo dejará de funcionar más pronto
# que tarde en cuanto AliExpress actualice su página web.
# Actualizado a Marzo de 2022

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Firefox()
driver.get("https://es.aliexpress.com/")

# Vamos a hacer una búsqueda dentro de AliExpress con el término: "mascarilla con filtro para virus"
# Podemos buscar los elementos con los que interaccionar. Las búsquedas se pueden hacer por id, name o
# incluso XPATH: https://selenium-python.readthedocs.io/locating-elements.html

time.sleep(1)  # No hace falta, pero para ir viendo poco a poco como se interacciona

# Primero cerramos el cuadro de suscribirse a notificaciones
# El botón de cerrrar el cuadro de suscripcion es la imagen con clase CSS "Sk1_X._1-SOk"

botonCerrarCuadroPublicidad = driver.find_element(By.CSS_SELECTOR, ".Sk1_X._1-SOk")
botonCerrarCuadroPublicidad.click()


driver.switch_to.default_content()  # Volvemos al frame principal

time.sleep(1)


#Buscamos el elemento del formulario de búsqueda (en este caso un <input> con id="search-key").

cuadroTexto = driver.find_element("id", "search-key")

cuadroTexto.clear()  # Borramos el posible contenido del cuadro de texto
time.sleep(1)
cuadroTexto.send_keys("mascarilla con filtro para virus")
time.sleep(1)

botonLupa = driver.find_element("xpath", "//input[@class='search-button']")
botonLupa.click()

[33]:
# Esperar hasta que aparezca un elemento en la página (por ejemplo si sabemos que
# va a crearse con JavaScript después de un tiempo)
# En este caso Twitter no muestra la barra de búsqueda hasta que pasa un tiempo


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.webdriver.common.keys import Keys

import time

driver = webdriver.Firefox()
driver.get("https://twitter.com/explore")

# Esperamos un máximo de 10 segundos hasta que aparezca el input deseado
try:
    busquedaTwitter = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.XPATH, "//input[@placeholder='Search Twitter']"))
    )
except:
    print("Error al localizar el elemento")
    driver.quit()


busquedaTwitter.clear()
busquedaTwitter.send_keys("Kim Jong Un")
busquedaTwitter.send_keys(Keys.RETURN)
[34]:
# Selenium tiene muchas más posibilidades: maximizar y minimizar ventanas, jugar con las distintas
# ventanas y pestañas, cuadros de dialogo, cookies, instalar addons, hacer capturas de pantalla, etc, etc

from selenium import webdriver
import time

driver = webdriver.Firefox()

time.sleep(1)

for ancho in range(400, 1000, 40):
    alto = ancho * 3 / 4
    driver.set_window_size(ancho, alto)
    driver.set_window_position(1000/2 - ancho/2, 800/2 - alto/2)
    time.sleep(.1)

driver.maximize_window()
time.sleep(1)

driver.get("https://selenium-python.readthedocs.io/")

driver.save_screenshot("salidas/pantallazo.png")

[34]:
True

Selenium IDE

Selenium también cuenta con una extensión tanto para Firefox como para Chrome:

Dicha extensión facilita la creación de scripts basados en Selenium. Es muy recomendable que la instales y compruebes sus posibilidades.