Tính đồng thời và song song: Sự khác biệt đáng kể đối với việc quét web

Cạo, Sự khác biệt, Mar-06-20245 phút đọc

Khi nói đến tính đồng thời và song song, có thể rõ ràng vì chúng đề cập đến các khái niệm tương tự trong việc thực thi các chương trình máy tính trong môi trường đa luồng. Chà, sau khi xem định nghĩa của chúng trong từ điển Oxford, bạn có thể có xu hướng nghĩ như vậy. Tuy nhiên, khi bạn đi sâu hơn vào những khái niệm này liên quan đến

Khi nói đến tính đồng thời và song song, có thể rõ ràng vì chúng đề cập đến các khái niệm tương tự trong việc thực thi các chương trình máy tính trong môi trường đa luồng. Chà, sau khi xem định nghĩa của chúng trong từ điển Oxford, bạn có thể có xu hướng nghĩ như vậy. Tuy nhiên, khi bạn đi sâu hơn vào các khái niệm này liên quan đến cách CPU thực hiện các lệnh chương trình, bạn sẽ nhận thấy rằng tính đồng thời và song song là hai khái niệm riêng biệt. 

Bài viết này đi sâu hơn vào tính đồng thời và song song, cách chúng khác nhau và cách chúng hoạt động cùng nhau để cải thiện năng suất thực thi chương trình. Cuối cùng, nó sẽ thảo luận về hai chiến lược nào phù hợp nhất để quét web. Vì vậy, hãy bắt đầu.

Thực hiện đồng thời là gì?

Đầu tiên, để làm cho mọi thứ đơn giản hơn, chúng ta sẽ bắt đầu với đồng thời trong một ứng dụng duy nhất được thực thi trong một bộ xử lý duy nhất. Dictionary.com định nghĩa đồng thời là một hành động hoặc nỗ lực kết hợp và sự xuất hiện của các sự kiện đồng thời. Tuy nhiên, người ta có thể nói tương tự về thực thi song song khi các lần thực thi trùng khớp, và do đó định nghĩa này có phần gây hiểu lầm trong thế giới lập trình máy tính.

Trong cuộc sống hàng ngày, bạn sẽ có các cuộc hành quyết đồng thời trên máy tính của mình. Ví dụ: bạn có thể đọc một bài viết blog trên trình duyệt của mình trong khi nghe nhạc trên Windows Media Player. Sẽ có một quá trình khác đang chạy: tải xuống tệp PDF từ một trang web khác — tất cả các ví dụ này là các quy trình riêng biệt.

Trước khi phát minh ra các ứng dụng thực thi đồng thời, các CPU đã thực thi tuần tự các chương trình. Điều này ngụ ý rằng các lệnh của một chương trình phải hoàn tất việc thực thi trước khi CPU chuyển sang chương trình tiếp theo.

Ngược lại, thực thi đồng thời xen kẽ một chút của mỗi quá trình cho đến khi tất cả hoàn tất.

Trong một môi trường thực thi đa luồng của bộ xử lý duy nhất, một chương trình thực thi khi một chương trình khác bị chặn cho đầu vào của người dùng. Bây giờ bạn có thể hỏi môi trường đa luồng là gì. Nó là một tập hợp các luồng chạy độc lập với nhau — nhiều hơn về các chủ đề phần tiếp theo.

Không nên nhầm lẫn tính đồng thời với thực hiện song song

Bây giờ, sau đó nó dễ dàng hơn để nhầm lẫn đồng thời với song song. Điều chúng tôi muốn nói về tính đồng thời trong các ví dụ trên là các quy trình không chạy song song. 

Thay vào đó, giả sử một quá trình yêu cầu hoàn thành thao tác Đầu vào / Đầu ra, sau đó Hệ điều hành sẽ phân bổ CPU cho một quy trình khác trong khi nó hoàn thành hoạt động I / O. Thủ tục này sẽ tiếp tục cho đến khi tất cả các quy trình hoàn thành việc thực hiện.

Tuy nhiên, vì việc chuyển đổi các tác vụ của Hệ điều hành xảy ra trong vòng nano hoặc micro giây, nó sẽ xuất hiện với người dùng khi các quy trình được thực hiện song song, 

Chủ đề là gì?

Không giống như trong thực thi tuần tự, CPU có thể không thực thi toàn bộ quá trình / chương trình cùng một lúc với các kiến trúc hiện tại. Thay vào đó, hầu hết các máy tính có thể chia toàn bộ quá trình thành nhiều thành phần nhẹ chạy độc lập với nhau theo thứ tự tùy ý. Đó là những thành phần nhẹ được gọi là chủ đề.

Ví dụ: Google Tài liệu có thể có một số luồng hoạt động đồng thời. Trong khi một luồng tự động lưu công việc của bạn, một luồng khác có thể chạy trong nền, kiểm tra chính tả và ngữ pháp.  

