Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Obtención de datos del Artículo

19/38
Recursos

Aportes 81

Preguntas 13

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Cuando imprime la cantidad de articulos, esta mal escrito no es ‘article’, es ‘articles’ que es la lista de articulos.

print(len(articles))

Si a alguien le sale el error:
TypeError: object of type ‘NoneType’ has no len()
en la linea de codigo:

return result[0].text if len(result) else ''

Se soluciona cambiando la linea de código por:

return result[0].text if result != None else ''

El mejor profe del mundo

un error que tuve probando es que algunos url obtetinos eran tipo: ** https://www.viveusa.mx** por lo cual caía en la categoría de un root_path , razon por la cual daba error porque terminaba creando links que eran como https://www.eluniversal.com.mx//https://www.viveusa.mx entonces para solucionar esto cree un nuevo chequeo

is_other_host = re.compile(r'^https?://.+/$')

y para ver que tipo de link es quedo asi:

if is_well_formed_link.match(link):
    returnlink
  elif is_other_host.match(link):
    returnlink
  elif is_root_path.match(link):
    return'{}{}'.format(host, link)
  else:
    return'{host}/{uri}'.format(host=host, uri=link)

Para el Tiempo (82 articulos) ( Enlace articulo: ‘.title-container a’ Titulo: ‘.titulo-principal-bk h1’ y el Cuerpo: ‘.contenido’ )

Para Semana (75 articulos) ( Enlace articulo: ‘.article-h a’ Titulo: ‘.tittleArticuloOpinion’ y el Cuerpo: ‘#contentItem’ )

Me aparecieron tres errores más que agregue a las excepciones:
from urllib3.exceptions import NewConnectionError
from requests.exceptions import ConnectionError
from socket import gaierror

Para saber más de expresiones regulares con Python, Platzi tiene un articulo escrito sobre eso: https://platzi.com/blog/expresiones-regulares-python/

Igual acá está la documentación en el sitio de Python: https://docs.python.org/3/library/re.html

A quienes les aparezca este error:

return result[0].text if len(result) else ''
TypeError: object of type 'NoneType' has no len()

Lo pueden solucionar cambiando len(result) por result!=None

The special characters are:
    "."      Matches any character except a newline.
    "^"      Matches the start of the string.
    "$"      Matches the end of the string or just before the newline at
             the end of the string.
    "*"      Matches 0 or more (greedy) repetitions of the preceding RE.
             Greedy means that it will match as many repetitions as possible.
    "+"      Matches 1 or more (greedy) repetitions of the preceding RE.
    "?"      Matches 0 or 1 (greedy) of the preceding RE.
    *?,+?,?? Non-greedy versions of the previous three special characters.
    {m,n}    Matches from m to n repetitions of the preceding RE.
    {m,n}?   Non-greedy version of the above.
    "\\"     Either escapes special characters or signals a special sequence.
    []       Indicates a set of characters.
             A "^" as the first character indicates a complementing set.
    "|"      A|B, creates an RE that will match either A or B.
    (...)    Matches the RE inside the parentheses.
             The contents can be retrieved or matched later in the string.
    (?aiLmsux) Set the A, I, L, M, S, U, or X flag for the RE (see below).
    (?:...)  Non-grouping version of regular parentheses.
    (?P<name>...) The substring matched by the group is accessible by name.
    (?P=name)     Matches the text matched earlier by the group named name.
    (?#...)  A comment; ignored.
    (?=...)  Matches if ... matches next, but doesn't consume the string.
    (?!...)  Matches if ... doesn't match next.
    (?<=...) Matches if preceded by ... (must be fixed length).
    (?<!...) Matches if not preceded by ... (must be fixed length).
    (?(id/name)yes|no) Matches yes pattern if the group with id/name matched,
                       the (optional) no pattern otherwise.

Una lástima como está dictado este curso. Estoy haciendo la ruta de Data Science y está completamente desconectado de lo visto hasta ahora. Hice todos los cursos de Python hasta ahora en la ruta, además de que ya tenía experiencia con el lenguaje, y se hace muy muy díficil de seguir. MAL

Si el body se divide en distintos párrafos como scrappeo? solo me toma el primero

esta clase estuvo intensa

Ya logré rasgar la página del Universal. Y que pena decirlo pero ni la página del Universal, ni del Pais, ni de Semana ni del Tiempo he podido rasgarlas con el código que dan acá… es más ni los títulos pude obtenerlos. Los aportes de los compañeros la verdad ni para que se molestan en postiar cosas que no sirven. Y es de entender porque:
A) Un webscraper profesional de Periódicos en linea vale unos 3,000 USD
B) Aoresti no va a poner aquí gratis un scraper profesional porque estaría regalando su trabajo como desarrollador.
C) Platzi no lo va a exigir tampoco.
D) Si uno quiere que funcione el .YAML debe utilizar otra libreria porque Beautiful Soup 4 se revienta cuando las clases (class) tienen espacios. La clase que contiene el texto de los articulos del universal tiene espacios.
E) Logré arreglar el código con BS4 pero sin YAML y desarrollé una heurística bastante extensa y compleja para extraer el solo texto sin los marcadores HTML por que hasta los mismos articulos tienen anuncios, incrustados, publicidad, scripts, etc… Y eso toca borrarlo de la búsqueda para obtener el texto limpio.

