8.3. Scraper para o site da Hemeroteca Digital de Lisboa#

Esse notebook busca coletar os dados dos periódicos digitalizados e disponíveis no site da Hemeroteca Digital de Lisboa. Os dados serão organizados em um dataframe e exportados para um arquivo csv.

Os dados coletados são:

  • Título: Título do periódico

  • Autoria: Autoria do periódico

  • Período: Período disponível do periódico

  • Link: Link para o periódico

  • Fichas Históricas: Link para as fichas históricas do periódico

  • Quantidade de PDF: Quantidade de PDFs disponíveis do periódico

  • Links dos PDFs: Lista com links para os PDFs disponíveis do periódico

# importar bibliotecas
from bs4 import BeautifulSoup as bs
import requests
import re
import pandas as pd
# definir lista de letras que foram o índice
letras = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U','V', 'X', 'Z']

# definir url base
url_base = 'https://hemerotecadigital.cm-lisboa.pt/Indice/Indice'

# final da url
url_final = '.htm'
# definir lista de urls completa
urls = [url_base + letra + url_final for letra in letras]
# função para criar objeto soup
def criar_soup(url):
    html = requests.get(url)
    soup = bs(html.content, 'html.parser')
    return soup
# find all a href that ends with .htm and start with ../Periodicos or ../OBRAS
def encontrar_links(soup):
    links = soup.find_all('a', href=True)
    links = [link['href'] for link in links if link['href'].endswith('.htm') and (link['href'].startswith('../Periodicos') or link['href'].startswith('../OBRAS'))]
    # replace ../ with https://hemerotecadigital.cm-lisboa.pt/
    links = [link.replace('../', 'https://hemerotecadigital.cm-lisboa.pt/') for link in links]
    return links
# loop com as urls
links_geral = []
for url in urls:
    soup = criar_soup(url)
    links = encontrar_links(soup)
    links_geral.extend(links)
# função para pegar o título e a data de publicação
def pegar_titulo_data(soup):
    titulo_data = soup.find('title').text
    # encontrar data com regex: tudo entre [ e ]
    # se não encontrar [ no título, retorna None
    pattern = r'\['
    if re.search(pattern, titulo_data) == None:
        data = "Sem data"
        titulo = titulo_data
    else:
        data = re.findall(r'\[(.*?)\]', titulo_data)
        # encontrar título com regex: tudo antes de [
        titulo = re.findall(r'(.*?)\[', titulo_data)
    return titulo, data
# Encontrar todos os links que terminam em ".pdf"
def encontrar_pdf(soup, url):
    pdf_links = soup.find_all('a', href=True)
    pdf_links = [link['href'] for link in pdf_links if link['href'].endswith('.pdf') or link['href'].endswith('.PDF')]
    # excluir string após última "/" em url
    url = url.rsplit('/', 1)[0]
    url = url + '/'
    # add url to pdf_links
    pdf_links = [url + link for link in pdf_links]
    return pdf_links
# criar lista geral para os dados
dados_geral = []
# loop nos links
for link in links_geral:
    soup = criar_soup(link)
    titulo, data = pegar_titulo_data(soup)
    pdf_links = encontrar_pdf(soup, link)
    # se FichasHistoricas estiver no link, adicionar à lista de dados na variável fichas
    fichas = "Sem Ficha Histórica"
    if any("FichasHistoricas" in pdf for pdf in pdf_links):
        fichas = [pdf for pdf in pdf_links if "FichasHistoricas" in pdf]
        # correção do link para o pdf
        fichas = fichas[0].replace(link, 'https://hemerotecadigital.cm-lisboa.pt/')
    # contar o número de pdfs
    num_pdfs = len(pdf_links)
    # verificar se algum pdf possui a string"FichasHistoricas"
    # se sim, excluir da lista e subtrair 1 do número de pdfs
    if any("FichasHistoricas" in pdf for pdf in pdf_links):
        pdf_links = [pdf for pdf in pdf_links if "FichasHistoricas" not in pdf]
        num_pdfs = num_pdfs - 1
    # criar lista com os dados
    dados = [titulo, data, num_pdfs, pdf_links, fichas]
    # adicionar dados à lista de dados
    dados_geral.append(dados)
