No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Convierte tus certificados en títulos universitarios en USA

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

16 Días
10 Hrs
28 Min
14 Seg

Extrayendo información

6/30
Recursos

Aportes 190

Preguntas 18

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

¡Reto resuelto! 😄

def notices(soup):
    """Función que recibe un objeto soup de una categoría determinada, 	 
    y devuelve los links de todas las noticias pertenecientes a ella"""

    featured_article = soup.find('div', attrs={
        'class': 'featured-article__container'
    })
    featured_article_link = [featured_article.find('a').get('href')]
    
    articles_h2 = soup.find('ul', attrs={
        'class': 'article-list'
    }).find_all('h2')
    articles_a = [article_h2.find('a', attrs= {
        'class': ''
    }) for article_h2 in articles_h2]
    
    articles_links = [article_a.get('href') for article_a in articles_a]
    
    return featured_article_link + articles_links

# Ejemplo de uso

university_section = BeautifulSoup(
    requests.get('https://www.pagina12.com.ar/secciones/universidad-diario').text,
    'lxml'
)

notices(university_section)

# Out
"""
['https://www.pagina12.com.ar/234308-con-record-de-inscriptos-en-la-unaj',
 'https://www.pagina12.com.ar/234323-agenda',
 'https://www.pagina12.com.ar/234350-el-costo-oculto-de-ir-a-estudiar',
 'https://www.pagina12.com.ar/233660-clonacion-bebes-de-diseno-etica-y-tecnologia',
 'https://www.pagina12.com.ar/233666-los-veinte-anos-de-agd',
 'https://www.pagina12.com.ar/233684-cada-vez-mas-personas-estudian-para-ser-docentes',
 'https://www.pagina12.com.ar/233038-el-aporte-de-las-universidades-al-plan-contra-el-hambre',
 'https://www.pagina12.com.ar/232346-la-conferencia-de-joanne-rosenthal-sangre-y-experiencia-posd',
 'https://www.pagina12.com.ar/232351-reflexiones-sobre-la-gratuidad-universitaria',
 'https://www.pagina12.com.ar/232362-a-70-anos-de-la-gratuidad-el-fin-de-la-universidad-elitista',
 'https://www.pagina12.com.ar/232394-festival-en-la-unaj',
 'https://www.pagina12.com.ar/232304-agenda',
 'https://www.pagina12.com.ar/232296-congreso-de-psicologia-el-sintoma-y-la-epoca',
 'https://www.pagina12.com.ar/231748-el-nuevo-local-de-la-lua',
 'https://www.pagina12.com.ar/231764-agenda']
"""

comparto mi codigo con una funciòn extra del curso de ingenieria de datos con python donde revisamos que la estructura de la url sea correcta, de la pagina en la cual me base, algunas url no estaban completas, les faltaba el host.

import requests
from bs4 import BeautifulSoup
import re

url = 'https://vandal.elespanol.com/noticias/videojuegos'

# estructura correcta de las url
is_well_formed_link = re.compile(r'^https?://.+/.+$')
is_root_path = re.compile(r'^/.+$')

#se hace el requerimiento a la pagina
vandal = requests.get(url)

#se hace el parse en formato lxml
s = BeautifulSoup(vandal.text, 'lxml')

#encontramos las secciones en el html en div con class:pildora
secciones = s.find_all('div', attrs={'class':'pildora'})

# con un list comprehension tomamos la etiqueta href
links_secciones = [seccion.a.get('href') for seccion in secciones]

# con la funcion build_link validamos que la estructura del link sea correcta

def build_link(url, link):
    if is_well_formed_link.match(link):
        return link
    elif is_root_path.match(link):
        return '{}{}'.format(url, link)
    else:
        return '{}/{}'.format(url, link)

links_secciones_modificadas = []

