Sites em AJAX são populados depois do carregamento da página pelo usuário, com informações enviadas pelo servidor a partir de, geralmente, um javascript.
Para podermos raspá-los precisamos simular uma requisição um pouco mais complexa que o normal. Tentando fazer uma requisição “do jeito comum” nesse site do exemplo, o primeiro problema encontrado é o seguinte:
Input:
>>> requests.get("https://www.clickbus.com.br/refresh/recife-pe/natal-rn?departureDate=2019-12-19&cached=0").text
Output:
<html>\r\n<head>\r\n **<META NAME="robots" CONTENT="noindex,nofollow">** \r\n<script src="/_Incapsula_Resource?SWJIYLWA=5074a744e2e3d891814e9a2dace20bd4,719d34d31c8e3a6e6fffd425f7e032f3">\r\n</script>\r\n<body>\r\n</body></html>'
Provavelmente batemos em uma proteção anti crawler do site.
Para poder simular um comportamento mais humano e conseguir uma resposta consistente do site, uma das principais formas é modificar o “header” da requisição, alterando o User-Agent do navegador e outros parâmetros que o site espera receber. Para saber quais são esses é só olhar na aba de Requisições de Rede do navegador (Ctrl+Shift+E no Firefox), selecionar a requisição certa e procurar os Request Headers.
No caso desse site temos, portanto:
headers = {'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36',
"Host": "www.clickbus.com.br",
"Accept": "text/css,*/*;q=0.1",
"Accept-Language": "pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate, br",
"DNT": "1",
"Connection": "keep-alive"}
Atenção para o fato de que os headers são dicionários e seus parâmetros são uma dupla de chave e valor em formato de string, até mesmo o valor “1” da chave “DNT”.
Fazendo uma requisição com a biblioteca requests
, e especificando o uso destes headers conseguimos receber o conteúdo que procuramos:
Input:
r = requests.get("https://www.clickbus.com.br/refresh/recife-pe/natal-rn?departureDate=2019-12-19&cached=0", headers=headers)
A resposta dada pelo site, enfim, é recebida como um dicionário gigante, em formato json, que podemos ler a partir da seguinte linha:
conteudo = r.json()
Finalmente, por coincidência, este json parece muito com um arquivo HTML, então podemos tentar parseá-lo com BeautifulSoup:
from bs4 import BeautifulSoup as bs
sopa = bs(conteudo["departure"], "html.parser")
for item in sopa.findAll("div"):
print(item.text)