Para todo esto me gasté unas 5 horas o más, y obvio no voy a regalar mi código!

Tengo el siguiente error:

result = self._select(self._queries('article_body'))
TypeError: 'dict' object is not callable

me parece extraño porque estoy haciendo el mismo código que el profesor, pero aun asi no funciona.

Alguien sabe por qué?

para el que le salga error al mostrar print(len(article)) es articles asi:
print(len(articles))

Una de las cosas se me han hecho mas duras es el tema de encontrar las clases en los html, y que toca estar revisando el cogido con compañeros pero he aprendido un montón. (cada clase es casi una hora, los 10 a 15 minutos mas un montón de tiempo buscando por que no me funciona)

Miren, la verdad si está algo complicado el curso. Hay que tener en cuenta que las estructuras de las paginas cambian, lo cual es bueno ya que nos hace buscar y deducir como vamos a realizar la busqueda ahora, y no solo copiar y pegar lo que escribio David. Sin embargo, está mas que claroque este curso no es para principiantes en python ni en la programación. Recomiendo tomar la ruta de fundamentos de programación…después de eso los 3 o 4 cursos que hay actualmente de python, incluendo el de POO y algoritmos, entonces sí regresar aquí 😄

está un poco complicado pero es porque manejo python básico. La cosa estará en práctica porque ahora puedo repetir lo que hizo el profesor pero hacer ese análisis en otra cosa me va a costar un millón.

Me funcionó perfecto!
Qué buen curso!!!

Quizás con esto se entienda un poco mejor lo que hicimos con re.compile()
|

|
Link al articulo: https://pynative.com/python-regex-compile/

Hay un absurdo error en la clase NewsPage que hace que “parezca que recuperan articulos” pero en realidad es el mismo texto de la página principal. Me tomó mas de una hora depurar ese error por que no se ve a simple vista. Para todos los que tienen articulos recuperados, revisen el contenido de los mismos!!!

Cuando se usan este tipo de tags para identificar el body de un articulo solo trae el primer parrafo porque, segun tengo entendido, select selecciona el primer bloque de html que cumple las condiciones, como deberiamos hacer para traer todo el body? gracias!

Así sale mi éxito:

INFO:__main__:Start fetching article at /opinion/gerardo-velazquez-de-leon/el-zoom-el-mejor-defensor-de-los-entrenadores
INFO:__main__:YAAASSSS!

Las expresiones regulares vistas en clase:

r'^https?://.+/.+$'?

r --> indica a python que es un string
^ --> inicio del patron
? --> caracter opcional
.+ --> una o más letras
$ —> final del patrón

He logrado hacer el ejercicio de insertarle los contenidos de las páginas de “El Pais” y de una página de sátira llamada “El Deforma”. SI les es útil, les comparto mi repositorio del curso

No entendí lo de expresiónes regulares

Esta clase tubo mucho nivel, y eso es bueno

Wow se ve genial cuando empieza a jalar los titulos y las url
🤯

El problema es que cuando no corre o no funciona la rutina cargada y el sistema de comentarios no ha tenido la solución previamente, sencillamente te estancas hasta quien sabe cuando responda el equipo de Platzi. Les recomiendo (a Platzi) que en sus videos muestren la sintaxis y funcionalidad de las expresiones. Video es solo el profesor escribiendo y escribiendo, hablando sin ejemplificar didacticamente. Solo se nota que transcribe un script previo y así cualquiera podría reproducir un video “enseñando”