# criar dataframe com headers
df = pd.DataFrame(dados_geral, columns=['Título', 'Data', 'Quantidade de PDF', 'Links dos PDFs', 'Fichas Históricas'])
# transformar itens da coluna 'Título' em string
df['Título'] = df['Título'].astype(str)
# remover colchetes e aspas da coluna 'Título'
df['Título'] = df['Título'].str.replace('[', '')
df['Título'] = df['Título'].str.replace(']', '')
df['Título'] = df['Título'].str.replace("'", '')
df['Título'] = df['Título'].str.replace('"', '')
/tmp/ipykernel_36193/591487736.py:4: FutureWarning: The default value of regex will change from True to False in a future version. In addition, single character regular expressions will *not* be treated as literal strings when regex=True.
  df['Título'] = df['Título'].str.replace('[', '')
/tmp/ipykernel_36193/591487736.py:5: FutureWarning: The default value of regex will change from True to False in a future version. In addition, single character regular expressions will *not* be treated as literal strings when regex=True.
  df['Título'] = df['Título'].str.replace(']', '')
# transformar itens da coluna 'Data' em string
df['Data'] = df['Data'].astype(str)
# remover colchetes e aspas da coluna 'Data'
df['Data'] = df['Data'].str.replace('[', '')
df['Data'] = df['Data'].str.replace(']', '')
df['Data'] = df['Data'].str.replace("'", '')
df['Data'] = df['Data'].str.replace('"', '')
/tmp/ipykernel_36193/3931564129.py:4: FutureWarning: The default value of regex will change from True to False in a future version. In addition, single character regular expressions will *not* be treated as literal strings when regex=True.
  df['Data'] = df['Data'].str.replace('[', '')
/tmp/ipykernel_36193/3931564129.py:5: FutureWarning: The default value of regex will change from True to False in a future version. In addition, single character regular expressions will *not* be treated as literal strings when regex=True.
  df['Data'] = df['Data'].str.replace(']', '')
# se coluna data for vazia, preencher com "Sem data"
df['Data'] = df['Data'].replace('', 'Sem data')

Corrigindo títulos vazios

# se links_pdf não for vazio, pegar o primeiro link
if df['Links dos PDFs'].empty == False:
    # se coluna 'Título' for vazia, preencher com item da coluna 'Links dos PDFs'
    df.loc[df['Título'] == '', 'Título'] = df['Links dos PDFs'].str[0]
# transforma itens da coluna 'Título' em string
df['Título'] = df['Título'].astype(str)
# excluir string até "Periodicos/" ou "OBRAS/" na coluna título usando regex
df['Título'] = df['Título'].str.replace(r'.*Periodicos/', '')
df['Título'] = df['Título'].str.replace(r'.*OBRAS/', '')
# substitui tudo após o primeiro "/" por vazio
df['Título'] = df['Título'].str.replace(r'/.*', '')
/tmp/ipykernel_36193/3298843525.py:2: FutureWarning: The default value of regex will change from True to False in a future version.
  df['Título'] = df['Título'].str.replace(r'.*Periodicos/', '')
/tmp/ipykernel_36193/3298843525.py:3: FutureWarning: The default value of regex will change from True to False in a future version.
  df['Título'] = df['Título'].str.replace(r'.*OBRAS/', '')
/tmp/ipykernel_36193/3298843525.py:5: FutureWarning: The default value of regex will change from True to False in a future version.
  df['Título'] = df['Título'].str.replace(r'/.*', '')

Excluir itens duplicados

# se valor na coluna 'Título' for repetido, exclui linha
df = df.drop_duplicates(subset=['Título'], keep='first')

Limpando as datas