#validamos la estructura de cada link y la añadimos a link_secciones_modificadas
for link in links_secciones:
    links_secciones_modificadas.append(build_link(url, link))```

Reto hecho hoy Junio 19, 2021 (la estructura de la página ha cambiado un poco):

## Función que recibe una variable de BeautifulSoap
# y entrega un listado con los links de las noticias
# p.e. s_seccion  que contiene la sección el-país
def get_links_section (url, x):
    links_section = []
    
    # Primera noticia promocional
    #new_1 = x.find('div', attrs={'class':'article-item__content'}).a.get('href')
    new_1 = x.find('h2', attrs={'class': 'title-list'}).a.get('href')
    links_section.append(url + new_1)
    
    # Segunda y tercera noticia promocional
    new_2_3 = x.find('div', attrs={'class': 'articles-list is-grid-col2 grid-mobile-row'}).find_all('h3')
    
    links_section = links_section + [url + link.a.get('href') for link in new_2_3]
    
    # Listado de noticias
    news_list = x.find_all('div', attrs={'class': 'article-item__content-footer-wrapper gutter-small deco-bar-here-left'})
    links_section = links_section + [url + link.a.get('href') for link in news_list]
    
    return links_section

Un pequeno a[porte para quienes estan haciendo el curso al igual que yo en Octubre 2021, los ultimos ‘href’ del ejercicio traen la url cortada (no trae el dominio web ni el https, me imagino que es un tema de evitar el scrapping), entonces para tener un href completo hice lo siguiente, que espero les sirva de ejemplo:

hiper = article.a.get('href')
link_nuevo = 'https://www.pagina12.com.ar'+hiper
link_nuevo

Es muy importante que vayan revisando el tipo de sus variables o resultados con type para que no obtengan resultados inesperados tratanto de aplicar métodos a objetos que no corresponden.

Y cada vez que regreso a repasar el curso, la estructura de la página que se ve en los videos ya cambio.
Por tanto, debes tomar esto en cuenta al tomar este curso pues puede no ser igual a lo que muestra el profesor. Esto no te impide seguir con el curso, sólo adáptate al cambio.

Hoy, 22 de septiembre del 2020, la estructura es tal, que el reto se puede resolver con éste código y tomando en cuenta que las noticias están divididas como en tres tipos o secciones diferentes.
Espero les sirva

def fnt_obtener_links(s):
    top_article = s.find('div', attrs={'class': 'article-item__content'})
    top_article_link = top_article.a.get('href')
    
    lista_h3 = s.find_all('h3', attrs={'class': 'title-list'})
    lista_links_seccion1 = [link.a.get('href') for link in lista_h3]
    
    lista_h4 = s.find_all('h4', attrs={'class': 'title-list'})
    lista_links_seccion2 = [link.a.get('href') for link in lista_h4]
    
    lista_links = [top_article_link] + lista_links_seccion1 + lista_links_seccion2
    
    return lista_links

import requests
from bs4 import BeautifulSoup

# resultado_request = requests.get('https://www.pagina12.com.ar/secciones/el-pais')
resultado_request = requests.get('https://www.pagina12.com.ar/secciones/economia')

s = BeautifulSoup(resultado_request.text, 'lxml') 

fnt_obtener_links(s)

Resultado:

['https://www.pagina12.com.ar/293956-una-lista-de-los-que-se-quedan-a-invertir',
 'https://www.pagina12.com.ar/293943-tarifas-abusivas-en-buenos-aires',
 'https://www.pagina12.com.ar/293939-nos-recomponemos-de-un-industricidio',
 'https://www.pagina12.com.ar/293935-paritarias-a-fuego-lento',
 'https://www.pagina12.com.ar/293931-jubilados-septiembre-2020-quienes-cobran-este-miercoles-23',
 'https://www.pagina12.com.ar/293930-asignacion-universal-por-embarazo-septiembre-2020-quienes-co',
 'https://www.pagina12.com.ar/288274-asignacion-universal-por-embarazo-septiembre-2020-la-anses-f',
 'https://www.pagina12.com.ar/288267-jubilados-septiembre-2020-la-anses-fijo-las-fechas-de-cobro',
 'https://www.pagina12.com.ar/293894-el-pbi-cayo-el-19-1-en-segundo-trimestre-del-ano',
 'https://www.pagina12.com.ar/293879-martin-guzman-expone-el-proyecto-de-presupuesto-2021-ante-lo',
 'https://www.pagina12.com.ar/293853-como-son-los-nuevos-creditos-para-electrodomesticos-que-lanz']

Hola, dejo mi función para obtener los links del articulo promocionado y los demas artículos en una lista:

<code>
def getArticles(section_page):
    # Get featured article link
    featured_article = section_page.find('div', attrs={'class': 'featured-article-sections'})
    featured_art_link = featured_article.a.get('href')
    
    # Get articles links in the list
    articles = section_page.find('ul', attrs={'class': 'article-list'}).find_all('li')
    article_links = [article.a.get('href') for article in articles if article.a] # Only takes valid <a> tags
    
    # Insert featured article link into the list
    article_links.insert(0, featured_art_link)
    
    return article_links

Después de ver el video 3 veces por fin pude traer los links 😃

Realice el reto haciendo Scraping a la pagina de Xataka en la seccion analisis:

Codigo:

Output desde Anaconda Prompt:

Reto - 17-Oct-2020

import requests
from bs4 import BeautifulSoup

url = 'https://www.pagina12.com.ar/secciones/economia'

def getlinks(soup):
    news = soup.find_all('div', attrs = {'class':'article-item__content'})

    links_news = [new.a.get('href') for new in news]
    print(links_news)

def main():
    p12 = requests.get(url)
    soup  = BeautifulSoup(p12.text, 'lxml')
    getlinks(soup)

if __name__ == '__main__':
    main()

Por si alguien cierra el proyecto y al abrirlo no le funcionan los comandos, ha de saber que debe volver a correr todas las líneas del proyecto de nuevo.

Febrero 2022
Hola, si están haciendo el curso en estas fechas pueden haber notado que el formato de la página web de Página 12 cambio su estructuración. Para acomodarme a la nueva infraestructura diseñé la siguiente función que cumple el reto propuesto por el profesor, espero que sea de utilidad.

def get_sections_links(some_soup):
    base_url = 'https://www.pagina12.com.ar'
    section_links = []
    for i in range(2,5):
        hn = f'h{i}'
        nodes = some_soup.find_all(hn, attrs={'class':'title-list'})
        for node in nodes:
            temp_link = node.find('a').get('href')
            section_links.append(base_url + temp_link)
    return section_links

¡Hola comunidad de Platzi ! , aquí mi solución al reto, hoy es 14-07-2022 y la estructura de la página ha cambiado un poco, esta vez hay dos artículos promocionados. Espero que les sirva y si tienen una mejor forma de hacerlo les pido por favor que lo dejen en los comentarios 😄

def get_articles_links( s_input, url ):
    featured_articles = s_input.find_all('article', attrs={'class':'article-item--featured'})
    links = []
    for article in featured_articles:
        links.append(url + article.find('h3').find('a').get('href'))
    
    normal_articles = s_input.find_all('article', attrs={'class': 'article-item article-item--teaser'})
    for narticle in normal_articles:
        links.append(url + narticle.find('h4').find('a').get('href'))
    
    
    return links

Dejo mi aporte del reto actualizado a Mayo del 2023

links_reto = []
url = 'https://www.pagina12.com.ar/'
url_2 = 'https://www.pagina12.com.ar'

#Campo url:       link de la pagina principal
#Campo texto_url: link de alguna sección de la pagina web

def link_header(url,texto_url):

  #------------------------------------------------------------------------------------------------------------------

  #realizamos la solicitud para extraer la información del articulo principal de alguna sección de la pagina

  url_header = requests.get(texto_url)
  
  #Realizamos la codificación con BS4 para poder manipular la información con el parser 'lxml'

  parseo_reto = BeautifulSoup(url_header.text, 'lxml')

  ###############################################

  #LINK HEADER

  ###############################################


  #Buscamos el link articulo principal, en este caso se encuentra en la sección h2 con clase 'h1 title-list'

  link_tittle = parseo_reto.find('h2',attrs ={'class':'h1 title-list'}).find('a').get('href')

  #Le adicionamos un texto para que quede bien el link y lo apilamos en una lista vacia que definimos mas arriba 

  link_tittle_2 = url_2 + link_tittle

  links_reto.append(link_tittle_2)

  ###############################################

  #LINKS SEGUNDA SECCIÓN

  ###############################################

  segundo_links = parseo_reto.find('div', {'articles-list is-grid-col2 grid-mobile-row'}).find_all('h3')

  segundo_links_2 = [url_2 + links.find('a').get('href') for links in segundo_links]

  links_reto.append(segundo_links_2)

  ###############################################

  #LINKS LISTA TERCERA SECCIÓN

  ###############################################


  try:

    [links_reto.append(url_2 + links.find('a').get('href')) for links in tercero_links]


  except AttributeError:
    pass
  

  return links_reto


Solución al reto Abril de 2023

import requests
from bs4 import BeautifulSoup

url = 'https://www.pagina12.com.ar'

response = requests.get(url)
home = BeautifulSoup(response.text, 'lxml')
#obtenemos la sección Ul que contiene los links
links_parsed = home.find('ul', attrs = {'class':'horizontal-list main-sections hide-on-dropdown'}).find_all('li')

#obtenemos los links mediante List Comprehension
links = [link.a.get('href') for link in links_parsed]


#definimos la funcion del reto
#al día de hoy el periodico tiene sus noticias en tres containers
#se hayan todas y se almacenan en variables para luego hayar los enlaces

def get_links_seccion(seccion): 
        main_article = seccion.find('h2',attrs = {'class':'h1 title-list'})
        featured_articles = seccion.find_all('h3', attrs = {'class': 'h2 title-list featured-article'})
        inline_articles = seccion.find_all('h4', attrs = {'class': 'h2 is-display-inline title-list'})
        link_main = [url + main_article.a.get('href')]
        links_featured = [url + link.a.get('href') for link in featured_articles]
        links_inline= [url + link.a.get('href') for link in inline_articles]
        
        return(link_main + links_featured+ links_inline)

# ejemplo de uso:
seccion2 = links[2]
print(seccion2)
response2 = requests.get(seccion2)
seccion2_parse= BeautifulSoup(response2.text, 'lxml')
print(get_links_seccion(seccion2_parse))

#resultado: 

['https://www.pagina12.com.ar/541403-organizaciones-feministas-y-de-dd-hh-repudiaron-el-0800-vida', 'https://www.pagina12.com.ar/541380-robots-realizan-un-trasplante-pulmonar-casi-sin-intervencion', 'https://www.pagina12.com.ar/541369-bronca', 'https://www.pagina12.com.ar/541338-juicio-a-sebastian-villa-no-podia-hablar-con-mi-madre-por-lo', 'https://www.pagina12.com.ar/541331-la-impresionante-tromba-marina-que-sorprendio-a-los-vecinos-', 'https://www.pagina12.com.ar/541317-un-ex-gran-hermano-fue-apunalado-cuando-intentaba-agredir-a-', 'https://www.pagina12.com.ar/512622-llega-un-nuevo-eclipse-hibrido-que-es-y-cuando-podra-verse', 'https://www.pagina12.com.ar/541256-por-que-elon-musk-suspendio-el-lanzamiento-del-cohete-starsh', 'https://www.pagina12.com.ar/541245-italia-hallan-dos-toneladas-de-cocaina-flotando-cerca-de-la-', 'https://www.pagina12.com.ar/541235-scapolan-esta-tarde-el-jury-dara-su-veredicto-sobre-las-acus', 'https://www.pagina12.com.ar/541205-sebatian-villa-primera-audiencia-en-el-juicio-por-lesiones-y']
<def articles_links(seccion):

    """Get the page content"""
    pais = requests.get(seccion)

    """Convert into a BS """
    pais_ = BeautifulSoup(pais.text)

    """Extract general URL"""
    base = pais_.find('div',{'class':'logo-content'}).a.get('href')

    """Extract the articles  """

    a =pais_.find(
    'section', attrs = {
    'class': 'list-content'
    }
    ).find_all('div', attrs= {'class':'article-item__header'}
        )
    
    """get the links"""
    Links = [ seccionaa.get('href') for seccionaa in [ seccionaa.find('a') for seccionaa in a]]

    """Convert  links"""
    links_ = [base + link for link in Links]


    return links_
     > 

Si estan viendo esto en 2021 y ven que la pagina república cambio lo pueden hacer así:

Aquí está mi solución, lo hice para la sección de cultura:

import requests
from bs4 import BeautifulSoup
url = 'https://www.pagina12.com.ar/suplementos/cultura-y-espectaculos/21-06-2020'
culture = requests.get(url)


if __name__ == "__main__":
    soup = BeautifulSoup(culture.text, 'html.parser')
    featured_article_container = soup.find('ul', attrs={'class' : 'cover-articles'})
    featured_article_title = featured_article_container('h2')[0].text
    
    featured_article_url = featured_article_container('a')[0]['href']
    other_articles = soup.select('ul.article-list.article-list--other')
    articles = other_articles[0].find_all('li')
    print('--- * '  *10 )
    for article in articles:
        title = article.find('h2').text
        article_url = article.find('a')['href']
        print(f'New article {title}')
        print(f'Article at {article_url}')

    
    print(f'Featured article title: {featured_article_title}')
    print(f'Featured article url: {featured_article_url}')
    print('--- * '  *10 )

si les sale un error de NoneType como a mi que estube trayendo la sección de hoy en la ual esta no tenia href lo que se puede hacer es :
links_secciones = [seccion.a.get(‘href’) for seccion in secciones if seccion.a is not None]

Esa pagina a hoy (25 de enero de 2022) ya ha cambiado mucho y toca revisar muy bien que es lo que se hace para seguir el ritmo de la clase…Es hasta bueno porque se practica como que el doble.
LA parte mas dura va siendo sacar los links de cada noticia.

Vamos avanzando y conociendo algunas funciones y la manera de extraer cierta información, no obstante, aún no comprendo del todo el objetivo final de hacer esto. El ejemplo de extraer todas las noticias de un diario a qué me conduciría? una vez tengo toda la lista de noticias de qué sirve? Me parece que construir un ejemplo más práctico con un objetivo más claro desde el inicio, ayudaría mucho a la lógica del curso.

Hola, comparto mi función del reto.

En mi caso todas las noticias estaban en un tag ‘article’, dentro de este tag está otro ‘a’ que tiene el atributo ‘href’ con el link, además los links no contenían el host por lo que doy un formato final, en mi caso hice el primer método get con el host terminado en ‘/’ y es por eso que agrego un if.

Estoy feliz. hace un año no podía comprender esto y ahora creo que me desenvuelvo mejor, ojalá a alguien le sirva, no dejen de aprender 😄

def get_links_from_pagina12_seccion(bs4_object_page, host):
    # Obtiene todos los tag 'article'
    articles_list = bs4_object_page.find_all('article')
    # Obtiene todos los links contenidos en los tag 'a', dentro del atributo 'href'
    articles_link_list = [article.a.get('href') for article in articles_list]
    # Formatea 'host'
    if host[-1] == '/':
        host = host[:len(host) - 1]
    # Formatea lista de links
    articles_link_list = [link_article if link_article.startswith(host) else host + link_article for link_article in articles_link_list]
    return articles_link_list

Pruebas

articles_link_list_1 = get_links_from_pagina12_seccion(featured_article, p12.request.url)
articles_link_list_1

Resultados

[‘https://www.pagina12.com.ar/413385-de-almuerzos-con-empresarios-a-una-gira-por-estados-unidos’,
https://www.pagina12.com.ar/413377-escandalo-en-el-parlasur-malestar-en-la-ucr-con-el-operador-’,
https://www.pagina12.com.ar/413373-el-testimonio-de-una-tia-en-el-juicio-de-las-brigadas-que-bu’,
https://www.pagina12.com.ar/413370-organizaciones-sociales-y-sindicales-protestaron-por-la-suba’,
https://www.pagina12.com.ar/413350-contrapunto-entre-zabaleta-y-rodriguez-larreta-por-las-organ’,
https://www.pagina12.com.ar/413338-la-destruccion-judicial-del-estado-de-derecho’,
https://www.pagina12.com.ar/413317-diputados-trabaja-en-la-ley-de-alquileres’,
https://www.pagina12.com.ar/413304-alberto-fernandez-y-gabriel-boric-recorrieron-el-museo-sitio’,
https://www.pagina12.com.ar/413287-estados-unidos-saco-a-la-argentina-del-listado-de-paises-con’,
https://www.pagina12.com.ar/413270-la-interna-de-juntos-por-el-cambio-los-radicales-inician-el-’,
https://www.pagina12.com.ar/413267-el-discurso-de-campana-de-rodriguez-larreta-contra-los-pique’]

Comparto mi código al 25/04/022, le agregue la función del curso de David para determinar si el link esta bien formado, y una función para limpiar el titulo.
Me guie de este aporte de Daniel Sánchez para obtener todos los links: https://platzi.com/comentario/3575413/

import requests
from bs4 import BeautifulSoup
import lxml
import re

url = "https://www.pagina12.com.ar"

response = requests.get(url)

soup = BeautifulSoup(response.text, 'lxml')

# Every section is inside a il that is inside a ul
sections = soup.find('ul', attrs={'class':'horizontal-list main-sections hide-on-dropdown'}).find_all('li')

sections_links = [section.a.get('href') for section in sections_li]
#print(sections_links)

sections_titles = [section.a.text for section in sections_li]
#print(sections_titles)

def build_link(url, link):
    is_well_formed_link = re.compile(r'^https?://.+/.+$')
    is_root_path = re.compile(r'^/.+$')
    
    if is_well_formed_link.match(link):
        return link
    elif is_root_path.match(link):
        return '{}{}'.format(url, link)
    else:
        return '{}/{}'.format(url, link)

def clean_text(text):
    return text.replace("\xa0", "")


def get_response(url):
    return requests.get(url)


def get_soup(response):
    # Soup object
    soup = BeautifulSoup(response.text, 'lxml')
    
    return soup
def get_featured_article(soup):
    
    # Get the featured article of the section 
    # by searching the FIRST div with class = article-item__content
    featured_article = soup.find('div', attrs={'class':'article-item__content'})
    
    featured_article_link = None
    featured_article_title = None
    
    if featured_article:
        featured_article_link = build_link(url,featured_article.a.get('href'))
        featured_article_title = clean_text(featured_article.a.text)
    
    # Returns a tuple
    return (featured_article_link, featured_article_title)
def get_all_articles(soup):

    article_groups = soup.find_all('div', attrs={'class':'articles-list'})
    
    # List of the articles left
    articles_list_links = []
    articles_list_titles = []
    
    for group in article_groups:
        # All the article of the group
        articles = group.find_all('article', attrs={'class':'article-item'})
        # Loop for every article
        for article in articles:
            # The div that has the article inside
            div_of_article = article.find('div', attrs={'class':'article-item__content-footer-wrapper'})
            articles_list_links.append(build_link(url, div_of_article.a.get('href')))
            articles_list_titles.append(clean_text(div_of_article.a.text))
    
    # Returns a tuple
    return (articles_list_links, articles_list_titles)
for section in sections_links:
    # Get the response
    section_response = get_response(section)
    # Only execute if the response status code was ok
    if section_response.status_code == 200:
        section_soup = get_soup(section_response)
        featured_article_link, featured_article_title = get_featured_article(section_soup)
        articles_list_links, articles_list_titles = get_all_articles(section_soup)
        print("\n- Section: ", section)
        print("- Response: ", section_response.status_code)
        print("- Featured article link: ", featured_article_link)
        print("- Featured article title: ", featured_article_title)
        print("- Articles list links: ", articles_list_links)
        print("- Articles list titles: ", articles_list_titles)
    else:
        print(f"Error: {section_response.status_code}")

Estoy haciéndolo en 2022, y la página de El País cambió un poco, ahora los artículos están almacenados en etiquetas _article _ dentro de un div. Así lo resolví:

# Define una nueva funcion
def obtener_articulos(soup):
    # Crea un array para almacenar los enlaces
    lista_articulos = []
    
    # Busca el div que almacena el primer articulo
    articulo_principal = soup.find('div', attrs={'class':'article-item__content'})
    if articulo_principal:
        # Agrega el href del a que esta dentro del h2 
        lista_articulos.append(articulo_principal.h2.a.get('href'))
    
    # Busca todos los divs que almacenan articulos
    grupo_articulos = soup.find_all('div', attrs={'class':'articles-list'})
    # Hay dos grupos de articulos, dos semi featured, y el resto de los articulos
    # Esta iteracion hace un loop por los grupos de articulos
    for grupo in grupo_articulos:
        articulos = grupo.find_all('article', attrs={'class':'article-item'})
        
        # Y esta hace un loop entre los articulos dentro de ese grupo de articulos
        for articulo in articulos:
            # Busca el div que almacena el texto, aunque el href tambien se podria conseguir en la img
            contenedor = articulo.find('div', attrs={'class':'article-item__content-footer-wrapper'})
            # Asigna el enlace al array
            lista_articulos.append(contenedor.a.get('href'))
            
    return lista_articulos

Si obtienes el error:

AttributeError: 'NoneType' object has no attribute 'get'

se soluciona con:

links_secciones = [seccion.a.get('href') for seccion in secciones if seccion.a is not None]

Propongo esta forma según la sección el-país de pagina 12, en el listado inferior de noticias. He encontrado en las opciones tag.name y tag.get una ayuda grande.

[python]
from bs4 import BeautifulSoup
import requests

def obtener_notas(url):
    p12 = requests.get(url)
    s   = BeautifulSoup(p12.text, "lxml") # Text to xml

    articles_set = s.find(lambda tag:tag.name=="div" and tag.get("class")==["articles-list"] )
    print(articles_set.prettify())

    article_set = articles_set.find_all(lambda tag2:tag2.name=="div" and tag2.get("class")==["article-item__content"] )
    title_set   = [document.a.get_text() for document in article_set]
    return title_set

url = "enlace Pagina12 seccion el pais"

[/python]

Trae los títulos delas noticias de una sección a base de sus links

import requests
from bs4 import BeautifulSoup

url = 'https://www.pagina12.com.ar/secciones/economia'
url_page = 'https://www.pagina12.com.ar'

def getlinks(soup):
    news = soup.find_all('div', attrs = {'class':'article-item__content'})
    links_news = [url_page + new.a.get('href') for new in news]
    return(links_news)


def gettitles(links_news):
    titles = []
    for l in links_news:
        p12_titles = requests.get(l)
        soup  = BeautifulSoup(p12_titles.text, 'lxml')
        title = soup.find('div', attrs = {'class':'col 2-col'}).find('h1').get_text()
        titles.append(title)
    return(titles)
 
def main():
    p12 = requests.get(url)
    soup  = BeautifulSoup(p12.text, 'lxml')
    #getlinks(soup)
    print(gettitles(getlinks(soup)))
    


if __name__ == '__main__':
    main()

Reto realizado el 27/11/2021. Para esta fecha ya ha cambiado la estructura de la página, ahora se cuentan con tres secciones de donde se deben de extraer los links, por lo que el código me quedo de la siguiente manera:

faltante = 'https://www.pagina12.com.ar'
featured_article = s_seccion.find('div', attrs={'class':'article-item__content-footer-wrapper gutter-small deco-bar-here-left is-mobile-top'})
links_promocional = faltante + featured_article.a.get('href')

articles_grid = s_seccion.find('div', attrs={'class':'articles-list is-grid-col2 grid-mobile-row'}).find_all('h3')
article_g = articles_grid[0]
links_articles_grid = [faltante + article_g.a.get('href') for article_g in articles_grid]

articles_list = s_seccion.find('div', attrs={'class':'article-item__content-footer-wrapper gutter-small deco-bar-here-left'}).find_all('h4',attrs={'class':'is-display-inline title-list'})
article_l = articles_list[0]
links_articles_list = [faltante + article_l.a.get('href') for article_l in articles_list]

links_total = [links_promocional, links_articles_grid, links_articles_list]

print(links_total)

# Output:
# ['https://www.pagina12.com.ar/385504-capacitacion-por-malvinas',
#  ['https://www.pagina12.com.ar/385497-crimen-y-prejuicio',
#   'https://www.pagina12.com.ar/385502-alberto-fernandez-suma-a-la-cgt-al-peronismo-territorial'],
#  ['https://www.pagina12.com.ar/385498-juntos-por-el-cambio-entre-las-fisuras-internas-y-el-coquete',
#   'https://www.pagina12.com.ar/385496-causa-hotesur-las-razones-del-sobreseimiento-a-cristina-kirc',
#   'https://www.pagina12.com.ar/385495-imputaron-con-prision-preventiva-a-los-acusados-del-asesinat',
#   'https://www.pagina12.com.ar/385493-organizaciones-politicas-y-sociales-marcharon-a-plaza-de-may',
#   'https://www.pagina12.com.ar/385490-espionaje-a-familiares-del-ara-san-juan-el-juez-debe-definir',
#   'https://www.pagina12.com.ar/385483-cristina-fernandez-de-kirchner-se-reunio-con-las-senadoras-d',
#   'https://www.pagina12.com.ar/385451-cristina-kirchner-y-sus-hijos-fueron-sobreseidos-en-el-caso-',
#   'https://www.pagina12.com.ar/385430-secuestros-y-torturas-durante-la-dictadura-el-comodoro-retir']]

Tuve suerte de que todos los links desde h2 hasta h3 están en un div con la misma clase. Agregué la url antes de cada link por que son relativos y no eran usables

def _get_article_links(soup):
    section_divs = s_section.find_all('div', {'class': 'article-item__content'})
    links = [ url + link.a.get('href') for link in section_divs]
    return links

Julio 2021, esta es la función que utilicé para obtener los links de las noticias.

def get_links(url):
  response = requests.get(url)
  soup = BeautifulSoup(response.text, 'lxml')
  sections = soup.find('ul', class_='horizontal-list main-sections hide-on-dropdown').find_all('li')
  links_seccions = [section.a.get('href') for section in sections]

  news_links = []

  for link in links_seccions:
    sec = requests.get(link)
    sec_s = BeautifulSoup(sec.text, 'lxml')
    if 'cultura-y-espectaculos' in link:
      divs_news = sec_s.find_all('div', class_='article-box__container')
      a_links = [link.a.get('href') for link in divs_news]
      news_links.append(a_links)
    else:
      divs_news = sec_s.find_all('div', class_='article-item__content')
      a_links = [link.a.get('href') for link in divs_news]
      news_links.append(a_links)

  return news_links
def link_noticias_seccion(sopa):
    article_list = sopa.find_all('div', attrs={'class':'article-item__content'})
    noticias_seccion = ['https://www.pagina12.com.ar'+item.find('a').get('href') for item in article_list]
    return noticias_seccion
    

link_noticias_seccion(s)

['https://www.pagina12.com.ar/355936-murio-juan-carlos-del-bello',
 'https://www.pagina12.com.ar/352208-en-la-facultad-de-ciencias-sociales-se-impuso-la-lista-que-p',
 'https://www.pagina12.com.ar/352002-como-sera-la-nueva-normalidad-en-las-universidades',
 'https://www.pagina12.com.ar/351404-elecciones-en-la-uba-votan-los-profesores-de-ciencias-social',
 'https://www.pagina12.com.ar/351250-el-cbc-de-la-uba-tendra-una-sede-en-el-barrio-31',
 'https://www.pagina12.com.ar/351151-la-facultad-de-derecho-de-la-uba-habilita-la-presencialidad-',
 'https://www.pagina12.com.ar/350463-la-expansion-del-sistema-universitario-mas-estudiantes-mas-p',
 'https://www.pagina12.com.ar/346979-la-escuela-de-ciencias-informaticas-ofrece-cursos-virtuales-',
 'https://www.pagina12.com.ar/346987-cursos-abiertos-para-la-comunidad',
 'https://www.pagina12.com.ar/346733-un-futuro-de-aulas-hibridas-para-las-universidades',
 'https://www.pagina12.com.ar/345913-un-centro-universitario-de-la-innovacion-en-el-corazon-del-c']

Tal vez cambió la estructura, porque veo que solucionan el reto con un filtro ‘h2’. A día de hoy (22 de Diciembre de 2020), la primera noticia se puede capturar con filtro h2. Más específicamente, el Xpath a su link es

//div[@class="article-item__content"]/h2/a/@href

luego aparecen dos noticias más con tamaño h3 (misma estructura, pero cambia h2 por h3 en su Xpath); y una lista de noticias con tamaño h4.

Hice el ejercicio con la sección economy, y extraje su link aprovechando la variable links_secciones que definimos hace unas clases, aunque obviamente pueden extraer el link de forma manual también. Dejo un resumen de todo hasta acá:

url = 'https://www.pagina12.com.ar/'
p12 = requests.get(url)
s = BeautifulSoup(p12.text, 'lxml')
secciones = s.find('ul', attrs = {'class':'horizontal-list main-sections hide-on-dropdown'})('a')
links_secciones = [s.get('href') for s in secciones]
economy = BeautifulSoup(requests.get(links_secciones[1]).text, 'lxml')

Notar que para elegir cualquier otra sección, donde puse economy = BeautifulSoup(requests.get(links_secciones[1]).text, ‘lxml’), pueden reemplazar ese 1 por cualquier otro número entre 0 y len(links_secciones) - 1.

Y, finalmente, la función que cree para obtener los links es la siguiente:

def links_to_news(bs_obj):
    """Recibe un objeto BeautifulSoup (bs_obj) de una página y una sección específicas, 
    y retorna una lista con todos los links a las notas/noticias (news) de esa sección"""
# Primero filtramos el div con class='article-item__content'    
featured_articles = bs_obj.find_all('div', attrs={'class':'article-item__content'})
# Y después obtenemos los links para los casos h2, h3 y h4 mencionados anteriormente.
    h2_links = [f.h2.a.get('href') for f in featured_articles if f.h2]
    h3_links = [f.h3.a.get('href') for f in featured_articles if f.h3]
    h4_links = [f.h4.a.get('href') for f in featured_articles if f.h4]
    return h2_links + h3_links + h4_links

Pongo la actualización al 26 de Diciembre 2020 sobre el link para obtener el resto de las notas.

notas = soup.find_all('h4',attrs={'class':'title-list'})
link_notas = [nota.a.get('href') for nota in notas]

P.D. Me ayudé de otros aportes que se habían perdido en los comentarios más viejos.

def getArticleLinks(url):
    page = requests.get(url)
    soup = BeautifulSoup(page.text, 'lxml')
    featured_link = soup.find('div', attrs={'class', 'featured-article__image-container'}).a.get('href')
    articles = soup.find('ul', attrs={'class':'article-list'}).find_all('div', attrs={'class': 'article-box__container'})
    articles_links = [article.h2.a.get('href') for article in articles]
    articles_links.insert(0, featured_link)
    return articles_links

Aca mi solucion.
No recibe un objeto de bs4, pero con ella pude hacerle scraping a todas las secciones, lo cual me parecio mas util. 🚀

def get_links(seccion):
    sec = requests.get(seccion)
    s_sec = BeautifulSoup(sec.text,'lxml')
    featured_article = s_sec('div',attrs={'class':'featured-article__container'})[0].a.get('href')
    articles_list_raw = s_sec.find('ul',attrs={'class':'article-list'})
    articles_list = [articles_list_raw('a')[i].get('href') for i in range(len(articles_list_raw('a')))]
    articles_list.insert(0,featured_article)
    return articles_list

Hola. Comparto mi código en donde se le da la opción al usuario que seleccione de cual categoría quiere los links

import requests
import bs4 

def get_section(url):
    response = requests.get(url)
    response.encoding = 'utf-8'
    soup = bs4.BeautifulSoup(response.text, 'html.parser')

    sections = list(set(soup.select('ul.horizontal-list a')))
    print('Choose a section to scraper')
    for i, section in enumerate(sections):
        print(f'Section: {i}. {section.text}')
    
    user = int(input('Option: '))

    try:
        option = sections[user]['href']
    except IndexError:
        print('Invalid Option')
    
    link_generator(option)

def link_generator(option):
    response = requests.get(option)
    response.encoding = 'utf-8'
    soup = bs4.BeautifulSoup(response.text, 'html.parser')

    news = set(soup.select('.title-list a'))

    for item in news:
        print(f"Link: {item['href']}")
    

if __name__ == '__main__':
    print('-----Welcome to your link generator-----')
    url = 'https://www.pagina12.com.ar'
    get_section(url)```