Admito que en los videos que ya venían durando aproximadamente 10 minutos me tardaba 30 o 40 minutos en seguirle el paso. Ya veo que es solo el comienzo.

Hola, yo tengo una dudilla. Creo que lo entiendo todo razonablemente bien, pero he ido construyendo el scraper orientado a sacar la data de la PlayStore y no de los diarios y me encuentro con un problema:

Al ir a sacar la info del body, me encuentro con diferentes elementos html que corresponden al div donde se encuentra la descripción, pero al ir al utilizarlos en el config y correr el script me dice que no hay body.

Al revisar el código fuente de la PlayStore, vemos que el div más “pegado” a la descripción no tiene un class sino un jsname. Quizá el problema venga de por ahí, pero no tengo claro exactamente por qué. He probado poniendo ese jsname en el config, también otros class que hay en divs más “arriba” en el árbol del código. En ninguno de los casos lo he logrado.

A ver si alguien tiene alguna idea al respecto 😃

PD: Con los diarios y los parámetros mencionados en las clases sí funciona bien.

Les recomiendo que hagan un try catch en la funcion de visit, ya que el request puede tener datos comprimidos y el parse con beautifulsoup nos marcará error:

        try:
            response = requests.get(url)

            response.raise_for_status()

            self._html = BeautifulSoup(response.text, 'html.parser')
        except:
            self._html = BeautifulSoup('')

el codigo del profesor tiene un bug:

en el archivo main.py…en la funcion ‘_build_link’ todos los links se estan llendo al ‘else’ y no funcionan:

def _build_link(host, link):
    if is_well_formed_url.match(link):
        return link
    elif is_root_path.match(link):
        print('{host}{uri}'.format(host=host, uri=link))
        return '{host}{uri}'.format(host=host, uri=link)
    else:
        print('{host}/{uri}'.format(host=host, uri=link))
        return '{host}/{uri}'.format(host=host, uri=link)```

coloque un print en cada if statement para comprobar que estaba sucediendo con el codigo, en la foto se puede ver que: agrega la homepage + / + el link, por eso en el path quedan con doble //, y en el que es link queda repeetido https:xxxxhtpps:dfdfffd y por eso sale la excepcion.


![](![Captura de Pantalla 2020-05-29 a la(s) 11.04.49 p. m..png](https://static.platzi.com/media/user_upload/Captura%20de%20Pantalla%202020-05-29%20a%20la%28s%29%2011.04.49%20p.%C2%A0m.-67b3308b-a709-44e0-ba94-71b694a7b57e.jpg)


el problema esta en las expresiones regulares que utilizamos para identificar si era una url o un path, aca esta el cambio generado para poder que funcionen correctamente:



is_well_formed_url = re.compile(r"^(https?😕/)?([\da-z.-]+).([a-z.]{2,6})([/\w .-])/?$") #expresiones regulares y hace match eje: https://example.com/hello
is_root_path = re.compile(r"(/?[a-z0-9-.~%!$&’()*+,;[email protected]]+(/[a-z0-9-.~%!$&’()+,;=:@]+)/?|/)")```

y este es el resultado, ya funcionan bien los statements y puedes comprobar que cuando hay path no repite // como lo hacia antes:

INFO:main:Start fetching article at http://www.unionyucatan.mx/articulo/2020/05/05/espectaculos/survivor-mexico-conductor-y-todo-lo-que-debes-saber
INFO:main:Article fetched!
Survivor México, conductor y todo lo que debes saber
INFO:main:Start fetching article at https://www.clubeluniversal.mx/content/suscripciones
INFO:main:Article fetched!

INFO:main:Start fetching article at https://www.eluniversalvideo.com.mx/videos/v_NeA7EiUQghc.html
WARNING:main:Invalid article. there is no body.
INFO:main:Start fetching article at https://interactivo.eluniversal.com.mx/2020/ciencia-matematicas-covid-19/
WARNING:main:Invalid article. there is no body.
INFO:main:Start fetching article at /universal-deportes/mas-deportes/amlo-acusaciones-de-corrupcion-ana-guevara-son-politicas
http://www.eluniversal.com.mx/universal-deportes/mas-deportes/amlo-acusaciones-de-corrupcion-ana-guevara-son-politicas
INFO:main:Article fetched!
Acusaciones de corrupción a Ana Guevara son políticas: AMLO
INFO:main:Start fetching article at https://www.eluniversalvideo.com.mx/videos/v_er2il4TIgR4.html
WARNING:main:Invalid article. there is no body.
INFO:main:Start fetching article at https://www.eluniversal.com.mx/o