Hệ điều hành xác định thứ tự và luồng nào cần ưu tiên, luồng nào phụ thuộc vào hệ thống.

Thực hiện song song là gì?

Bây giờ bạn đã biết việc thực thi các chương trình máy tính trong một môi trường với một CPU duy nhất. Ngược lại, các máy tính hiện đại thực hiện nhiều quy trình đồng thời trong nhiều CPU, được gọi là thực thi song song. Hầu hết các kiến trúc hiện tại đều có nhiều CPU.

Như bạn có thể thấy trong sơ đồ bên dưới, CPU thực thi từng luồng thuộc về một tiến trình song song với nhau.  

Trong song song, Hệ điều hành chuyển đổi các luồng đến và đi từ CPU trong vòng phân chia macro hoặc micro giây tùy thuộc vào kiến trúc hệ thống. Để Hệ điều hành đạt được thực thi song song, các lập trình viên máy tính sử dụng khái niệm được gọi là lập trình song song. Trong lập trình song song, các lập trình viên phát triển mã để tận dụng tốt nhất nhiều CPU. 

Làm thế nào đồng thời có thể tăng tốc độ quét web

Với rất nhiều tên miền sử dụng quét web để cạo dữ liệu từ các trang web, một nhược điểm đáng kể là thời gian tiêu thụ để cạo một lượng lớn dữ liệu. Nếu bạn không phải là một nhà phát triển dày dạn kinh nghiệm, bạn có thể sẽ lãng phí nhiều thời gian để thử nghiệm các kỹ thuật cụ thể trước khi cuối cùng chạy mã không có lỗi và hoàn hảo.

Phần dưới đây phác thảo một số lý do tại sao web scraping chậm.

Lý do quan trọng tại sao web scraping chậm?

Đầu tiên, scraper phải điều hướng đến trang web mục tiêu trong web scraping. Sau đó, nó sẽ phải kéo và truy xuất các thực thể từ các thẻ HTML mà bạn muốn cạo. Cuối cùng, trong hầu hết các trường hợp, bạn sẽ lưu dữ liệu vào một tệp bên ngoài như định dạng CSV.  

Vì vậy, như bạn có thể thấy, hầu hết các tác vụ trên yêu cầu thao tác I / O bị ràng buộc nặng như kéo dữ liệu từ các trang web và sau đó lưu nó vào các tệp bên ngoài. Điều hướng đến các trang web mục tiêu thường phụ thuộc vào các yếu tố bên ngoài như tốc độ mạng hoặc chờ đợi trong khi mạng trở nên khả dụng.

Như bạn có thể thấy trong hình bên dưới, mức tiêu thụ thời gian cực chậm này có thể làm giảm thêm quá trình cạo khi bạn phải cạo ba hoặc nhiều trang web. Giả sử rằng bạn thực hiện thao tác cạo tuần tự.

Do đó, bằng cách này hay cách khác, bạn sẽ phải áp dụng tính đồng thời hoặc song song cho các hoạt động cạo của mình. Chúng ta sẽ xem xét tính song song đầu tiên trong phần tiếp theo.

Tính đồng thời trong web scraping bằng Python

Tôi chắc chắn rằng bây giờ bạn đã có một cái nhìn tổng quan về tính đồng thời và song song. Phần này sẽ tập trung vào tính đồng thời trong web scraping với một ví dụ mã hóa đơn giản trong Python.

Một ví dụ đơn giản chứng minh mà không cần thực hiện đồng thời

Trong ví dụ này, chúng tôi sẽ cạo URL của các quốc gia theo danh sách các thành phố thủ đô dựa trên dân số từ Wikipedia. Chương trình sẽ lưu các liên kết và sau đó đi đến từng trang trong số 240 trang và lưu HTMLof các trang đó cục bộ.

 Để chứng minh tác động của tính đồng thời, chúng tôi sẽ hiển thị hai chương trình - một chương trình thực thi tuần tự và chương trình còn lại đồng thời với đa luồng.

Đây là mã:

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()

Giải thích mã

Đầu tiên, chúng tôi nhập các thư viện, bao gồm BeautifulSoap, để trích xuất dữ liệu HTML. Các thư viện khác bao gồm yêu cầu truy cập trang web, urllib để tham gia các URL như bạn sẽ khám phá và thư viện thời gian để tìm ra tổng thời gian thực hiện cho chương trình.

Yêu cầu nhập khẩu
từ bs4 nhập khẩu BeautifulSoup
từ urllib.parse nhập urljoin
Thời gian nhập khẩu

Chương trình đầu tiên bắt đầu với mô-đun chính, gọi hàm get_countries(). Hàm sau đó truy cập URL Wikipedia được chỉ định trong biến quốc gia thông qua phiên bản BeautifulSoup thông qua trình phân tích cú pháp HTML.