Les dejo mi solución. Estoy abierto a cualquier sugerencia. Gracias =)

# Librerias a utilizar
import requests
from bs4 import BeautifulSoup

def saludar():
    """
    saludar es una función que devuelve un breve saludo
    """
    print('----- Bienvenido a Página 12 -----\n')


def elegir_seccion(url):
    """
    elegir_seccion es una función que toma la URL de Página 12 y devuelve
    el número de sección escogida y una lista de los links de las secciones 
    """
    # Solicitud a la URL
    pagina = requests.get(url)

    # Crear un objeto BeautifulSoup para analizar el contenido HTML
    soup = BeautifulSoup(pagina.text, 'lxml')

    # Buscar el elemento <ul>. Encontrar elementos <li>
    grupo_secciones = soup.find('ul', attrs={'class':'horizontal-list main-sections hide-on-dropdown'}).find_all('li')

    # Obtener nombres de las secciones y link de cada una de ellas
    secciones = [link.a.get_text() for link in grupo_secciones]
    links_secciones = [link.a.get('href') for link in grupo_secciones]

    # Mensaje para el usuario 
    print('Secciones de Página 12: ')

    # Listar cada sección con su número de orden respectivo
    # [print(f'{num+1}: {secciones[num]} -> {links_secciones[num]}') for num in range(0,len(secciones))]
    for num in range(0, len(secciones)):
        print(f'{num + 1}: {secciones[num]} -> {links_secciones[num]}')

    # Ingresar número e imprimir sección elegida
    while True:
        numero = input('\n----- ¿Qué sección deseas elegir? ')
        try:
            numero = int(numero)
            if 1 <= numero <= len(secciones):
                break
            else:
                print('----- Número fuera del rango permitido. Inténtelo de nuevo.')

        except ValueError:
            print('----- Entrada no válida')

    print(f'----- Usted eligió la sección: {secciones[numero-1]}\n')

    # Retornar número de sección escogida y URLs de cada sección
    return numero, links_secciones


