Como raspar um site javascript em Python?

Sep 06 2020

Estou tentando raspar um site. Tentei usar dois métodos, mas ambos não me fornecem o código-fonte completo do site que estou procurando. Estou tentando extrair os títulos das notícias do URL do site fornecido abaixo.

URL: "https://www.todayonline.com/"

Esses são os dois métodos que tentei, mas falhei.

Método 1: bela sopa

tdy_url = "https://www.todayonline.com/"
page = requests.get(tdy_url).text
soup = BeautifulSoup(page)
soup  # Returns me a HTML with javascript text
soup.find_all('h3')

### Returns me empty list []

Método 2: Selênio + BeautifulSoup

tdy_url = "https://www.todayonline.com/"

options = Options()
options.headless = True

driver = webdriver.Chrome("chromedriver",options=options)

driver.get(tdy_url)
time.sleep(10)
html = driver.page_source

soup = BeautifulSoup(html)
soup.find_all('h3')

### Returns me only less than 1/4 of the 'h3' tags found in the original page source 

Por favor ajude. Eu tentei vasculhar outros sites de notícias e é muito mais fácil. Obrigado.

Respostas

2 politicalscientist Sep 06 2020 at 15:38

Você pode acessar os dados via API (verifique a guia Rede):


Por exemplo,

import requests
url = "https://www.todayonline.com/api/v3/news_feed/7"
data = requests.get(url).json()
2 Noname Sep 06 2020 at 15:39

Existem diferentes maneiras de reunir o conteúdo de uma página da web que contém Javascript.

  1. Usando seleniumo driver da web do Firefox
  2. Usando um navegador sem cabeça com phantomJS
  3. Fazer uma chamada de API usando um cliente REST ou requestsbiblioteca python

Você tem que fazer sua pesquisa primeiro

2 MananGajjar Sep 06 2020 at 16:04

Vou sugerir a você uma abordagem bastante simples,

import requests
from bs4 import BeautifulSoup as bs

page = requests.get('https://www.todayonline.com/googlenews.xml').content
soup = bs(page)
news = [i.text for i in soup.find_all('news:title')]

print(news)

resultado

['DBS named world’s best bank by New York-based financial publication',
 'Russia has very serious questions to answer on Navalny - UK',
 "Exclusive: 90% of China's Sinovac employees, families took coronavirus vaccine - CEO",
 'Three militants killed after fatal attack on policeman in Tunisia',
.....]

Além disso, você pode verificar a página XML para obter mais informações, se necessário.

PS Sempre verifique a conformidade antes de descartar qualquer site :)

2 DemianWolf Sep 06 2020 at 16:00

Os dados de notícias no site que você está tentando copiar são obtidos do servidor usando JavaScript (isso é chamado de XHR - XMLHttpRequest ). Isso está acontecendo dinamicamente, enquanto a página está carregando ou sendo rolada. portanto, esses dados não são retornados dentro da página retornada pelo servidor.

No primeiro exemplo, você está recebendo apenas a página retornada pelo servidor - sem as notícias, mas com o JS que deve obtê-las. Nem as solicitações nem o BeautifulSoup podem executar JS.

No entanto, você pode tentar reproduzir solicitações que estão obtendo títulos de notícias do servidor com solicitações Python. Execute as seguintes etapas:

  1. Abra DevTools do seu navegador (normalmente você tem que pressionar F12ou Ctrl+ Shit+ Ipara isso) e dê uma olhada nas solicitações que estão obtendo títulos de notícias do servidor. Às vezes, é ainda mais fácil do que web scraping com BeautifulSoup. Aqui está uma captura de tela (Firefox):
  1. Copie o link de solicitação (clique com o botão direito -> Copiar -> Copiar link) e passe-o para requests.get(...).

  2. Saia .json()do pedido. Ele retornará um dict que é fácil de trabalhar. Para entender melhor a estrutura do dicionário, eu recomendaria usar em pprintvez da impressão simples. Observe que você deve fazer from pprint import pprintantes de usá-lo.

Aqui está um exemplo do código que obtém os títulos das principais notícias da página:

import requests


nodes = requests.get("https://www.todayonline.com/api/v3/news_feed/7")\
        .json()["nodes"]
for node in nodes:
    print(node["node"]["title"])

Se você quiser raspar um grupo de notícias sob legenda, você precisa alterar o número após news_feed/na URL de solicitação (para obtê-lo, você só precisa filtrar as solicitações por "news_feed" no DevTools e rolar a página de notícias para baixo).

Às vezes, os sites têm proteção contra bots (embora o site que você está tentando copiar não tenha). Nesses casos, você também pode precisar executar essas etapas .