para las personas que quieran profundizar mas en el tema de expresiones regulares, les dejo esta pagina:

https://rico-schmidt.name/pymotw-3/re/index.html

Clase muy llena de información, pero muy clara. Gracias.

Les paso mi config con dos ejemplos que me funcionaron:

news_sites:
  lanacion:
    url: https://www.lanacion.com.ar
    queries:
      homepage_article_links: ".com-title a"
      article_body: "p.com-paragraph"
      article_title: "h1.com-title"
  elpais:
    url: https://elpais.com
    queries:
      homepage_article_links: "h2.c_t a"
      article_body: ".a_c p"
      article_title: "h1.a_t"

Dos errores que me ocurrieron:


Había un link de El Pais que tenía un espacio al comienzo, algo así:

' https://www.eluniversal.com.co/multimedia'

Esto provocaba que el código se cayera al no encontrar la página correcta. Se solucionó con un .strip() así

return set(link['href'].strip() for link in link_list)

El error anteriormente mencionado debió haberlo captura el try except, pero aún así se caia el código. Lo solucioné importando el HTTPError desde requests en lugar de urllib.error

from requests import HTTPError

Y bueno, un último error fué TypeError: object of type ‘NoneType’ has no len(), pero esto ya lo mencionaron en otro comentario. Yo lo solucioné así:

return None if result is None else result[0].text

Obtengo el siguiente error:

  File "C:\Users\Joel\Documents\web_scrapper_curso_data_eng\news_page_objects.py", line 48, in body
    result = self._select(self._queries['article_body'])
KeyError: 'article_body'```

ya intente cambiando el article_body por algun otro identificador, pero sigo sin poder resolverlo. Será que no estoy tomando el identificador correcto?

Me salía el error TypeError: _log() got an unexpected keyword argument ‘exc_inf’ al intentar obtener los artículos, solamente cambien en la funcion _fetch_article la linea logger.warning(‘Error while fechting the article’) eliminando el parámetro exc_inf=False

Este error me salió después de varios días probando es scrapper, supongo que fue un articulo que tenia un HTML diferente a los demás

Me gustaría conocer más a fondo cómo encontrar los queries de las páginas de internet, entiendo que todo el código se basa en estos y a medida que pasa el tiempo se van actualizando lo que genera errores en el web_scrapper

Así como David comenta, aquí se mezclan viejas practicas con nuevas practicas y el uso de conceptos mas avanzados de Python como los decoradores, No traten de entender todo a la primera, sigan con el curso y después vuelvan por el segundo intento o después de revisar mas recursos y practicar

Que clase tan increíble, recoge de muy buena manera los conceptos de las anteriores y conecta con el curso de Python, vamos por mas en esta carrera por el ML.

Si quieren agregarle colores a sus logs de python les comparto este enlace

cambiar colores en terminal python

Super cool!

web_scrapper\common.py", line 10, in config
with open(‘config.yaml’, mode=‘r’) as f:
FileNotFoundError: [Errno 2] No such file or directory: ‘config.yaml’

Unused variable ‘e’

Me siento como un hacker con este curso

buen dia, alguien que me pueda ayudar con el siguiente error

Traceback (most recent call last):
  File "main.py", line 75, in <module>
    _news_scraper(args.news_site)
  File "main.py", line 29, in _news_scraper
    article = _fetch_article(news_site_uid, host, link)
  File "main.py", line 49, in _fetch_article
    if article and not article.body:
  File "/home/gerardino/web_scrapper_curso_data_eng/news_page_objects.py", line 51, in body
    return result[0].text if len(result) else ''
TypeError: object of type 'NoneType' has no len()```

mi codigo esta en aqui 

https://drive.google.com/open?id=1M41wTo60NGEGQgVZzdaLTVMv_I76uksa```

Muy interesante , gracias por ensenarnos esos trucos para mejorar el codigo!

Estimados.
Tengo un problema al ejecutar el archivo main.py.
Por favor si alguien me puede ayudar.