def obtener_links_articulos(num, links_secciones, url):
    """
    obtener_links_articulos es una función que toma el número de sección 
    escogida, los links de las secciones y la URL de Página 12
    """
    
    # Solicitud a la URL de la sección
    pag_seccion = requests.get(links_secciones[num-1])

    # Crear un objeto BeautifulSoup para analizar el contenido HTML
    soup_seccion = BeautifulSoup(pag_seccion.text, 'lxml')

    # Buscar todos elementos <div> con la clase 'article-item__content'
    grupo_articulos = soup_seccion.find_all('div', attrs={'class':'article-item__content'})

    # Obtener las URLs y títulos de los artículos (find_all devuelve una lista)
    url_articulos = [url + str(link.find('a').get('href')) for link in grupo_articulos]
    texto_articulos = [link.find('a').get_text() for link in grupo_articulos]

    # Retornar los links de los articulos y su titulo correspondiente
    return url_articulos, texto_articulos


def articulos_pagina12(url):
    """
    articulos_pagina12 toma como entrada la URL de Página 12 y devuelve la
    sección de interés del usuario con sus articulos y sus respectivos enlaces 
    """
    # Saludar
    saludar()

    # Elegir seccion
    numero, links_secciones = elegir_seccion(url)

    # Listar los links de los articulos segun la seccion escogida
    url_articulos, texto_articulos = obtener_links_articulos(numero, links_secciones, url)

    # Imprimir cada articulo con su respectivo link
    [print(f'    > Noticia N°{num+1}: {texto_articulos[num]} -> {url_articulos[num]}') for num in range(len(url_articulos))]


url = 'https://www.pagina12.com.ar/'
articulos_pagina12(url)

Este es mi codigo no se si este correcto

15/08/23 sonaré muy tonto, pero después de un par de horas sin entender y buscando la respuesta, y a punto de tirar la toalla, decidí hacerlo por mi cuenta y obtuve la lista de las notas:
veo que la página ha cambiado mucho.
si alguien tiene una corrección o comentario, bienvenido.

<code> 
def obtener_notas(soup):
    lista_notas= []
    #obtengo el artículo promocionado
    featured_article=soup.find('div' , attrs={'class':'article-item__content'})
    if featured_article:
        lista_notas.append(featured_article.a.get('href'))
    #obtengo el listado de articulos
    article_list= soup.find('section', attrs= {'class':'list-content' })
    for article in article_list.find_all('article'):
        if article.a:
            lista_notas.append(article.a.get('href'))
    return lista_notas

resultado:
’/578674-ultraderecha-argentina-nuestro-pais-tambien-herido’,
’/578767-paso-2023-ganaron-el-fondo-milei-y-el-poder-judicial-pero-so’,
’/578960-las-claves-del-voto-milei-como-por-que-lo-que-viene’,
’/578827-de-el-dipy-a-luis-brandoni-y-marcela-pagano-como-le-fue-a-lo’,
’/578875-por-que-las-encuestas-no-previeron-el-voto-por-javier-milei-’,
’/578882-bullrich-el-dia-despues-bajo-la-sombra-de-javier-milei’,
’/578684-nueva-derecha-nuevos-desafios’,
’/578903-el-gobierno-notifico-el-tramite-iniciado-por-ana-maria-figue’,
’/578914-union-por-la-patria-redefine-la-estrategia-para-remontar-la-’,
’/578918-el-fiasco-de-la-boleta-electronica-en-caba-llego-a-la-justic’,
’/578921-el-juez-marcelo-martinez-de-giorgi-avanza-con-la-investigaci’,
’/578715-el-escrutinio-definitivo-de-las-paso-comienza-manana-por-la-’]

Al 07/08/2023 la página ha sufrido varias modificaciones, por lo que les dejo mi código con la forma que encontré para resolverlo

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

def scrape_noticias_pagina12():
    url = "https://www.pagina12.com.ar/secciones/el-pais"

    # Realizar la solicitud HTTP GET a la página web
    response = requests.get(url)

    # Comprobar que la solicitud se haya realizado correctamente
    if response.status_code != 200:
        print("Error al obtener la página:", response.status_code)
        return []

    # Analizar el contenido HTML con BeautifulSoup
    soup = BeautifulSoup(response.content, "html.parser")

    # Encontrar todos los elementos <h4> con la clase "title is-display-inline ff-20px-w700-ls-07"
    noticias = soup.find_all("h4", class_="title is-display-inline ff-20px-w700-ls-07")

    # Lista para almacenar los enlaces completos de las noticias
    enlaces_noticias = []

    # Obtener los enlaces completos y agregarlos a la lista
    for noticia in noticias:
        enlace = noticia.find("a").get("href")
        enlace_completo = urljoin(url, enlace)
        enlaces_noticias.append(enlace_completo)

    return enlaces_noticias

# Ejecutar la función y obtener los enlaces
enlaces_noticias_pagina12 = scrape_noticias_pagina12()

# Imprimir los enlaces completos de las noticias
for enlace in enlaces_noticias_pagina12:
    print(enlace)

20/Julio/2023

Mi función del reto

 def linksPage(soup, url):
	# cargar los links de la sección de la pagina
        featured_article = soup.find('div', attrs={'class':'article-item__content'}).find('h2', attrs={'class':'title ff-31px-w800-ls-1'})
        article_list1 = soup.find('section', attrs={'class':'list-content'}).find_all('h3', attrs={'class':'title ff-31px-w800-ls-1'})
        article_list2 = soup.find('section', attrs={'class':'list-content'}).find_all('h4', attrs={'title is-display-inline ff-22px-w800-ls-07'})
        
	
	# Inicializar los vectores que contienen los links
        links1 = article_list1[0]
        links2 = article_list2[0]

	
        # Extraer el href que contiene los links de cada articulo
        feature_link = featured_article.a.get('href')
        article_links1 = [links1.a.get('href') for links1 in article_list1]
        article_links2 = [links2.a.get('href') for links2 in article_list2]
        
	# expandir el primer vector para que se iguale a las dimensiones de los otros 
        feature_link = np.expand_dims(feature_link, 0)
                
	# concatenar todo en un solo vector
        matriz_links = np.concatenate([feature_link, article_links1, article_links2])
        
	# Inicializar la matriz de vectores
        total_links=[]
        
	# añadir la url base + el link href de cada articulo
        for link in matriz_links:
            total_links.append(url + link)
        
        return total_links


linksPage(s_seccion, links_secciones[0])

21 de Junio 2023, la gran mayoria de clases han cambiado, es dificil obtener un output con las instrucciones del curso. Pero el contenido es bueno.

Reto completado

<

def main(url):
    espe = requests.get(url)
    so = BeautifulSoup(espe.text, "lxml")
    cabeza = so.find('ul', attrs={"class": "article-list cover-articles"}).find_all('h2')
    atn = so.find('ul', attrs={"class": "article-list article-list--other"}).find_all('h2')
    new = atn+cabeza
    print(new)

    links = ["https://www.pagina12.com.ar/"  + new.a.get("href") for new in atn]
    return links

if __name__ == '__main__':
    url = "https://www.pagina12.com.ar/suplementos/cultura-y-espectaculos/19-05-2023"
    links = main(url)
    for i in links:
        print(i)
> 

Reto resuelto teniendo en cuenta la nueva estructura que tiene la pagina a esta fecha

def get_links():
    # URL de la página de la que se quieren extraer los links
    url = 'https://www.pagina12.com.ar/secciones/economia'

    # Hacer la petición GET a la página
    response = requests.get(url)

    # Verificar que la petición fue exitosa (código 200)
    if response.status_code == 200:
        # Obtener el contenido HTML de la página
        html = response.content

        # Crear un objeto BeautifulSoup (bs) a partir del contenido HTML
        soup = bs(html, 'html.parser')

        # Encontrar todos los elementos 'a' que estén dentro de una etiqueta 'h2' 
        # con la clase 'article-title'
        links = soup.find_all('h2', class_='h1 title-list') + soup.find_all('h3', class_='h2 title-list featured-article') + soup.find_all('h4', class_='h2 is-display-inline title-list')
            

        # Iterar sobre la lista de links encontrados para obtener su href
        for link in links:
            # Obtener el href del link
            href = link.find('a')['href']

            # Imprimir el href del link
            print(href)
    else:
        # Si la petición no fue exitosa, imprimir el código de estado recibido
        print(f'Status code error: {response.status_code}')

espero les sirva

def links_seccion(elemento):
    link = elemento.get("href")
    req = requests.get(link)
    print(f"status code = {req.status_code}")
    sopa = BeautifulSoup(req.text)
    extrae=sopa.find_all("article")
    extrae = [elemento.find("a") for elemento in extrae]
    extrae = [elemento.get("href") for elemento in extrae]
    return extrae 

Para los que están haciendo el curso actualmente, la estructura de la página cambió un poco desde el inicio del curso, ahora hay un titular importante, dos títulos secundarios y luego una lista de 8 títulos menores.
El ejercicio propuesto en esta clase me quedó d ela siguiente manera, solo que modifique un poco la consigna, en vez de hacer una funcion que recibe un objeto bs4, creé una función donde directamente se recibe el link de alguna seccion de pagina 12 (en este caso la sección mundo), y dentro de la función se crea el objeto bs4 y es donde sucede la magia:

def getNewsLinks(page):

    """ Here we get the page content """
    linkPage=requests.get(page)

    """ We convert the the page content into a BS4 type variable """
    soupPage=BeautifulSoup(linkPage.text,'lxml')

    """ en uuna variable almacenamos el contenedor de los articulos """
    articles_container=soupPage.find('div',attrs={'class':'main-content'})

    """ dentro de article_list almacenamos una lista de todos los articulos dentro del contenedor"""
    article_list=articles_container.find_all('article',attrs={'class':'article-item'})

    """ obtenemos los links a los cuales redirige cada item en la lista """
    anclas_articulos=[articulos.a.get('href')for articulos in article_list]

    """ Agregamos el link originario de la página, para armar un link funcional, ya que los href que nos brinda el sitio son, por ejemplo '/deportes' , sin la parte inicial, que podemos obtener agragandolo manualmente el link de la pagina a cada ancla en la lista """
    links_anclas=[p12.request.url + ancla for ancla in anclas_articulos]


    """ devolvemos el resultados con los links de esa sección del diario """
    return links_anclas

