Python Web Scraping - dynamiczne strony internetowe

W tym rozdziale dowiemy się, jak wykonywać skrobanie sieci w dynamicznych witrynach internetowych i szczegółowo omówimy związane z tym koncepcje.

Wprowadzenie

Skrobanie sieci jest złożonym zadaniem, a złożoność wzrasta, jeśli witryna jest dynamiczna. Według Global Audit of Web Accessibility Organizacji Narodów Zjednoczonych ponad 70% stron internetowych ma charakter dynamiczny, a ich funkcjonalność opiera się na JavaScript.

Przykład dynamicznej witryny internetowej

Spójrzmy na przykład dynamicznej strony internetowej i dowiedzmy się, dlaczego trudno ją zeskrobać. W tym miejscu weźmiemy przykład wyszukiwania w witrynie o nazwiehttp://example.webscraping.com/places/default/search.Ale jak możemy powiedzieć, że ta witryna ma dynamiczny charakter? Można to ocenić na podstawie danych wyjściowych następującego skryptu Pythona, który spróbuje zeskrobać dane z wyżej wymienionej strony internetowej -

import re
import urllib.request
response = urllib.request.urlopen('http://example.webscraping.com/places/default/search')
html = response.read()
text = html.decode()
re.findall('(.*?)',text)

Wynik

[ ]

Powyższe dane wyjściowe pokazują, że przykładowy skrobak nie mógł wyodrębnić informacji, ponieważ element <div>, który próbujemy znaleźć, jest pusty.

Podejścia do pobierania danych z dynamicznych witryn internetowych

Widzieliśmy, że skrobak nie może pobrać informacji z dynamicznej witryny internetowej, ponieważ dane są ładowane dynamicznie za pomocą JavaScript. W takich przypadkach możemy użyć dwóch technik pobierania danych z dynamicznych witryn zależnych od JavaScript -

  • Inżynieria odwrotna JavaScript
  • Renderowanie JavaScript

Inżynieria odwrotna JavaScript

Proces zwany inżynierią wsteczną byłby przydatny i pozwala nam zrozumieć, w jaki sposób dane są ładowane dynamicznie przez strony internetowe.

Aby to zrobić, musimy kliknąć plik inspect elementtab dla określonego adresu URL. Następnie klikniemyNETWORK tab, aby znaleźć wszystkie żądania skierowane do tej strony internetowej, w tym search.json ze ścieżką do /ajax. Zamiast uzyskiwać dostęp do danych AJAX z przeglądarki lub przez zakładkę SIEĆ, możemy to zrobić również za pomocą następującego skryptu Pythona -

import requests
url=requests.get('http://example.webscraping.com/ajax/search.json?page=0&page_size=10&search_term=a')
url.json()

Przykład

Powyższy skrypt umożliwia nam dostęp do odpowiedzi JSON przy użyciu metody json w języku Python. Podobnie możemy pobrać nieprzetworzoną odpowiedź w postaci ciągu znaków i używając metody json.loads w Pythonie, możemy ją również załadować. Robimy to za pomocą następującego skryptu Pythona. Zasadniczo zeskrobuje wszystkie kraje, wyszukując literę alfabetu „a”, a następnie iterując wynikowe strony odpowiedzi JSON.

import requests
import string
PAGE_SIZE = 15
url = 'http://example.webscraping.com/ajax/' + 'search.json?page={}&page_size={}&search_term=a'
countries = set()
for letter in string.ascii_lowercase:
   print('Searching with %s' % letter)
   page = 0
   while True:
   response = requests.get(url.format(page, PAGE_SIZE, letter))
   data = response.json()
   print('adding %d records from the page %d' %(len(data.get('records')),page))
   for record in data.get('records'):countries.add(record['country'])
   page += 1
   if page >= data['num_pages']:
      break
   with open('countries.txt', 'w') as countries_file:
   countries_file.write('n'.join(sorted(countries)))

Po uruchomieniu powyższego skryptu otrzymamy następujące dane wyjściowe, a rekordy zostaną zapisane w pliku o nazwie countries.txt.

Wynik

Searching with a
adding 15 records from the page 0
adding 15 records from the page 1
...

Renderowanie JavaScript

W poprzedniej sekcji przeprowadziliśmy inżynierię wsteczną na stronie internetowej, w jaki sposób działa API i jak możemy go użyć do pobrania wyników w pojedynczym żądaniu. Jednak podczas wykonywania inżynierii odwrotnej możemy napotkać następujące trudności -

  • Czasami witryny internetowe mogą być bardzo trudne. Na przykład, jeśli strona internetowa jest utworzona za pomocą zaawansowanego narzędzia przeglądarki, takiego jak Google Web Toolkit (GWT), wynikowy kod JS byłby generowany maszynowo i trudny do zrozumienia i inżynierii wstecznej.

  • Niektóre frameworki wyższego poziomu, takie jak React.js może utrudnić inżynierię wsteczną poprzez abstrakcję już złożonej logiki JavaScript.

Rozwiązaniem powyższych trudności jest użycie silnika renderującego w przeglądarce, który analizuje HTML, stosuje formatowanie CSS i wykonuje JavaScript w celu wyświetlenia strony internetowej.

Przykład

W tym przykładzie do renderowania skryptu Java użyjemy znanego modułu Pythona Selenium. Poniższy kod Pythona wyrenderuje stronę internetową przy pomocy Selenium -

Najpierw musimy zaimportować webdriver z selenu w następujący sposób -

from selenium import webdriver

Teraz podaj ścieżkę sterownika sieciowego, który pobraliśmy zgodnie z naszymi wymaganiami -

path = r'C:\\Users\\gaurav\\Desktop\\Chromedriver'
driver = webdriver.Chrome(executable_path = path)

Teraz podaj adres URL, który chcemy otworzyć w tej przeglądarce internetowej, teraz kontrolowanej przez nasz skrypt Pythona.

driver.get('http://example.webscraping.com/search')

Teraz możemy użyć identyfikatora zestawu narzędzi wyszukiwania do ustawienia elementu do wybrania.

driver.find_element_by_id('search_term').send_keys('.')

Następnie możemy użyć skryptu java, aby ustawić zawartość pola wyboru w następujący sposób -

js = "document.getElementById('page_size').options[1].text = '100';"
driver.execute_script(js)

Poniższy wiersz kodu pokazuje, że wyszukiwanie jest gotowe do kliknięcia na stronie internetowej -

driver.find_element_by_id('search').click()

Następny wiersz kodu pokazuje, że będzie czekał 45 sekund na zakończenie żądania AJAX.

driver.implicitly_wait(45)

Teraz, aby wybrać linki do krajów, możemy użyć selektora CSS w następujący sposób -

links = driver.find_elements_by_css_selector('#results a')

Teraz można wyodrębnić tekst każdego linku w celu utworzenia listy krajów -

countries = [link.text for link in links]
print(countries)
driver.close()