Executando o BeatifulSoup e conectando-se de forma confiável e tratando exceções
Executando o BeatifulSoup
Vamos apresentar um exemplo da execução da biblioteca BeutifulSoup:
from urllib.request import urlopenfrom bs4 import BeautifulSouphtml =urlopen('http://www.pythonscraping.com/pages/page1.html')bs =BeautifulSoup(html.read(),'html.parser')print(bs.h1)
Eis a saída:
<h1>An Interesting Title</h1>
Note que somente a primeira instância da tag h1 encontrada na página é devolvida. A função urlopen está sendo importada e a função html.read() é chamada para obter o conteúdo HTML da página.
Objeto BeautifulSoup
Além da string de texto, o BeautifulSoup também pode usar diretamente o objeto de arquivo devolvido por urlopen, sem precisar chamar .read() antes:
bs = BeautifulSoup(html, 'html.parser')
O conteúdo HTML é então transformado em um objeto BeautifulSoup com a seguinte estrutura:
• html → <html><head>...</head><body>...</body></html>
• head → <head><title>A Useful Page<title></head>
• title → <title>A Useful Page</title>
• body → <body><h1>An Int...</h1><div>Lorem ip...</div></body>
• h1 → <h1>An Interesting Title</h1>
• div → <div>Lorem Ipsum dolor...</div>
Observe que a tag h1 que você extraiu da página está aninhada a dois níveis de profundidade na estrutura do objeto BeautifulSoup (html → body → h1). No entanto, ao buscá-la no objeto, é possível acessar a tag h1 diretamente:
Com efeito, qualquer uma das chamadas de função a seguir produziria o mesmo resultado:
Ao criar um objeto BeautifulSoup, dois argumentos são passados:
O primeiro é o texto HTML no qual o objeto se baseia, e o segundo especifica o parser que queremos que o BeautifulSoup use para criar esse objeto. Na maioria dos casos, o parser escolhido não fará diferença. Mas, para citar como exemplos, além do html.parser, outros 2 parsers famosos são o lxml e o html5lib.
Conectando-se de forma confiável e tratando exceções
A web é confusa. Os dados são mal formatados, os sites ficam inativos e tags de fechamento podem estar ausentes. Assim, devemos sempre prever possíveis exceções.
Vamos analisar a primeira linha de nosso scraper, depois das instruções de importação, e descobrir como lidar com qualquer exceção que seja lançada:
Dois erros podem ocorrer nessa linha: • A página não é encontrada no servidor (ou houve um erro ao obtê-la). • O servidor não foi encontrado.
A página não é encontrada no servidor
Nesse caso, um erro de HTTP será devolvido. Esse erro pode ser “404 Page Not Found” (página não encontrada), “500 Internal Server Error” (erro interno do servidor), e assim por diante. Em todos esses casos, a função urlopen lançará a exceção genérica HTTPError. Essa exceção pode ser tratada da seguinte maneira:
Se um código de erro HTTP for devolvido, o programa agora exibirá o erro e não executará o resto do programa que está na instrução else.
Se o servidor não for encontrado
Nesse caso, urlopen lançará um URLError. Isso quer dizer que não foi possível acessar nenhum servidor e, como o servidor remoto é responsável por devolver códigos de status HTTP, um HTTPError não pôde ser lançado, e o erro URLError, mais grave, deve ser capturado. Podemos acrescentar uma verificação para saber se é isso que está acontecendo:
O conteúdo da página não ser exatamente o que você esperava
Sempre que acessar uma tag em um objeto BeautifulSoup, acrescentar uma verificação para garantir que ela realmente exista é uma atitude inteligente. Se você tentar acessar uma tag que não existe, o BeautifulSoup devolverá um objeto None. O problema é que tentar acessar uma tag em um objeto None resultará no lançamento de um AttributeError.
A linha a seguir (em que nonExistentTag é uma tag inventada, e não o nome de uma verdadeira função do BeautifulSoup):
Devolve um objeto None. É perfeitamente razoável conferir e tratar esse objeto. O problema ocorrerá se você não o conferir, mas prosseguir tentando chamar outra função no objeto None, conforme mostra o código a seguir:
Uma exceção será devolvida:
Então, como é possível se proteger nessas duas situações? O modo mais fácil é verificar as duas situações explicitamente:
Reorganizando o código
Nesse exemplo, criamos uma função getTitle que devolve o título da página, ou um objeto None caso tenha havido algum problema para obtê-lo. Em getTitle, verificamos se houve um HTTPError, como no exemplo anterior, e encapsulamos duas das linhas do BeautifulSoup em uma instrução try. Um AttributeError pode ser lançado por qualquer uma dessas linhas (se o servidor não existir, html seria um objeto None e html.read() lançaria um AttributeError). Na verdade, você poderia incluir quantas linhas quiser em uma instrução try, ou chamar outra função totalmente diferente, que poderia lançar um AttributeError em qualquer ponto.
html = urlopen('http://www.pythonscraping.com/pages/page1.html')
from urllib.request import urlopen
from urllib.error import HTTPError
try:
html = urlopen('http://www.pythonscraping.com/pages/page1.html')
except HTTPError as e:
print(e) -> devolve null, executa um break ou algum outro "Plano B"
else: -> o programa continua. Nota: se você retornar ou executar um break no catch da exceção, não será necessário usar a instrução "else"
from urllib.request import urlopen
from urllib.error import HTTPError
from urllib.error import URLError
try:
html = urlopen('https://pythonscrapingthisurldoesnotexist.com')
except HTTPError as e:
print(e)
except URLError as e:
print('The server could not be found!')
else:
print('It Worked!')
print(bs.nonExistentTag)
print(bs.nonExistentTag.someTag)
AttributeError: 'NoneType' object has no attribute 'someTag'
try:
badContent = bs.nonExistingTag.anotherTag
except AttributeError as e:
print('Tag was not found')
else:
if badContent == None:
print ('Tag was not found')
else:
print(badContent)
from urllib.request import urlopen
from urllib.error import HTTPError
from bs4 import BeautifulSoup
def getTitle(url):
try:
html = urlopen(url)
except HTTPError as e:
return None
try:
bs = BeautifulSoup(html.read(), 'html.parser')
title = bs.body.h1
except AttributeError as e:
return None
return title
title = getTitle('http://www.pythonscraping.com/pages/page1.html')
if title == None:
print('Title could not be found')
else:
print(title)