Mi solución en 2023

def content(section):
    # creation of lists to append results
    cont_name=[]
    cont_links=[]

    #first content extraction, heading and link. featured article
    cont_fet=section.find('h2',attrs={'class':'h1 title-list'})
    link_fet="https://www.pagina12.com.ar"+cont_fet.find('a').get('href')
    head_fet=cont_fet.find('a').get_text()
    cont_name.append(head_fet)
    cont_links.append(link_fet)

    #second content extraction, heading and link. secondaries featured articles
    cont_fet_2=section.find('div',attrs={'class':"articles-list is-grid-col2 grid-mobile-row"}).find_all('h3')
    links_fet_2=["https://www.pagina12.com.ar"+ link.a.get('href') for link in cont_fet_2]
    head_fet_2=[link.a.get_text() for link in cont_fet_2]

    cont_name.extend(head_fet_2)
    cont_links.extend(links_fet_2)

    # articles content extraction, heading and link. all others articles.
    cont=section.find('div',attrs={'class':"article-item__content-footer-wrapper gutter-small deco-bar-here-left"}).find_all('h4')
    links=["https://www.pagina12.com.ar"+ link.a.get('href') for link in cont]
    head=[link.a.get_text() for link in cont]

    cont_name.extend(head)
    cont_links.extend(links)



    print(cont_name)
    print(cont_links)

[‘Bandera de largada para un extenso año electoral’, ‘Mesa política: Bandera de largada para discutir la estrategia electoral’, ‘Juntos por el Cambio ajusta el guión en Córdoba’, ‘Aerolíneas desmiente a Macri’, ‘Lesa humanidad: Pedidos de condena para policías retirados de Río Negro’, ‘Por ahora nada detiene el juicio político a los supremos’, ‘Un Plan de Gobierno hace falta’, ‘Los faltantes tienen un sesgo geográfico\xa0’, ‘La fuerte polémica que abrió el censo 2022 sobre el distrito de La Matanza’, ‘Alberto Fernández confirmó la convocatoria a una mesa política del Frente de Todos’, ‘Vuelos de la muerte: cómo se gestó la repatriación del avión que es símbolo de los crímenes de la ESMA’]

