ciemne logo proxyscrape

Współbieżność a równoległość: Znaczące różnice w skrobaniu stron internetowych

Skrobanie, Różnice, styczeń-01-20225 minut czytania

When it comes to concurrency vs. parallelism, it may be apparent as they refer to the same concepts in executions of computer programs in a multi-threaded environment. Well, after looking at their definitions in the Oxford dictionary, you may be inclined to think so. However, when you go deeper into these notions with respect to

Jeśli chodzi o współbieżność a równoległość, może się wydawać, że odnoszą się one do tych samych pojęć w wykonywaniu programów komputerowych w środowisku wielowątkowym. Cóż, po zapoznaniu się z ich definicjami w słowniku Oxford, można być skłonnym tak myśleć. Gdy jednak zagłębimy się w te pojęcia w odniesieniu do sposobu, w jaki procesor wykonuje instrukcje programu, zauważymy, że współbieżność i równoległość to dwa odrębne pojęcia. 

Niniejszy artykuł zagłębia się w temat współbieżności i równoległości, ich różnic i współpracy w celu poprawy wydajności wykonywania programów. Na koniec omówimy, które dwie strategie są najbardziej odpowiednie do skrobania stron internetowych. Zacznijmy więc.

Czym jest wykonywanie współbieżne?

Po pierwsze, aby uprościć sprawę, zaczniemy od współbieżności w pojedynczej aplikacji wykonywanej przez pojedynczy procesor. Dictionary. com definiuje współbieżność jako połączone działanie lub wysiłek oraz występowanie jednoczesnych zdarzeń. Można jednak powiedzieć to samo o równoległym wykonywaniu, ponieważ egzekucje są zbieżne, a zatem definicja ta jest nieco myląca w świecie programowania komputerowego.

W codziennym życiu na komputerze wykonywane są równoległe operacje. Na przykład, możesz czytać artykuł na blogu w przeglądarce, słuchając muzyki w odtwarzaczu Windows Media Player. Byłby też uruchomiony inny proces: pobieranie pliku PDF z innej strony internetowej - wszystkie te przykłady są oddzielnymi procesami.

Przed wynalezieniem współbieżnie wykonywanych aplikacji, procesory wykonywały programy sekwencyjnie. Oznaczało to, że instrukcje jednego programu musiały zakończyć wykonywanie, zanim procesor przeszedł do następnego.

W przeciwieństwie do tego, współbieżne wykonywanie naprzemiennie wykonuje niewielką część każdego procesu, aż wszystkie zostaną ukończone.

W jednoprocesorowym, wielowątkowym środowisku wykonawczym, jeden program wykonuje się, gdy inny jest zablokowany na wejście użytkownika. Teraz możesz zapytać, czym jest środowisko wielowątkowe. Jest to zbiór wątków, które działają niezależnie od siebie - więcej o wątkach w następnej sekcji.

Współbieżności nie należy mylić z wykonywaniem równoległym

Teraz łatwiej jest pomylić współbieżność z równoległością. W powyższych przykładach przez współbieżność rozumiemy to, że procesy nie działają równolegle. 

Zamiast tego, powiedzmy, że jeden proces wymaga ukończenia operacji wejścia/wyjścia, wówczas system operacyjny przydzieli CPU innemu procesowi, podczas gdy on ukończy swoją operację wejścia/wyjścia. Procedura ta będzie kontynuowana do momentu, aż wszystkie procesy zakończą swoje wykonywanie.

Jednakże, ponieważ przełączanie zadań przez system operacyjny odbywa się w ciągu nano lub mikrosekund, użytkownikowi wydaje się, że procesy są wykonywane równolegle, 

Czym jest wątek?

W przeciwieństwie do sekwencyjnego wykonywania, CPU nie może wykonać całego procesu/programu na raz z obecnymi architekturami. Zamiast tego większość komputerów może podzielić cały proces na kilka lekkich komponentów, które działają niezależnie od siebie w dowolnej kolejności. To właśnie te lekkie komponenty nazywane są wątkami.

Na przykład, Dokumenty Google mogą mieć kilka wątków, które działają jednocześnie. Podczas gdy jeden wątek automatycznie zapisuje pracę, inny może działać w tle, sprawdzając pisownię i gramatykę.  

System operacyjny określa kolejność i priorytety wątków, co jest zależne od systemu.

Czym jest wykonywanie równoległe?

Teraz wiesz, że wykonywanie programów komputerowych odbywa się w środowisku z jednym procesorem. W przeciwieństwie do tego, nowoczesne komputery wykonują wiele procesów jednocześnie na wielu procesorach, co znane jest jako wykonywanie równoległe. Większość obecnych architektur posiada wiele procesorów.

Jak widać na poniższym diagramie, CPU wykonuje każdy wątek należący do procesu równolegle ze sobą.  