# tente encontrar '\d{4}-\d{4}' na coluna 'Data' senão encontrar busca por '\d{4}'
df['Anos'] = df['Data'].str.findall(r'\d{4}-\d{4}|\d{4}')
# deletar '\d{4}-\d{4}|\d{4}' da coluna 'Data'
df['Data'] = df['Data'].str.replace(r'\d{4}-\d{4}|\d{4}', '')
# deletar ';', '?', da coluna 'Data'
df['Data'] = df['Data'].str.replace(r';|\?', '')
# deletar 'Sem data' da coluna 'Data'
df['Data'] = df['Data'].str.replace(r'Sem data', '')
# deletar espaços em branco da coluna 'Data'
df['Data'] = df['Data'].str.strip()
# adicionar 'Sem local' à coluna 'Data' se estiver vazia
df['Data'] = df['Data'].replace('', 'Sem local')
# renomear coluna 'Data' para 'Local'
df = df.rename(columns={'Data': 'Local'})
/tmp/ipykernel_36193/3049931291.py:2: FutureWarning: The default value of regex will change from True to False in a future version.
  df['Data'] = df['Data'].str.replace(r'\d{4}-\d{4}|\d{4}', '')
/tmp/ipykernel_36193/3049931291.py:4: FutureWarning: The default value of regex will change from True to False in a future version.
  df['Data'] = df['Data'].str.replace(r';|\?', '')
# reorganizar colunas
df = df[['Título', 'Anos', 'Local', 'Fichas Históricas', 'Quantidade de PDF', 'Links dos PDFs']]
# exportar locais únicos para csv
df['Local'].value_counts().sort_values(ascending=False).to_csv('data/locais_hemeroteca_lisboa_raw.csv')
# manualmente corri os locais e criei o csv com os locais corretos
# leia o csv com os locais
locais = pd.read_csv('data/locais_hemeroteca_lisboa.csv', header=None)
# print o datafram locais
print(locais)
                                     0                        1
0                            Sem local                Sem local
1                              Lisboa,                   Lisboa
2                             Coimbra,                  Coimbra
3                      Rio de Janeiro,           Rio de Janeiro
4                               Porto,                    Porto
5                                    ,                Sem local
6                               Indice                Sem local
7                 Série 2 e 3, Lisboa,                   Lisboa
8               I e II Séries, Lisboa,                   Lisboa
9                             Novembro                Sem local
10            Lisboa e Rio de Janeiro,  Lisboa e Rio de Janeiro
11                            Porto, )                    Porto
12                              Praia,                    Praia
13          25 de Abril a 2 de Maio de                Sem local
14                  N.º 629 ao n.º 714                Sem local
15                   Lourenço Marques,         Lourenço Marques
16                           Nova Goa,                 Nova Goa
17                             29 Dez.                Sem local
18                             Outubro                Sem local
19                   N.º 1 ao n.º 52 |                Sem local
20                              Paris,                    Paris
21             Rio de Janeiro, 13 Maio           Rio de Janeiro
22                           Lisboa, -                   Lisboa
23            N.º 1 | 2 de Dezembro de                Sem local
24                          9 Dezembro                Sem local
25                     I e II Séries |                Sem local
26                               Díli,                     Díli
27                             1 Junho                Sem local
28  Rio de Janeiro, 11 a 18 de Maio de           Rio de Janeiro
29                 Washington, October               Washington
30                    Paris, 11-18 Mai                    Paris
31                  Paris, 17-23 Avril                    Paris
32             Luanda, 15 de Agosto de                   Luanda
33                Abril  | N.º  ao n.º                Sem local
34                   N.º 1 ao n.º 35 |                Sem local
35                         21 Novembro                Sem local
36                             30 Maio                Sem local
37                   | N.º 1 ao n.º 28                Sem local
38                 Bolama, Guiné, Nov.                   Bolama
39                              Macau,                    Macau
40                      Coimbra, Junho                  Coimbra
41           Madrid, 11 Mayo   8 Junio                   Madrid
42                          Lisboa,  -                   Lisboa
43                            1.º Ano,                Sem local
# se o valor da coluna 0 no dataframe locais for igual ao valor da coluna 'Local' no dataframe df, substitua o valor da coluna 'Local' no dataframe df pelo valor da coluna 1 no dataframe locais
for i in range(len(locais)):
    df.loc[df['Local'] == locais[0][i], 'Local'] = locais[1][i]
    