INFO:root:Beginning scraper for http://www.eluniversal.com.mx
Traceback (most recent call last):
File “main.py”, line 89, in <module>
_news_scraper(args.news_site)
File “main.py”, line 24, in _news_scraper
homepage = news.HomePage(news_site_uid, host)
File “C:\Users\mmora\OneDrive\Carpeta Sincronizada\Platzi\Ingeniero de datos\platziData-5e1f90191f3a83497c2c7078a9acfeac61f24a24\web_scrapper_curso_data_eng\news_page_objects.py”, line 30, in init
super().init(news_site_uid, url)
TypeError: super() takes at least 1 argument (0 given)

Gracias

Hola, antes de terminar de correr todo me apareció lo siguiente, creo que no es un error como tal ¿Alguno me podría indicar qué es?

Excelente clase

Me pasa que el scraper funciona, pero hay algunas noticias que están en un formato diferente (en video) y la etiqueta para traerlas es diferente a las que son en texto. ¿cómo puedo agregar esa funcionalidad, que si son en texto busque una etiqueta para traer el título y si es en vídeo, busque otra diferente?

interesante!

Si no entendiste nada sobre las expresiones regulares aqui en platzi esta este excelente curso

https://platzi.com/clases/expresiones-regulares/

El paso a paso con el profesor, me resulto muy bien. He aquí el resultado:

Uff cada ves se ve que hay que repetir mucho para ir entendiendo…

las expresiones regulares las comprendo no son tan complejas pero me perdi un poco en la jerarquia del desarrollo de los objetos

import argparse
import logging
logging.basicConfig(level=logging.INFO)
import re

from requests.exceptions import HTTPError
from urllib3.exceptions import MaxRetryError

import news_page_objects as news
from common import config

is_well_formed_url= re.compile(r'^https?://.+/.+$') # i.e. https://www.somesite.com/something
is_root_path = re.compile(r'^/.+$') # i.e. /some-text
logger = logging.getLogger(__name__)


def _news_scraper(news_site_uid):
    host = config()['news_sites'][news_site_uid]['url']

    logging.info('Beginning scraper for {}'.format(host))
    logging.info('Finding links in homepage...')

    homepage = news.HomePage(news_site_uid, host)

    articles = []
    for link in homepage.article_links:
        article = _fetch_article(news_site_uid, host, link)

        if article:
            logger.info('Article fetched!')
            articles.append(article)
            print(article.title)

    print(len(articles))


def _fetch_article(news_site_uid, host, link):
    logger.info('Start fetching article at {}'.format(link))

    article = None
    try:
        article = news.ArticlePage(news_site_uid, _build_link(host, link))
    except (HTTPError, MaxRetryError) as e:
        logger.warn('Error while fetching article!', exc_info=False)

    if article and not article.body:
        logger.warn('Invalid article. There is no body.')
        return None

    return article


def _build_link(host, link):
    if is_well_formed_url.match(link):
        return link
    elif is_root_path.match(link):
        return '{host}{uri}'.format(host=host, uri=link)
    else:
        return '{host}/{uri}'.format(host=host, uri=link)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()

    news_site_choices = list(config()['news_sites'].keys())
    parser.add_argument('news_site',
                        help='The news site that you want to scrape',
                        type=str,
                        choices=news_site_choices)

    args = parser.parse_args()
    _news_scraper(args.news_site)```
import yaml

__config = None


def config():
    global __config
    if not __config:
         with open('config.yaml',mode = 'r') as f:
             __config =yaml.safe_load(f)

    return __config```

cual es el curso de Python que tanto habla David Aroesti ? me lo pueden compartir por favor

Logrado, aunque tuve que detenerme a leer muchas veces el código.
el reto es hacer un scrapper propio para otro tipo de información que no sea periódicos.