W trybie równoległym system operacyjny przełącza wątki do i z procesora w ułamkach makro lub mikrosekund, w zależności od architektury systemu. Aby system operacyjny mógł osiągnąć równoległe wykonanie, programiści komputerowi używają koncepcji znanej jako programowanie równoległe. W programowaniu równoległym programiści opracowują kod, aby jak najlepiej wykorzystać wiele procesorów. 

Jak współbieżność może przyspieszyć skrobanie stron internetowych

Przy tak wielu domenach wykorzystujących web scraping do skrobania danych ze stron internetowych, istotną wadą jest czas, jaki zajmuje skrobanie ogromnych ilości danych. Jeśli nie jesteś doświadczonym programistą, możesz stracić mnóstwo czasu na eksperymentowanie z określonymi technikami, zanim ostatecznie uruchomisz kod bezbłędnie i idealnie.

Poniższa sekcja przedstawia niektóre z powodów, dla których skrobanie stron internetowych jest powolne.

Istotne powody, dla których skrobanie stron internetowych jest powolne?

Po pierwsze, skrobak musi przejść do strony docelowej w skrobaniu stron internetowych. Następnie musi pobrać i pobrać encje ze znaczników HTML, z których chcesz zeskrobać. Wreszcie, w większości przypadków dane zapisywane są do pliku zewnętrznego, np. w formacie CSV.  

Jak więc widać, większość z powyższych zadań wymaga ciężkich operacji wejścia/wyjścia, takich jak pobieranie danych ze stron internetowych, a następnie zapisywanie ich w plikach zewnętrznych. Nawigacja do docelowych stron internetowych często zależy od czynników zewnętrznych, takich jak szybkość sieci lub oczekiwanie, aż sieć stanie się dostępna.

Jak widać na poniższym rysunku, to ekstremalnie wolne zużycie czasu może dodatkowo utrudniać proces scrapingu, gdy trzeba scrape'ować trzy lub więcej stron internetowych. Zakłada się, że operacja scrapingu jest przeprowadzana sekwencyjnie.

Dlatego w ten czy inny sposób będziesz musiał zastosować współbieżność lub równoległość do operacji skrobania. Równoległości przyjrzymy się najpierw w następnej sekcji.

Współbieżność w skrobaniu stron internetowych przy użyciu Pythona

Jestem pewien, że masz już ogólny zarys współbieżności i równoległości. Ta sekcja skupi się na współbieżności w skrobaniu stron internetowych z prostym przykładem kodowania w Pythonie.

Prosty przykład demonstrujący wykonanie bez współbieżności

W tym przykładzie będziemy pobierać adresy URL krajów według listy stolic na podstawie liczby ludności z Wikipedii. Program zapisze linki, a następnie przejdzie do każdej z 240 stron i zapisze HTML tych stron lokalnie.

 Aby zademonstrować efekty współbieżności, pokażemy dwa programy - jeden z sekwencyjnym wykonywaniem, a drugi współbieżnie z wielowątkowością.

Oto kod:

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
import time

def get_countries():
    countries = 'https://en.wikipedia.org/wiki/List_of_national_capitals_by_population'
    all_countries = []
    response = requests.get(countries)
    soup = BeautifulSoup(response.text, "html.parser")
    countries_pl = soup.select('th .flagicon+ a')
    for link_pl in countries_pl:
        link = link_pl.get("href")
        link = urljoin(countries, link)
        
        all_countries.append(link)
    return all_countries
  
def fetch(link):
    res = requests.get(link)
    with open(link.split("/")[-1]+".html", "wb") as f:
        f.write(res.content)
  

        
def main():
    clinks = get_countries()
    print(f"Total pages: {len(clinks)}")
    start_time = time.time()
    for link in clinks:
        fetch(link)
 
    duration = time.time() - start_time
    print(f"Downloaded {len(links)} links in {duration} seconds")
main()

Wyjaśnienie kodu

Po pierwsze, importujemy biblioteki, w tym BeautifulSoap, aby wyodrębnić dane HTML. Inne biblioteki obejmują żądanie dostępu do strony internetowej, urllib do łączenia adresów URL, jak się dowiesz, oraz bibliotekę czasu, aby sprawdzić całkowity czas wykonania programu.

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
import time

Program rozpoczyna się od modułu głównego, który wywołuje funkcję get_countries(). Funkcja ta następnie uzyskuje dostęp do adresu URL Wikipedii określonego w zmiennej countries za pośrednictwem instancji BeautifulSoup poprzez parser HTML.

Następnie wyszukuje adres URL dla listy krajów w tabeli, wyodrębniając wartość w atrybucie href znacznika zakotwiczenia.

Pobierane linki są linkami względnymi. Funkcja urljoin przekonwertuje je na linki bezwzględne. Linki te są następnie dołączane do tablicy all_countries, którą funkcja zwraca do funkcji głównej 