Sau đó, nó tìm kiếm URL cho danh sách các quốc gia trong bảng bằng cách trích xuất giá trị trong thuộc tính href của thẻ neo.

Các liên kết mà bạn truy xuất là các liên kết tương đối. Hàm urljoin sẽ chuyển đổi chúng thành các liên kết tuyệt đối. Các liên kết này sau đó được nối vào mảng all_countries, nó trở về hàm chính 

Sau đó, hàm fetch lưu nội dung HTML trong mỗi liên kết dưới dạng tệp HTML. Đó là những gì các đoạn mã này làm:

def fetch (liên kết):
    res = requests.get(liên kết)
    Với open(link.split("/")[-1]+".html", "wb") là f:
        f.write (res.content)

Cuối cùng, chức năng chính in thời gian cần thiết để lưu các tệp ở định dạng HTML. Trong PC của chúng tôi, mất 131.22 giây.

Chà, lần này chắc chắn có thể được thực hiện nhanh hơn. Chúng ta sẽ tìm hiểu nó trong phần tiếp theo, nơi cùng một chương trình được thực thi với nhiều luồng.

Chương trình tương tự với tính đồng thời

Trong phiên bản đa luồng, chúng tôi sẽ phải tinh chỉnh các thay đổi nhỏ để chương trình thực thi nhanh hơn.

Hãy nhớ rằng, đồng thời là về việc tạo nhiều luồng và thực thi chương trình. Có hai cách để tạo luồng — thủ công và sử dụng lớp ThreadPoolExecutor. 

Sau khi tạo các luồng theo cách thủ công, bạn có thể sử dụng hàm join trên tất cả các luồng cho phương thức thủ công. Bằng cách đó, phương thức chính sẽ đợi tất cả các luồng hoàn tất việc thực hiện chúng.

Trong chương trình này, chúng ta sẽ thực thi code với lớp ThreadPoolExecutor là một phần của concurrent. mô-đun tương lai. Vì vậy, trước hết, bạn phải đặt dòng dưới đây trong chương trình trên. 

từ concurrent.futures import ThreadPoolExecutor

Sau đó, bạn có thể thay đổi vòng lặp for lưu nội dung HTML ở định dạng HTML như sau:

  với ThreadPoolExecutor(max_workers=32) làm người thực thi:
           executor.map (tìm nạp, clinks)

Đoạn mã trên tạo ra một thread pool với tối đa 32 thread. Đối với mỗi CPU, tham số max_workers khác nhau và bạn cần thử nghiệm với các giá trị khác nhau. Nó không nhất thiết tương đương với số lượng luồng càng cao, thời gian thực hiện càng nhanh.

Vì vậy, trong PC của chúng tôi đã tạo ra đầu ra là 15.14 giây, tốt hơn nhiều so với khi chúng tôi thực thi nó tuần tự.

Vì vậy, trước khi chúng ta chuyển sang phần tiếp theo, đây là mã cuối cùng cho chương trình có thực thi đồng thời:

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()

Làm thế nào song song có thể tăng tốc độ quét web

Bây giờ chúng tôi hy vọng rằng bạn đã đạt được một sự hiểu biết về thực hiện đồng thời. Để giúp bạn phân tích tốt hơn, hãy xem cùng một chương trình hoạt động như thế nào trong môi trường đa bộ xử lý với các quy trình thực thi song song trong nhiều CPU.

Trước tiên, bạn phải nhập mô-đun cần thiết:

từ nhóm nhập đa xử lý, cpu_count

Python cung cấp phương thức cpu_count(), đếm số lượng CPU trong máy của bạn. Nó chắc chắn rất hữu ích trong việc xác định số lượng nhiệm vụ chính xác mà nó có thể thực hiện song song.

Bây giờ bạn phải thay thế mã bằng vòng lặp for trong thực thi tuần tự bằng mã này:

với Pool (cpu_count()) là p:
 
p.map (tìm nạp, clinks)

Sau khi chạy mã này, nó tạo ra thời gian thực thi tổng thể là 20,10 giây , tương đối nhanh hơn so với thực thi tuần tự trong chương trình đầu tiên.

Kết thúc

Tại thời điểm này, chúng tôi hy vọng rằng bạn có thể có một cái nhìn tổng quan toàn diện về lập trình song song và tuần tự — lựa chọn sử dụng cái này hơn cái kia chủ yếu phụ thuộc vào kịch bản cụ thể mà bạn đã phải đối mặt.

Đối với kịch bản quét web, chúng tôi khuyên bạn nên bắt đầu với việc thực thi đồng thời và sau đó chuyển sang một giải pháp song song sẽ rất tuyệt. Chúng tôi hy vọng rằng bạn thích đọc bài viết này. Đừng quên đọc các bài viết khác liên quan đến web scraping như thế này trong blog của chúng tôi.