news_sites:
  eluniversal:
    url: http://www.eluniversal.com.mx
    queries:
      homepage_article_links: '.field-content a'
      article_body: '.field-name-body'
      article_title: '.pane-content h1'
  elpais:
    url: https://elpais.com
    queries:
      homepage_article_links: '.articulo-titulo a'
      article_body: '.articulo-cuerpo'
      article_title: '.articulo-titulo'```

me aparece el siguiente error cuando ejecuto “python main.py eluniversal”

print(len(article))
TypeError: object of type ‘ArticlePage’ has no len()

Paso 4: Obtención de datos del Artículo

scraping.py
1- se modifica _news_scraper, se inicializa la función _fetch_article en la variable article.

2- en la funcion _fetch_article se inicializa article en None. luego se realiza programacion defensiva, si todo va bien, se guarda en article.

3- se genera expresiones regulares, ‘import re’ con el fin de encontrar la estructura de los links.

4- se crea la funcion _build_link para armar los links y se ocupa el modulo re.

import argparse
import logging
import news_page_objects as news
import re                                                                   #se importa expresiones regulares
from requests.exceptions import HTTPError                                   #se importa el error
from config_yaml import config

logging.basicConfig(level=logging.INFO)                                     #se inicia en nivel basico para que informe durante el proceso
logger = logging.getLogger(__name__)                                        #se obtiene la referencia del logging con la variable globar que es __name__

def _news_scraper(news_site_uid):
    host = config()['news_sites'][news_site_uid]['url']                     #se llama al url especifico desde el argumento escojido, x ejemplo 'eluniversal' o 'elpais'
    logging.info('Comenzanco el scraping en: {}'.format(host))

    homepage = news.HomePage(news_site_uid, host)                           #se inicializa la clase HomePage, el host es el link que se encuentra en el url de config.yaml

    articles = []   #lista de articulos que se encontrarán

    for link in homepage.article_links:
        article = _fetch_article(news_site_uid, host, link)                 #se crea esto para ordenar en la funcion _fetch_article

        if article: #se ejecuta si es que hay un articulo
            logger.info('se encontro el articulo')
            articles.append(article)                        #se agregan los articulos a la lista de articulos
            print('titulo = {}'.format(article.title))      #printea el titulo del articulo,

    print('cantidad de articulos es de : {}'.format(len(article))) #se printea cantidad de articulos

def _fetch_article(news_site_uid, host, link):                              #funcion que ordena que elemento entregar y que no,
    logger.info('esta buscando el archivo en {}'.format(link))              #este logger explica  lo en que link está buscando
    article = None

    try:
        article = news.ArticlePage(news_site_uid, _build_link(host, link))
    except (HTTPError) as e: #programación defensiva, en caso de que este error ocurra.
        logger.warning('Error mientras se buscaba el articulo', exc_info= False)  # exc_info= False no muestra el error para no llenar la consola de basura

    if article and not article.body: #quiere decir si hay articulo, pero no tiene article.body se ejecuta
        logger.warning('articulo invalido, no hay cuerpo en el articulo')
        return None  #retorna nada porque no nos sirve

    return article

is_well_formed_link = re.compile(r'^https?://.+/.+$')   #esta es una expresión regular, ejemplo de la expresion regular https://example.com/hello
is_root_path = re.compile(r'^/.+$') #ejemplo, /some-text
# # ^ = quiere decir que es el inicio del patron, ? = la letra que está al lado izquierdo se encuentra o no,
#.+= expresa 1 o más letras, $ = finaliza

def _build_link(host, link):
    if is_well_formed_link.match(link):     #retorna el link, el martch es si lo encuentra
        return link
    elif is_root_path.match(link):          #construye el link, en caso que este mal formado
        return '{}/{}'.format(host, link)
    else:
        return '{}/{}'.format(host, link)

if __name__ == '__main__':
    parser = argparse.ArgumentParser('siempre debe iniciar el parse así')   #escribir dentro del parentesis es opcional
    news_site_choices = list(config()['news_sites'].keys())                 #se inicializa hacia la def config() que está dentro de config_yaml
    parser.add_argument('news_site'                                         # 'news_site' es el  nombre del argumento y de los comandos
                        ,help= 'Escoge el sitio el cual quieres buscar'     # este es el mensaje que saldrá
                        ,type= str                                          # este es el tipo
                        ,choices= news_site_choices)

    arg = parser.parse_args()                                               #esta linea es para pedir un objeto con el parse para luego poder llamarlo en _news_scraper(arg.news_site)
    _news_scraper(arg.news_site)

A mi la verdad no me funciona nada. Tal vez cambiaron los formatos de los enlaces y no puedo reconfigurar el YAML.

para lo que tienen windoes pueden bajar la consola cmder, el profe en una clase lo sugirio.

les dejo el link
https://cmder.net/

Como la mayoria, perdido, me imagino que somos los de MinTic… si me pongo a leer comentarios me tranquilizo, porque la verdad apenas arranque con el tema de la programacion, me parece fascinante todo lo que podemos lograr, y se que que hay muchisimo camino por recorrer para aprender las bases y asi poder entender con claridad lo que se esta programamdo.

Funcionó muy bien, sin embargo al llegar a uno de los artículos el programa se bloqueo al parecer por un tipo de error diferente al que se intentó atrapar en el algoritmo:

¡Está VIVOOOOO!!! Funciona mi webscraper 😃

Por si les paso; en mi scraper encontre algunos articulos en pdf, por ahora los estoy omitiendo, con la expresion

is_pdf = re.compile(r'.+pdf$')
#Y sobre la funcion _build_link agrege otra validacion
def _build_link(host,link):
    if is_pdf.match(link):
        return False
    elif is_well_formed_link.match(link):
        return link
    elif is_root_path.match(link):
        return '{}{}'.format(host,link)
    else:
        return '{}/{}'.format(host,link)

#y en la funcion de fetch_article una validacion del retorno _build_link
def _fetch_article(news_site_uid,host,link):
    logger.info("Start fetching article at {}".format(link))
    article = None
    try:
        if _build_link(host,link):
            article = news.ArticlePage(news_site_uid,_build_link(host,link))
        else:
            article = False

A quiénes les ha resultado muy complicado llegar hasta aquí o no les corre el código, tomen los cursos introductorios de Python, pero sobre todo Programación Orientada a Objetos, lo que es herencia y encapsulamiento.

Disfruten cada cosa nueva que aprendan, no se frustren.
Sean pacientes con su propio ritmo y amables con ustedes mismos. Agradecerse y felicitarse por cada cosa nueva que aprendan.

Se ve complicado, pero es entender el ¿por qué? lo que los va ayudar a ser mejores programadores.
¿por qué usa tantos archivos distintos si con jupyter era más fácil? ¿por qué usa self? ¿por qué las variables tiene un _variable? ¿qué son esas librerías? etc…

La respuesta a la mayoría de esas preguntas está en POO y leyendo la documentación.

Intenten entender cada línea de código. No copien y peguen solamente, entiendan cuál es el propósito.

Se logro el objetivo con ambos periodicos apesar de errores que tenia, hay que reconocer que el profesor tiene razon cuando dice ‘‘estamos para ayudarte’’, estamos los estudiantes porque no he visto el primer comentario que responda y hay muchas preguntas por resolver.

Esto está muy denso.

No se ha entendido mucho pero se logró completar el ejercicio.

Hola amigos me ayudan con los tags de el pais, cambiaron

Obtuve en estos pero no funcionan del todo.

elpais:
    url: http://elpais.com
    queries:
      homepage_article_links: '.headline a'
      article_body: '.description a'
      article_title: '.headline b'```