Następnie funkcja pobierania zapisuje zawartość HTML w każdym linku jako plik HTML. To właśnie robią te fragmenty kodu:

def fetch(link):
    res = requests.get(link)
    with open(link.split("/")[-1]+".html", "wb") as f:
        f.write(res.content)

Wreszcie, główna funkcja drukuje czas potrzebny do zapisania plików w formacie HTML. Na naszym komputerze zajęło to 131,22 sekundy.

Cóż, ten czas z pewnością można przyspieszyć. Przekonamy się o tym w następnej sekcji, w której ten sam program jest wykonywany w wielu wątkach.

Ten sam program ze współbieżnością

W wersji wielowątkowej musielibyśmy wprowadzić drobne zmiany, aby program wykonywał się szybciej.

Należy pamiętać, że współbieżność polega na tworzeniu wielu wątków i wykonywaniu programu. Istnieją dwa sposoby tworzenia wątków - ręcznie i przy użyciu klasy ThreadPoolExecutor. 

Po ręcznym utworzeniu wątków można użyć funkcji join na wszystkich wątkach dla metody ręcznej. W ten sposób główna metoda będzie czekać na zakończenie wykonywania wszystkich wątków.

W tym programie wykonamy kod za pomocą klasy ThreadPoolExecutor, która jest częścią modułu concurrent. futures. Przede wszystkim należy więc umieścić poniższą linię w powyższym programie. 

from concurrent.futures import ThreadPoolExecutor

Następnie można zmienić pętlę for, która zapisuje zawartość HTML w formacie HTML w następujący sposób:

  with ThreadPoolExecutor(max_workers=32) as executor:
           executor.map(fetch, clinks)

Powyższy kod tworzy pulę wątków z maksymalną liczbą 32 wątków. Dla każdego procesora parametr max_workers jest inny i należy poeksperymentować z różnymi wartościami. Niekoniecznie oznacza to, że im większa liczba wątków, tym szybszy czas wykonania.

Tak więc w naszym komputerze wydajność wyniosła 15,14 sekundy, co jest znacznie lepszym wynikiem niż w przypadku sekwencyjnego wykonywania.

Zanim przejdziemy do następnej sekcji, oto ostateczny kod programu z wykonywaniem współbieżnym:

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
from concurrent.futures import ThreadPoolExecutor
import time

def get_countries():
    countries = 'https://en.wikipedia.org/wiki/List_of_national_capitals_by_population'
    all_countries = []
    response = requests.get(countries)
    soup = BeautifulSoup(response.text, "html.parser")
    countries_pl = soup.select('th .flagicon+ a')
    for link_pl in countries_pl:
        link = link_pl.get("href")
        link = urljoin(countries, link)
        
        all_countries.append(link)
    return all_countries
  
def fetch(link):
    res = requests.get(link)
    with open(link.split("/")[-1]+".html", "wb") as f:
        f.write(res.content)


def main():
  clinks = get_countries()
  print(f"Total pages: {len(clinks)}")
  start_time = time.time()
  

  with ThreadPoolExecutor(max_workers=32) as executor:
           executor.map(fetch, clinks)
        
 
  duration = time.time()-start_time
  print(f"Downloaded {len(clinks)} links in {duration} seconds")
main()

Jak równoległość może przyspieszyć skrobanie stron internetowych

Mamy nadzieję, że udało nam się już zrozumieć współbieżne wykonywanie programu. Aby pomóc w lepszej analizie, przyjrzyjmy się, jak ten sam program działa w środowisku wieloprocesorowym z procesami wykonywanymi równolegle w wielu procesorach.

Najpierw należy zaimportować wymagany moduł :

from multiprocessing import Pool,cpu_count

Python udostępnia metodę cpu_count(), która zlicza liczbę procesorów w komputerze. Jest to niewątpliwie pomocne w określeniu dokładnej liczby zadań, które mogą być wykonywane równolegle.

Teraz należy zastąpić kod z pętlą for w wykonaniu sekwencyjnym tym kodem:

with Pool (cpu_count()) as p:
 
   p.map(fetch,clinks)

Po uruchomieniu tego kodu całkowity czas wykonania wyniósł 20,10 sekundy, czyli stosunkowo szybciej niż w przypadku sekwencyjnego wykonywania pierwszego programu.

Wnioski

Mamy nadzieję, że w tym momencie masz już kompleksowy przegląd programowania równoległego i sekwencyjnego - wybór jednego z nich zależy przede wszystkim od konkretnego scenariusza, z którym się zetknąłeś.

W przypadku scenariusza skrobania stron internetowych zalecamy rozpoczęcie od współbieżnego wykonywania, a następnie przejście do rozwiązania równoległego. Mamy nadzieję, że podobał Ci się ten artykuł, ale nie zapomnij również przeczytać innych artykułów związanych z web scrapingiem, takich jak ten na naszym blogu.