[‘https://www.pagina12.com.ar/521420-bandera-de-largada-para-un-extenso-ano-electoral’, ‘https://www.pagina12.com.ar/521414-mesa-politica-bandera-de-largada-para-discutir-la-estrategia’, ‘https://www.pagina12.com.ar/521412-juntos-por-el-cambio-ajusta-el-guion-en-cordoba’, ‘https://www.pagina12.com.ar/521430-aerolineas-desmiente-a-macri’, ‘https://www.pagina12.com.ar/521405-lesa-humanidad-pedidos-de-condena-para-policias-retirados-de’, ‘https://www.pagina12.com.ar/521410-por-ahora-nada-detiene-el-juicio-politico-a-los-supremos’, ‘https://www.pagina12.com.ar/521411-un-plan-de-gobierno-hace-falta’, ‘https://www.pagina12.com.ar/521413-los-faltantes-tienen-un-sesgo-geografico’, ‘https://www.pagina12.com.ar/521323-la-fuerte-polemica-que-abrio-el-censo-2022-sobre-el-distrito’, ‘https://www.pagina12.com.ar/521303-alberto-fernandez-confirmo-la-convocatoria-a-una-mesa-politi’, ‘https://www.pagina12.com.ar/521239-vuelos-de-la-muerte-como-se-gesto-la-repatriacion-del-avion-’]

Anotaciones y Ejercicios Curso de Web Scraping - Modulo II
Les dejo esta y todas las anotaciones del curso actualizados al 2023, tome nota de los comentarios y ejercicios resueltos de cada sección. Lleve en cuenta las explicaciones del docente y más! Espero les sirva. 👌
(si quieres apoyar deja una estrella a la repo ✌⭐)

LES COMPARTO MI CODIGO ACTUALIZADO 2022 CON NUEVSO TAGS, ATRIBUTOS Y CLASSES

import pandas as pd
from bs4 import BeautifulSoup
import requests
import lxml.html as html

URL = 'https://www.pagina12.com.ar/'
def extract_soup_form(url):
    try:
        response = requests.get(url)
        if response.status_code == 200:
            s = BeautifulSoup(response.text, 'lxml')
            return s
        else:
            print('Status Code Error: => ', response.status_code)

    except ValueError as e:
        print('Error home links: =>', e)
def extract_home_links(s):
    try:
        sections_web = s.find('ul', attrs = {'class':'horizontal-list main-sections hide-on-dropdown'}).find_all('a')
        sections_list = [section.get('href') for section in sections_web]
        print('There are this Home Section Links: ====>: \n')
        print(sections_list)
        return sections_list

    except ValueError as e:
        print('Error: => ', e)
def extract_news_links(link, extract_soup_form):
    try:
        s = extract_soup_form(link)
        sections_web = s.find_all('div', attrs={'class': 'article-item__content'})
        sections_list= ['https://www.pagina12.com.ar'+section.p.a.get('href') for section in sections_web]
        print('\nThere are this Inside Section Links ===> \n')
        print(sections_list)
        return sections_list

    except ValueError as e:
        print('Error inside first link: =>' , e)
def main():
    s = extract_soup_form(URL)
    list_section_links=extract_home_links(s)
    inside_links=extract_news_links(list_section_links[0], extract_soup_form)
    
if __name__ == '__main__':
    main()

def pagina_12(url):
pagina = requests.get(url)
sopa = BeautifulSoup(pagina.text, ‘lxml’)
print(sopa.prettify())
secciones = sopa.find(‘ul’, attrs={‘class’: ‘main-sections’}).find_all(‘li’)
seccion = secciones[0]
seccion.a.get(‘href’)
links_secciones = [seccion.a.get(‘href’) for seccion in secciones]

return links_secciones
def analisis_section(objeto):
  total_articulos=[]
  article=objeto.find('section', attrs={'class':'col0'})
  articles=article.find_all('article')
  articulo=articles[0]
  link_articulo=[articulo.a.get('href') for articulo in articles]
  for i in link_articulo:
    link_final='https://www.eltiempo.com'+i
    total_articulos.append(link_final)
  
  print(total_articulos)

¡Reto resuelto con la estructura de la página en Octubre 2022!

import requests
from bs4 import BeautifulSoup

HOME_URL = 'https://www.pagina12.com.ar'


def parse_page(link):
    try:
        section = requests.get(link)
        if section.status_code == 200:
            pass
            #PARSE CURRENT PAGE
            soupSection = BeautifulSoup(section.text, 'lxml')
            #GET FEATURED ARTICLE
            featuredArticle = soupSection.find('h1', attrs={'class': 'title-list'})
            featuredLink = HOME_URL + featuredArticle.a.get('href')
            #GET HORIZONTAL ARTICLES
            articleListH = soupSection.find_all('h2', attrs={'class': 'title-list featured-article'})
            articleLinkH = [(HOME_URL + titleH.a.get('href')) for titleH in articleListH]
            #GET VERTICAL ARTICLES
            articleListV = soupSection.find_all('h2', attrs={'class': 'is-display-inline title-list'})
            articleLinkV = [(HOME_URL + titleV.a.get('href')) for titleV in articleListV]

            return(articleLinkH + articleLinkV)
        else:
            raise ValueError(f'Error: {section.status_code}')
    except ValueError as ve:
        print(ve)



def parse_home():
    try:
        response = requests.get(HOME_URL)
        if response.status_code == 200:
            soup = BeautifulSoup(response.text, 'lxml')
            #GET NAV LINKS
            listLinks = soup.find('ul', attrs = {'class': 'main-sections'}).find_all('li')
            link = listLinks[1].a.get('href')
            parse_page(link)
        else:
                raise ValueError(f'Error: {response.status_code}')
    except ValueError as ve:
        print(ve)


def run():
    parse_home()


if __name__=='__main__':
    run()

Reto

Mi aporte del reto utilizando un decorador, acepto feedback:

def link_web(func):
    """
    Función que recibe link de la página 
    y verifica la respuesta 200 OK.
    """
    def wrapper(url):
        try:
            request = requests.get(url)
            if request.status_code == 200:
                print('Respuesta OK')
                func(url)
            else:
                raise ValueError(f'Error: {request.status_code}')
        except ValueError as ve:
            print(ve)
    return wrapper


@link_web
def article_scraper(url):
    """
    Función que extrae el articulo y sus links
    """
    request = requests.get(url)
    web_body = BeautifulSoup(request.text, 'lxml')
    article = web_body.find('div', attrs={'class':'article-item__content-footer-wrapper gutter-small deco-bar-here-left is-mobile-top'})
    link_article = [url + article.a.get('href')]
    print(f'Noticia principal: {link_article}')
    article_second = web_body.find('div', attrs={
        'class':
        'articles-list is-grid-col2 grid-mobile-row'
    }).find_all('h2')
    links_seconds = [url + link.a.get('href') for link in article_second]
    print(f'Noticia secundaria: {links_seconds}')
    article_list = web_body.find('article', attrs={
        'class':
        'article-item article-item--teaser'
    }).find_all('h2')
    links_articles = [url + link.a.get('href') for link in article_list]
    print(f'Otros artículos: {links_articles}')

    return link_article + links_seconds + links_articles

Link utilizado:

pag = 'https://www.pagina12.com.ar/secciones/el-pais'
article_scraper(pag)

El texto también se puede extraer como:

seccion.a.text

Arroja el mismo tipo de dato str, quizá haya diferencia en tiempo de ejecución.

Reto resuelto

<def fnt_obtener_links(s):
    top_article = s.find('div', attrs={'class': 'article-item__content'})
    top_article_link = top_article.a.get('href')
    
    lista_h3 = s.find_all('h3', attrs={'class': 'title-list'})
    lista_links_seccion1 = [link.a.get('href') for link in lista_h3]
    
    lista_h2 = s.find_all('h2', attrs={'class': 'title-list'})
    lista_links_seccion2 = [link.a.get('href') for link in lista_h2]
    
    lista_links = [top_article_link] + lista_links_seccion1 + lista_links_seccion2
    
    return lista_links

import requests
from bs4 import BeautifulSoup

# resultado_request = requests.get('https://www.pagina12.com.ar/secciones/el-pais')
resultado_request = requests.get('https://www.pagina12.com.ar/secciones/el-pais')

s = BeautifulSoup(resultado_request.text, 'lxml') 

fnt_obtener_links(s)> 
def news(link):
    """
    Definición: Extracción de titulares y links de portal de noticias Argentino.
    
    Parámetro: 
    -link: URL de la sección de noticias a procesar.
    
    Retorno: Títulos y links de las noticias pertenecientes a ella.   
    """
    req = requests.get(link)
    soup = BeautifulSoup(req.text, "lxml")
    article_list = soup.find_all("h4", attrs={"class" : "is-display-inline title-list"})
    #Obtenemos los links de las noticias
    links_list = [link[:27]+article.a.get("href") for article in article_list]
    #Obtenemos los titulos de las noticias
    titles_list = [article.a.get_text() for article in article_list]
    return titles_list,links_list
def main(url:str):
  response = requests.get(url)

  if response.status_code == 200:
    s = BeautifulSoup(response.text, 'lxml')

    sections = s.find_all('h2')

    sections_links = [url + section.a.get('href') for section in sections]
    sections_links

    for link in sections_links:
      s_child = requests.get(link)

      article = BeautifulSoup(s_child.text, 'lxml')

      title = article.find('h1')

      print(title.get_text())

Debido al cambio del HTML de la pagina, así me quedo el reto:

#Importacion de Librerias
import requests
from bs4 import BeautifulSoup

#Funciones
def GetLinks(ProtocolDomain, Soup):
    Notices=Soup.find_all('div',{'class','article-item__content'})
    Notices=[ProtocolDomain+ Notice.a.get('href') for Notice in Notices]
    return(Notices)

#Variables de Entrada
Links_sections='https://www.pagina12.com.ar/secciones/el-pais'
url='https://www.pagina12.com.ar'

#Preparacion de Variable
page = requests.get(Links_sections)
soup= BeautifulSoup(page.text,'lxml')

#Invocacion de Funcion
GetLinks(url, soup)

Reto cumplido:

import requests
from bs4 import BeautifulSoup

URL = 'https://www.pagina12.com.ar/secciones/el-pais'

def get_links_notices(url):
    """Función que recibe un URL de una categoría determinada de 
    la página web de noticias 'Página 12' y devuelve los links de 
    todas las noticias pertenecientes a ella
    """
    
    soup = BeautifulSoup(requests.get(url).text,'lxml')
    
    # obtener url raíz
    main_url = url[:27]
    
    featured_article = soup.find('div', attrs={'class':'article-item__content'})
    featured_article_link = [main_url + featured_article.a.get('href')]
    
    article_content = soup.find_all('h3', attrs={'class':'title-list'})
    links_article_content = [main_url + links.a.get('href') for links in article_content]
    
    items_article = soup.find_all('div', attrs={'class':'article-item__header'})
    links_items_article = [main_url + links.a.get('href') for links in items_article]
    
    links = featured_article_link + links_article_content + links_items_article
    
    return links

get_links_notices(URL)

output

[‘https://www.pagina12.com.ar/440247-denuncia-contra-la-corte-suprema-en-las-naciones-unidas’,
https://www.pagina12.com.ar/440245-rafecas-tendra-a-su-cargo-la-causa-por-el-ataque-al-institut’,
https://www.pagina12.com.ar/440236-miles-de-antorchas-por-evita-y-para-respaldar-al-gobierno-de’,
https://www.pagina12.com.ar/440247-denuncia-contra-la-corte-suprema-en-las-naciones-unidas’,
https://www.pagina12.com.ar/440245-rafecas-tendra-a-su-cargo-la-causa-por-el-ataque-al-institut’,
https://www.pagina12.com.ar/440236-miles-de-antorchas-por-evita-y-para-respaldar-al-gobierno-de’,
https://www.pagina12.com.ar/440232-evita-el-recuerdo-de-un-mito-inalterable’,
https://www.pagina12.com.ar/440225-la-tumba-de-evita-peron-donde-el-color-de-las-flores-avisa-q’,
https://www.pagina12.com.ar/440221-casa-rosada-todos-los-nombres-conducen-a-massa’,
https://www.pagina12.com.ar/440219-pese-a-su-caida-en-las-encuestas-el-sector-de-macri-no-renun’,
https://www.pagina12.com.ar/440211-ordenan-realizar-una-inspeccion-ocular-al-centro-clandestino’,
https://www.pagina12.com.ar/440201-potenciar-trabajo-bono-de-11-mil-pesos-a-1-3-millones-de-ben’,
https://www.pagina12.com.ar/440200-anuncian-la-construccion-de-nuevo-edificio-del-archivo-provi’,
https://www.pagina12.com.ar/440178-mario-negri-estaciono-su-auto-en-una-rampa-para-discapacitad’]

Hola!
Aquí va mi respuesta al reto, actualizado al 26/07/22

def get_links_noticias(seccion):

    main_article = seccion.find('section', attrs={'class':'top-content'}).find_all('h2')
    #Se usa url[0:-1] para quitar el último caracter de la url guardada anteriormente (https://www.pagina12.com.ar/)
    #y se concatena con el link de la noticia que está en forma relativa.
    main_article_list = [url[0:-1]+ma.find('a').get('href') for ma in main_article]

    featured_articles = seccion.find_all('article', attrs={'class':'article-item--featured'})
    featured_article_list = [url[0:-1]+fa.find('h3').find('a').get('href') for fa in featured_articles]

    regular_articles = seccion.find_all('article', attrs={'class':'article-item article-item--teaser'})
    regular_articles_list = [url[0:-1]+ra.find('h4').find('a').get('href') for ra in regular_articles]

    links = main_article_list + featured_article_list + regular_articles_list

    return links
    
#En la lista de los link de secciones deportes corresponde a la posicion 4
seccion_deportes = BeautifulSoup(requests.get(link_secciones[4]).text, 'lxml')
get_links_noticias(seccion_deportes)

Encontré tres formas diferentes de obtener los enlaces.

Con selectores CSS.

sections = soup.select('ul.main-sections>li>a')
{i.get_text(): i['href'] for i in sections}
[O] 
{'El país': 'https://www.pagina12.com.ar/secciones/el-pais',
 'Economía': 'https://www.pagina12.com.ar/secciones/economia',
 'Sociedad': 'https://www.pagina12.com.ar/secciones/sociedad',
 'Cultura y Espectáculos': 'https://www.pagina12.com.ar/suplementos/cultura-y-espectaculos',
 'Deportes': 'https://www.pagina12.com.ar/secciones/deportes',
 'El mundo': 'https://www.pagina12.com.ar/secciones/el-mundo',
 'Contratapa': 'https://www.pagina12.com.ar/secciones/contratapa'}

Con métodos de bs

sections = soup.find('ul', attrs={'class', 'main-sections'}).find_all('li')
dict(map(lambda section: (section.find('a').get_text(), section.find('a')['href']) ,sections ))
[O] ----------------------------------------
{'El país': 'https://www.pagina12.com.ar/secciones/el-pais',
 'Economía': 'https://www.pagina12.com.ar/secciones/economia',
 'Sociedad': 'https://www.pagina12.com.ar/secciones/sociedad',
 'Cultura y Espectáculos': 'https://www.pagina12.com.ar/suplementos/cultura-y-espectaculos',
 'Deportes': 'https://www.pagina12.com.ar/secciones/deportes',
 'El mundo': 'https://www.pagina12.com.ar/secciones/el-mundo',
 'Contratapa': 'https://www.pagina12.com.ar/secciones/contratapa'}

Con xpath

hhh = etree.HTML(str(soup))
sections = hhh.xpath(r'//ul[contains(@class, "main-sections")]/li/a')
dict(map(lambda x: (x.text, x.get('href')), sections))
[O] --------------------------------------------
{'El país': 'https://www.pagina12.com.ar/secciones/el-pais',
 'Economía': 'https://www.pagina12.com.ar/secciones/economia',
 'Sociedad': 'https://www.pagina12.com.ar/secciones/sociedad',
 'Cultura y Espectáculos': 'https://www.pagina12.com.ar/suplementos/cultura-y-espectaculos',
 'Deportes': 'https://www.pagina12.com.ar/secciones/deportes',
 'El mundo': 'https://www.pagina12.com.ar/secciones/el-mundo',
 'Contratapa': 'https://www.pagina12.com.ar/secciones/contratapa'}

Personalmente, prefiero utilizar los selectores CSS, son más simples y el código queda en pocas líneas.

RETO

import requests
from bs4 import BeautifulSoup

pagina = requests.get('https://www.pagina12.com.ar/secciones/sociedad')
soup = BeautifulSoup(pagina.text,'lxml')

def get_links(soup):
    article_items = soup.find_all('article', attrs={'class':'article-item'})
    article_links = [item.a.get('href') for item in article_items]
    return article_links

get_links(soup)

""" Salida: 
 ['/426589-una-alianza-contra-elon-musk-en-twitter',
 '/426565-designan-otra-jefa-en-la-unidad-10-de-melchor-romero-que-alo',
 '/426544-garganta-del-diablo-se-suspendio-el-acceso-del-publico-por-l',
 '/416474-por-que-el-420-es-el-numero-de-la-marihuana',
 '/426516-murio-el-noba-tras-10-dias-internado-en-terapia-intensiva',
 '/426514-las-vegas-una-empresa-busca-impedir-que-imitadores-de-elvis-',
 '/426505-oms-el-cambio-climatico-tiene-fuerte-impacto-en-la-salud-men',
 '/424163-quien-era-el-noba-el-popular-musico-de-cumbia-420-que-murio-',
 '/426497-alberto-fernandez-a-siete-anos-del-primer-3-j-seguiremos-tra',
 '/426489-abre-al-publico-la-reserva-ecologica-de-costanera-norte',
 '/426053-efemerides-de-hoy-que-paso-un-3-de-junio'] """

Hola! Yo estoy siguiendo este curso pero con el proyecto de hacer web scrapping de la web IMDB, donde podemos observar el top de 250 películas. En este caso decidí extraer información de: nombre de la película, directores y protagonistas, año de release, IMDB score y numero de votantes. Aquí les dejo el código por donde va:

# Web Scrapper - IMDB Top 250 movies
import requests as rq
from bs4 import BeautifulSoup

# Saving the URL
url = 'https://www.imdb.com/chart/top/?ref_=nv_mv_250'

# Creating the request
page = rq.get(url)

# Making sure the web got the solicitude
page.status_code
print('The status of the request is: {}'.format(page.status_code))

# Creating the soup
soup = BeautifulSoup(page.text, 'lxml')

# Looks like the 'lister' tag has all the information we need
general = soup.find('div', attrs = {'class':'lister'})

# Now let's go deeper
general = soup.find('tbody', attrs= {'class':'lister-list'}).find_all('tr')

# All the information are in these tags, we just need to extract them!
# First, let's create a list with all the links

def link_creation(general):
    section = general[0]
    link_sections = [section.a.get('href') for section in general]
    index = 1
    common = '?pf_rd_m=A2FGELUUNOQJNL&pf_rd_p=1a264172-ae11-42e4-8ef7-7fed1973bb8f&pf_rd_r=MBY5AS7XQMWKBXS6Y8N7&pf_rd_s=center-1&pf_rd_t=15506&pf_rd_i=top&ref_=chttp_tt_'
    links = []
    for i in link_sections:
        link = ('www.imdb.com' + str(i) + common + str(index))
        links.append(link)
        index += 1
    
    return links

# Now let's create a list of movie names!

def movie_names(general):
    section = general[0]
    movie_names = [section.find('td', attrs = {'class':'titleColumn'}).a.get_text() for section in general]
    return movie_names

# And for everything else...

def categorics(general):
    section = general[0]
    ratings = [section.find('span', attrs = {'name':'ir'}).get('data-value') for section in general]
    qualifiers = [section.find('span', attrs = {'name':'nv'}).get('data-value') for section in general]
    year = [section.find('span', attrs = {'class':'secondaryInfo'}).get_text() for section in general]
    protagonist_dir = [section.find('td', attrs = {'class':'titleColumn'}).a.get('title') for section in general]

    return(section, ratings, qualifiers, year, protagonist_dir)

Mi get_article functions 😄

def get_articles(link_seccion): 
    sec = requests.get(link_seccion)
    if sec.status_code == 200:
        soup = BeautifulSoup(sec.text,'lxml')
        lister = soup.find_all('article')
        articulos = {lister[x].find_all('a')[1].get_text():lister[x].find_all('a')[1].get('href') for x in range(0,len(lister))}
        return articulos
    else:
        print('HTML code is not 200')

Estuve horas y no encontraba el error :´v
Si el objeto.status_code te regresa un 403 como en mi caso no te va a salir la práctica. Si me percate que el DOM de la página había cambiado ligeramente y creía que ahí esta el error, hasta que después de ver varias veces los videos, me di cuenta que la salida del objeto.text no correspondía del todo a la pág que solicitaba. Por lo que intente conseguir un código de status 200.
Investigando me di cuenta que si hacia uso de los encabezados de solicitud tal vez podría arreglar el status de mi solicitud(En el video 4, minuto 5:50 obtuve una señal para solucionar el problema)
----Código----
url = 'https://www.pagina12.com.ar/'
headers = {‘user-agent’: ‘my-agent/1.0.1’}
p12 = requests.get(url, headers=headers)

try:
    page =requests.get(url)
    if page.status_code == 200:
        soup = bs(page.text, "lxml")
        listUrl = list(soup.find_all("a"))
        for i in listUrl:   
            print(i.get("href"))
except (ValueError, IndexError):
    print("error")

pues alli le puse un poco de lo que tambien habia aprendido en anteriores cursos de platzi

si realizo estos procedimientos con Xpath, es peor o mejor que BeautifulSoup? o cual es la diferencia

Esta es mi resolución al reto, personalmente me gusta trabajar más con select que con find, siento que es más dinámico.

Agregué una pequeña función map para agregar el hosntame a ciertos articulos que venía el link sin el hostname.

Reto listo. Pagina de los artículos: https://www.pagina12.com.ar/secciones/el-pais

def soup(sopa):
    """Funcion que recive un objeto BS y retorna todos los links de los articulos que hay en la pagina"""
    articles = sopa.find_all('article',attrs={'class':'article-item'})
    links = [article.find('a').get('href') for article in articles]
    return links

No se si ustedes les paso pero la pagina que utiliza
cambio su estructura, es por eso que decidí hacerlo con la pagina del periódico el Sol de México

Reto en la actualidad (13/12/21)

def links_section(url):
    pag = requests.get(url)
    if pag.status_code == 200:
        soup3 = BeautifulSoup(pag.text, 'lxml')
        links = soup3.select('.title-list a')
        href = [url + i.get('href') for i in links]
        print(href)
    else:
        print(f'Página no responde Error: {pag.status_code}')

if __name__ == '__main__':
    url3 = "https://www.pagina12.com.ar/secciones/el-pais"
    links_section(url3)

Mi código (7/12/2021):

def extraer_links(link):
    pagina = requests.get(link)
    s_link = BeautifulSoup(pagina.text, 'lxml')
    s_noticiash2 = s_link.find_all('h2', attrs = {'class':'title-list'})
    s_noticiash3 = s_link.find_all('h3', attrs = {'class':'title-list'})
    s_noticiash4 = s_link.find_all('h4', attrs = {'class':'is-display-inline title-list'})
    s_noticias = s_noticiash2 + s_noticiash3 + s_noticiash4
    links_noticias = ['https://www.pagina12.com.ar' + seccion.find('a').get('href') for seccion in s_noticias]
    return links_noticias

Al día de hoy. El div de la primera noticia se llama ‘article-item__content’.

Al día de hoy, 2021-11-10, la estructura de la página ha cambiado un poco. El código que me función queda así:

def links_category(sopa):
    ''' Input is a category page soup 
        and outputs are links '''
    
    link_feat_art = sopa.h2.a.get('href')

    #HTML text
    h3 = sopa.find_all('h3')
    h4 = sopa.find_all('h4')

    #lists

    links = []

    link_h2 = [link_feat_art]
    link_h3 = [x.a.get('href') for x in h3]
    link_h4 = [x.a.get('href') for x in h4 if x.a is not None]

    #Output

    links.extend(link_h2)
    links.extend(link_h3)
    links.extend(link_h4)

    links = ['https://www.pagina12.com.ar'+link for link in links]

    return links

Probándolo:

test = BeautifulSoup(requests.get('https://www.pagina12.com.ar/secciones/el-pais').text, 'lxml')
links_category(test)

Lo que me devuelve:

['https://www.pagina12.com.ar/381027-el-equipo-de-francisco',
 'https://www.pagina12.com.ar/381002-congelamiento-de-medicamentos-carla-vizzotti-y-roberto-felet',
 'https://www.pagina12.com.ar/380982-creamos-trabajo-queremos-derechos-movimientos-sociales-expon',
 'https://www.pagina12.com.ar/380963-el-frente-de-todos-porteno-cierra-la-campana-con-una-caravan',
 'https://www.pagina12.com.ar/366614-elecciones-2021-se-puede-cortar-boleta-este-14-de-noviembre',
 'https://www.pagina12.com.ar/380955-piden-la-indagatoria-de-mauricio-macri-por-el-destino-de-los',
 'https://www.pagina12.com.ar/380954-el-derrrumbe-de-otra-operacion-politica-del-macrismo-la-just',
 'https://www.pagina12.com.ar/360242-elecciones-2021-quienes-estan-exceptuados-por-covid-de-votar',
 'https://www.pagina12.com.ar/380950-cuales-son-los-requisitos-de-ingreso-al-pais-para-los-extran',
 'https://www.pagina12.com.ar/367149-elecciones-2021-cuando-empieza-la-veda-electoral-y-cuanto-ti',
 'https://www.pagina12.com.ar/380938-capturaron-a-roberto-brunello-un-represor-que-secuestro-tort']

Esta es de una página de noticias de Panamá

import requests
from bs4 import BeautifulSoup
import lxml.html as html


def article_list(url,s):
    
    #haciendo el primer listado de links
    seccion = s.find('div', attrs={'class' : "nav" }).find_all('li')
    seccion0=seccion[0]
    links_secciones = [url+seccion0.a.get('href') for seccion0 in seccion]
    print(links_secciones)

    #Entrando en el primer link
    num= int(input('Ingrese el número de link que desea obtener sus links: '))
    sec = requests.get(links_secciones[num-1])

    #Haciendo el soup de la página buscada
    s_seccion = BeautifulSoup(sec.text, 'lxml')
    #print(s_seccion)
    
    #Artículo principal
    featured_art= s_seccion.find('div', attrs={'class' : "article-details con" })
    #print(featured_art)
    feat=url+featured_art.a.get('href')
    
    
    #Otros artículos
    article_list = s_seccion.find('div', attrs={'class':'category-secondary-sidebar'}).find_all('a')
    article_list0=article_list[0]
   
    
    links_secciones2 = [url+article_list0.get('href') for article_list0 in article_list]
   
    lista=[feat]
    lista.extend(links_secciones2)
    print(lista)



if __name__ == '__main__':
    url = 'https://www.laestrella.com.pa'

    p12=requests.get(url)
    print(p12.status_code)

    s=BeautifulSoup(p12.text, 'lxml')
    
    article_list(url,s)

Me he saltado un poco las reglas y lo he estado haciendo con una tienda, extrayendo el Titulo y el Precio

def obtener_notas(soup):
    lista_notas = []
    #Titulo del producto
    titulo = soup.find('h1', attrs={'class':'product_title entry-title'})
    if titulo:
        lista_notas.append(titulo.get_text())
    #Precio del producto
    precios = soup.find_all('bdi')
    precio = precios[2]
    if precios:
        lista_notas.append(precio.get_text())
    return lista_notas

obtener_notas(s_barril)
#OUT
['Barril Asador Ahumador Lamina de Hierro 10 Lb', 'COP $304,900.00']

Para resolver el reto he tenido en cuenta los casos en los que podía fallar.

Este código válido a 24/08/21.

import requests
from bs4 import BeautifulSoup

def news_links(soup):
    
    """
    @param BeautifulSoup soup
    @returns Array of soup's links
    """
    if soup is None:
        return []
    
    # Variables
    featured_article_link = []
    top_articles_links = []
    general_articles_links = []
    
    # Featured articles
    featured_article = soup.find('section', attrs={'class':'top-content'})
    if featured_article is not None:
        featured_article = featured_article.find('article')
        if featured_article is not None:
            featured_article = featured_article.find('h2')
            if featured_article is not None:
                featured_article = featured_article.find('a')
                if featured_article is not None:
                    featured_article_link = [featured_article.get('href')]
    
    # Rest of the articles in page
    articles_lists = soup.find('section', attrs={'class':'list-content'})
    if articles_lists is not None:
        articles_lists = articles_lists.find_all('div', {'class':'articles-list'})
    
        ## Top articles
        top_articles = articles_lists[0].find_all('article')
        if top_articles is not None:
            for article in top_articles:
                article = article.find('p', attrs={'class':'p12-separator--left--primary'})
                if article is not None:
                    article = article.find('a')
                    if article is not None:
                        top_articles_links += [article.get('href')]

        ## General articles
        general_articles = articles_lists[1].find_all('article')
        if general_articles is not None:
            for article in general_articles:
                article = article.find('a', attrs={'class':'p12-separator--left--primary'})
                if article is not None:
                    general_articles_links += [article.get('href')]
                        
    return featured_article_link + top_articles_links + general_articles_links

# Usage example
the_links = []

main_response = requests.get('https://www.pagina12.com.ar/')
s = BeautifulSoup(main_response.text, 'lxml')

sections = s.find('div', attrs={'class':'p12-dropdown-column'}).find_all('a')

for section in sections:
    response = requests.get(section.get('href'))
    soup = BeautifulSoup(response.text, 'lxml')
    the_links = the_links + news_links(soup)
    
the_links

guarde en una lista, listas donde almacene links de noticias segun su categoria , fueron 66 noticias, no se si sean todas

def seccion_link(obj, index):
    notices = obj.find_all('div' , attrs={'class':'article-item__content'})
    notices = [i.a.get('href') for i in notices]
    for i in notices:
        all_notices[index].append(i)
    


 
def search_link(obj):
    secciones = page.find('ul', attrs={'class':'horizontal-list main-sections hide-on-dropdown'}).find_all('li')
    links = [i.a.get('href') for i in secciones]

    for a,b in enumerate(links):
        all_notices.append([])
        category = requests.get(b)
        category = bs(category.text , 'lxml')
        seccion_link(category, a)
        
        
        
        
if __name__ == '__main__':
    search_link(page) 
    contador = 0
    for lista in all_notices:
        for i in lista:
            print(f'{i} \n')
            contador += 1
            
    print(contador)
        

Reto al 07-mayo-2021:

def get_article_links(soup):
    """ Receives a soup object and return the article links list"""
    articles = soup.find_all(attrs={'class': 'title-list'})
    return [article.a.get('href') for article in articles]

Acá va el reto 😈:

from bs4 import BeautifulSoup
import requests


SECCION_URL = 'https://web.archive.org/web/20191111171336/https://www.pagina12.com.ar/secciones/el-pais'

NOTICIAS_PRINCIPALES_SELECTOR = '.featured-article__container > h2 > a'
NOTICIAS_SECUNDARIAS_SELECTOR = '.article-box__container > h2 > a'


def seleccionar_noticias(seccion_soup, noticias_selector):
    return seccion_soup.select(noticias_selector)


def noticias_principales(seccion_soup):
    return seleccionar_noticias(seccion_soup, NOTICIAS_PRINCIPALES_SELECTOR)


def noticias_secundarias(seccion_soup):
    return seleccionar_noticias(seccion_soup, NOTICIAS_SECUNDARIAS_SELECTOR)


def obtener_links(noticias_soup):
    links = map(lambda noticia: noticia.get('href'), noticias_soup)
    return list(links)


def obtener_links_de_las_noticias(seccion_soup):
    """
    Recibe una sopa de una sección de noticias.
    Retorna los links de las noticias.
    """
    noticias = noticias_principales(seccion_soup) +\
        noticias_secundarias(seccion_soup)

    return obtener_links(noticias)


def main():
    """ Ejemplo de uso """
    p12_request = requests.get(SECCION_URL)
    p12_html = p12_request.text

    seccion_soup = BeautifulSoup(p12_html, 'lxml')

    links = obtener_links_de_las_noticias(seccion_soup)

    for indice, link in enumerate(links):
        print(f"Noticia {indice}: {link}")
        print("-" * 50)


if __name__ == '__main__':
    main()

Reto cumplido

import requests
from bs4 import BeautifulSoup

response = requests.get('https://www.pagina12.com.ar/')
s = BeautifulSoup(response.text, 'lxml')
secciones = s.find('ul',attrs={'class':'horizontal-list main-sections hide-on-dropdown'}).find_all('li')
seccion = secciones[0]
link_secciones = [seccion.a.get('href') for seccion in secciones]
sec = requests.get(link_secciones[5])
soup = BeautifulSoup(sec.text, 'lxml')

def notice(soup):
 
    Article_list = soup.find_all('div', attrs = {'class':'article-item__content'})

    links_Article= [link.find('a').get('href') for link in Article_list]
    
    return links_Article
    
if __name__ == '__main__':
    a = notice(soup)
    for links in a:
        print(links)

Con las modificaciones que le hicieron a la página 2021

def noticias_por_seccion (seccion ):
	"""Debe recibir un link ya pharseado lxml"""
	noticia_lista_links = seccion.find_all('article')
	noticia_lista_links = [noticia.a.get('href') for noticia in noticia_lista_links]
return noticia_lista_links

A mi me sirvieron las siguientes líneas para resolver el tema:

links = [item.get('href') for item in secciones]
texto = [item.get_text() for item in secciones]

Hola a todos, por acá les dejo un link donde pueden descargar la versión del notebook que estoy contruyendo a medida que el curso avanza.

https://drive.google.com/file/d/1V5tttkZWpSnu2yqR3Cfs0s4STqOkzcMV/view?usp=sharing

para la comunidad si tienen erro al momento de realizar el boc = requests.get(LinkBocon[0]) es por que si se fijan bien no tiene https:// entonces para poder invocar a esa funcion le ayudamos poniendo el nimbre de la pagina /2pagina
boc = requests.get(‘https://elbocon.pe’+LinkBocon[1])

Este es el código en Jupiter Notebook

import requests
url = 'https://www.pagina12.com.ar/'
p12 = requests.get(url)
p12.status_code
p12.content
p12.request.url
from bs4 import BeautifulSoup
s = BeautifulSoup(p12.text, 'lxml')
secciones = s.find('ul', attrs={'class':'horizontal-list main-sections hide-on-dropdown'}).find_all('li')
links_secciones = [seccion.a.get('href') for seccion in secciones]
sec = requests.get(links_secciones[0])
s_seccion = BeautifulSoup(sec.text, 'lxml')
article_list = s_seccion.find('div', attrs={'class':'articles-list is-grid-col2 grid-mobile-row'})
articles = s_seccion.find('div', attrs={'class':'article-item__content-footer-wrapper gutter-small deco-bar-here-left'}).find_all('h4', attrs={'class':not ''})
links_articles = [article.a.get('href') for article in articles]
links_articles

https://www.pagina12.com.ar/304090-que-es-el-dispo-y-que-cambia-respecto-al-aspo’,
https://www.pagina12.com.ar/304075-el-gobierno-prepara-el-paso-del-aislamiento-al-distanciamien’,
https://www.pagina12.com.ar/304062-declaran-de-interes-publico-las-vacunas-contra-el-coronaviru’,
https://www.pagina12.com.ar/304060-habilitan-la-vuelta-a-clases-presenciales-en-las-universidad’,
https://www.pagina12.com.ar/304031-como-sera-la-caravana-del-retorno-a-la-patria-de-evo-morales’,
https://www.pagina12.com.ar/304028-por-un-sistema-de-medios-publicos-con-perspectiva-de-genero’,
https://www.pagina12.com.ar/304013-santiago-cafiero-sigue-siendo-una-posibilidad-enviar-este-an’,
'https://www.pagina12.com.ar/304009-formalizaron-el-primer-ingreso-al-programa-de-empleo-trans-e

¿Alguien sabe porqué me aparece éste error?

Challenge:

def articles_links(soup):
    articles_featured = soup.find('section', attrs={'class':'top-content'}).find_all('h2') 
    articles_h3 = soup.find('section', attrs={'class':'list-content'}).find_all('h3')
    articles_h4 = soup.find('section', attrs={'class':'list-content'}).find_all('h4', attrs={'class':'is-display-inline title-list'})
    links_featured = [article.a.get('href') for article in articles_featured]
    links_h3 = [article.a.get('href') for article in articles_h3]
    links_h4 = [article.a.get('href') for article in articles_h4]
    print(links_featured, links_h3, links_h4)

Ayuda me sale este error

<code>
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-14-befa6dea293b> in <module>
----> 1 secciones = s.find('ul', attrs={'class':'hot-sections'}).find_all('li')
      2 secciones

AttributeError: 'NoneType' object has no attribute 'find_all'
<code>

Reto resuelto!

def extract_links_p12(soup):
    """
    Función que recibe un objeto de tipo BeautifulSoup de una categoría de Página12, y devuelve un diccionario con el nombre del artículo y su enlace.
    """
    soup_list = soup.find('ul', attrs = {'class':'article-list'}).find_all('div', attrs = {'class': 'article-box-sections'})
    links = [l.a.get('href') for l in soup_list]
    descriptions = [l.a.get_text() for l in soup_list]
    dict_description = {description:link for description, link in zip(descriptions, links)}
    return dict_description

# Usandolo con la página de economía el 03/09/2020

{'Preventivo de crisis|Atento Argentina': 'https://www.pagina12.com.ar/289727-preventivo-de-crisis',
 'Reactivan la construcción en CABA|Para obras de más de 5000 m2 y próximas a su finalización': 'https://www.pagina12.com.ar/289723-reactivan-la-construccion-en-caba',
 'Alberto Fernández: "Necesitamos que cada autoparte la produzca una pyme"|La apuesta a la integración nacional en la producción automotriz': 'https://www.pagina12.com.ar/289701-alberto-fernandez-necesitamos-que-cada-autoparte-la-produzca',
 'Sectores con buenos números\xa0|Materiales, cosechadoras, línea blanca y textiles': 'https://www.pagina12.com.ar/289688-sectores-con-buenos-numeros',
 'Críticas a la toma de tierras|"Es un delito", aseguró Bianco': 'https://www.pagina12.com.ar/289686-criticas-a-la-toma-de-tierras',
 'Bianco: "Las salidas a estas crisis siempre vinieron de la mano del Estado"|Entrevista al jefe de Gabinete del gobierno de la Provincia de Buenos Aires': 'https://www.pagina12.com.ar/289680-bianco-las-salidas-a-estas-crisis-siempre-vinieron-de-la-man',
 'Estímulos para recuperar la producción y el empleo industrial|"Vemos a varios sectores firmes en la generación de trabajo", afirmó el secretario del área, Ariel Schale': 'https://www.pagina12.com.ar/289677-estimulos-para-recuperar-la-produccion-y-el-empleo-industria',
 'La producción de autos se recupera y busca llegar a niveles pre pandemia|La comparación contra el 2019 sigue siendo negativa': 'https://www.pagina12.com.ar/289658-la-produccion-de-autos-se-recupera-y-busca-llegar-a-niveles-',
 'El congelamiento de alquileres se prorroga\xa0|El presidente Alberto Fernández confirmó que trabajan en una extensión de la medida': 'https://www.pagina12.com.ar/289652-el-congelamiento-de-alquileres-se-prorroga',
 'Enacom con grandes empresas: el lado B de la pirotecnia mediática\xa0|La regulación de internet, celulares y TV por cable': 'https://www.pagina12.com.ar/289610-enacom-con-grandes-empresas-el-lado-b-de-la-pirotecnia-media',
 'La recaudación se recupera de a poco|Registró en agosto una suba interanual del 33,5 por ciento': 'https://www.pagina12.com.ar/289385-la-recaudacion-se-recupera-de-a-poco',
 'La bolsa en baja|Retrocedió 3,6 por ciento': 'https://www.pagina12.com.ar/289388-la-bolsa-en-baja',
 'Impulso a la industria para comenzar a crecer|Anuncios del ministro de Desarrollo Productivo, Matías Kulfas': 'https://www.pagina12.com.ar/289393-impulso-a-la-industria-para-comenzar-a-crecer',
 'El IFE y la toma de tierras|Raverta dijo que no se constató un vínculo': 'https://www.pagina12.com.ar/289414-el-ife-y-la-toma-de-tierras'}
def links_function(BS):
    featured_article = BS.find('div', attrs = {'class':'featured-article__container'})
    featured_article = featured_article.a.get('href')
    article_list = BS.find('ul', attrs = {'class':'article-list'})
    sub_articulos = article_list.find_all('h2')
    links_sub_articulos = [ sub_articulo.a.get('href') for sub_articulo in sub_articulos]
    print('el articulo principal de la seccion es: \n', featured_article)
    print('los articulos secundarios son:\n')
    print ('{}:{}'.format(i,links_sub_articulos[i]) for i in enumerate(links_sub_articulos))

Mi solucion

def get_links_article_page(beautifulSoup):
    links = []

    ul_tag = beautifulSoup.find('ul', attrs={'class':'article-list'})
    h2_list = ul_tag.find_all('h2')
    
    for h2 in h2_list:
        a = h2.find('a').get('href')
        links.append(a)

    return links

Mi solucion al reto:


import requests 
import bs4
from bs4 import BeautifulSoup
def get_link(link):
    articulo = link.find('ul', attrs={'class':'article-list'}).find_all('h2')
    sec = articulo[0]
    sec.find('a')
    #art = link.find('div', attrs={'class':'article-box__container'})
    link_articulo = [sec.a.get('href') for sec in articulo]
    return(link_articulo)
if __name__ == '__main__':
    url = input('ingrese el url: ')
    pagina = requests.get(url)
    s = BeautifulSoup(pagina.text, 'lxml')
    lin = get_link(s)
    for i in lin:
        print(i)```

Mi solución:

def get_article_links(soup):
    article_links = []
    # El artículo promocionado
    featured_article = soup_seccion.find('div', attrs={'class': 'featured-article__container'})
    featured_article_link = featured_article.find('h2').a.get('href')
    article_links.append(featured_article_link)
    
    # La lista de abajo
    article_list = soup_seccion.find('ul', attrs={'class': 'article-list'})
    article_list = article_list.find_all('div', 'article-box__container')
    [article_links.append(article.h2.a.get('href')) for article in article_list]
    return article_links

Trato de hacer un for con el text y no me funciona.
CÓDIGO:

texts = [t.a.get_text() for t in td]

AttributeError Traceback (most recent call last)
<ipython-input-28-f56178ad35a3> in <module>
----> 1 texts = [t.a.get_text() for t in td]

<ipython-input-28-f56178ad35a3> in <listcomp>(.0)
----> 1 texts = [t.a.get_text() for t in td]

AttributeError: ‘NoneType’ object has no attribute ‘get_text’

Mi solución

def showNews( objBs4SectionPage: BeautifulSoup ):
    featured_article= s_seccion.find('div', attrs={'class':'featured-article__container'})
    featured_article
    print(featured_article.a.get('href'))
    article_list = s_seccion.find('ul', attrs={'class':'article-list'})

    list_featured_articles = article_list.find_all('h2')
    links_featured_articles = [ articles.a.get('href') for articles in list_featured_articles]
    for link in links_featured_articles :
        print(link)
        
return;

Al ejecutar

showNews(s_seccion)

Obtenemos

https://www.pagina12.com.ar/237129-orcar-laborde-presidira-el-parlasur
https://www.pagina12.com.ar/237005-las-retenciones-de-vuelta-la-burra-al-trigo
https://www.pagina12.com.ar/237133-con-la-mirada-puesta-en-la-camara-baja
https://www.pagina12.com.ar/237144-alberto-fernandez-recibio-a-daniel-rafecas
https://www.pagina12.com.ar/237168-sin-memoria
https://www.pagina12.com.ar/237067-en-el-senado-el-macrismo-pierde-una-integrante
https://www.pagina12.com.ar/237075-dictadura-proyecto-para-reparar-legajos-de-trabajadores-desa
https://www.pagina12.com.ar/237086-hacia-la-emergencia-bonaerense
https://www.pagina12.com.ar/237091-lange-de-falcone-fue-desplazada-de-la-presidencia-de-la-cort
https://www.pagina12.com.ar/237099-peajes-una-empresa-que-estuvo-vinculada-a-los-macri-se-acogi
https://www.pagina12.com.ar/237100-sospechas-sobre-carrio-allanamiento-en-la-afip
https://www.pagina12.com.ar/237101-la-solidaridad-de-la-cgt
https://www.pagina12.com.ar/237118-primer-encuentro-del-pro-tras-la-derrota
https://www.pagina12.com.ar/237122-el-pacto-fiscal-de-macri-paso-a-mejor-vida
https://www.pagina12.com.ar/237127-el-macrismo-no-dara-quorum-y-votara-en-contra-de-la-ley-de-s

Mi solución particular, partiendo de tener el link de la sección como un objeto de bs4

def get_article_links(bs4_object):
    # Parse Featured article
    featured_article = (bs4_object.find(
        'div', attrs={'class': 'featured-article__container'}).a.get('href'))

    # Parse Article List block
    article_list_data = bs4_object.find('ul', attrs={'class': 'article-list'})
    article_links = [article.a.get('href')
                    for article in article_list_data if article.a != None]

    return [featured_article, *article_links]


get_article_links(s_seccion)