df.head(20)
Título Anos Local Fichas Históricas Quantidade de PDF Links dos PDFs
0 Academia Portuguesa [1933] Sem local Sem Ficha Histórica 11 [https://hemerotecadigital.cm-lisboa.pt/Period...
1 O Académico: publicação bi-semanal redigida po... [] Sem local Sem Ficha Histórica 1 [https://hemerotecadigital.cm-lisboa.pt/Period...
2 O academico: revista quinzenal litteraria [1878] Sem local https://hemerotecadigital.cm-lisboa.pt/Periodi... 6 [https://hemerotecadigital.cm-lisboa.pt/Period...
4 O Académico: semanário ilustrado [1902-1903] Sem local https://hemerotecadigital.cm-lisboa.pt/Periodi... 8 [https://hemerotecadigital.cm-lisboa.pt/Period...
5 AccaoColonial_NComemorativo [1934] Sem local Sem Ficha Histórica 1 [https://hemerotecadigital.cm-lisboa.pt/Period...
6 Acção realista [1924-1926] Sem local Sem Ficha Histórica 29 [https://hemerotecadigital.cm-lisboa.pt/Period...
7 La Acción, Ano 1, n.º 1 (1 de Febrero de 1908) [] Sem local Sem Ficha Histórica 1 [https://hemerotecadigital.cm-lisboa.pt/Period...
8 Actas da Câmara Municipal de Lisboa [1974] Sem local Sem Ficha Histórica 3 [https://hemerotecadigital.cm-lisboa.pt/OBRAS/...
9 Actas das sessões da Câmara Municipal de Lisboa [] Sem local Sem Ficha Histórica 8 [https://hemerotecadigital.cm-lisboa.pt/OBRAS/...
11 Água lustral : arte e crítica [1913] Coimbra Sem Ficha Histórica 1 [https://hemerotecadigital.cm-lisboa.pt/Period...
12 costumes [1903-1904] Sem local https://hemerotecadigital.cm-lisboa.pt/Periodi... 12 [https://hemerotecadigital.cm-lisboa.pt/Period...
13 Álbum das Glórias [1880-1902] Lisboa Sem Ficha Histórica 39 [https://hemerotecadigital.cm-lisboa.pt/Period...
14 Album dos artistas [1956-1959] Lisboa Sem Ficha Histórica 48 [https://hemerotecadigital.cm-lisboa.pt/Period...
15 Álbum dos vencidos [1913-1914] Lisboa Sem Ficha Histórica 12 [https://hemerotecadigital.cm-lisboa.pt/Period...
16 O aldrubia [1931-1932] Lisboa Sem Ficha Histórica 4 [https://hemerotecadigital.cm-lisboa.pt/Period...
17 Alma de marialvas : órgão dos Marialvas de S. ... [1957] Sem local https://hemerotecadigital.cm-lisboa.pt/Periodi... 1 [https://hemerotecadigital.cm-lisboa.pt/Period...
18 Alma Nova: quinzenário académico [1931] Sem local https://hemerotecadigital.cm-lisboa.pt/Periodi... 2 [https://hemerotecadigital.cm-lisboa.pt/Period...
20 Alma Nova [1915-1918, 1922-1925, 1927-1929] Sem local https://hemerotecadigital.cm-lisboa.pt/Periodi... 39 [https://hemerotecadigital.cm-lisboa.pt/Period...
21 Altura: Cadernos de Poesia [1945] Sem local https://hemerotecadigital.cm-lisboa.pt/Periodi... 2 [https://hemerotecadigital.cm-lisboa.pt/Period...
22 Almanach das Artes e Letras [1874-1876] Lisboa Sem Ficha Histórica 3 [https://hemerotecadigital.cm-lisboa.pt/Period...
# salvar dataframe em csv
df.to_csv('data/hemeroteca_lisboa.csv', index=False)