Para el caso de El País agregué un par de errores con noticias de otros sitios.

from urllib3.exceptions import DecodeError
from requests.exceptions import ContentDecodingError
except(HTTPError, MaxRetryError, DecodeError, ContentDecodingError) as e:
        logger.warning('Error while fetching the article', exc_info=False)
        return None

La configuración del archivo config.yaml con los datos a la fecha:

news_sites:
  eluniversal:
    url: https://www.eluniversal.com.mx
    queries:
      homepage_articles_links: '.titulo a'
      article_body: '.field-name-body'
      article_title: 'h1'
  elpais:
    url: https://elpais.com
    queries:
      homepage_articles_links: 'h2 > a'
      article_body: 'h1'
      article_title: '.a_b article_body > p'

Si a alguien le aparece el error: TypeError: object.init() takes exactly one argument (the instance to initialize). Debe eliminar la linea: super().init(news_site_uid, url)
Porque parece ya no hace falta en python para volver a llamar a la función

Haciendo la práctica me encontré con un problema puesto que en uno de mis host, para acceder a la información se le añadía un path y cuando quería acceder a la noticia, como la noticia tenia path relativos, se duplicaba el path del host con el path relativo. Para arreglar ese problema realice el siguiente método.

def _convert_url_host(host):
    host_parser = urlparse(host)
    if host_parser.path=='':
        return host
    else:
        return '{scheme}://{domain}'.format(scheme=host_parser.scheme,domain=host_parser.netloc)

Espero les sirva 😁

def _convert_url_host(host):
    host_parser = urlparse(host)
    if host_parser.path=='':
        return host
    else:
        return '{scheme}://{domain}'.format(scheme=host_parser.scheme,domain=host_parser.netloc)

La cantidad de tiempo que te ahorras en revisiones usando por ejemplo VSCode 😃

Sería bueno utilizar la programación asíncrona o multihilo para mejorar el rendimiento del scraper.