Python đọc csv không đồng bộ

pd.read_csv không phải là một phương pháp không đồng bộ, vì vậy tôi không tin rằng bạn thực sự nhận được bất kỳ sự song song nào từ phương pháp này. Bạn cần sử dụng thư viện tệp không đồng bộ như aiofiles để đọc các tệp vào bộ đệm một cách không đồng bộ, sau đó gửi chúng tới pd.read_csv(.)

Lưu ý rằng hầu hết các hệ thống tệp không thực sự không đồng bộ, vì vậy, aiofiles về mặt chức năng là một nhóm luồng. Tuy nhiên, nó vẫn có khả năng nhanh hơn đọc nối tiếp


Đây là một ví dụ tôi đã có với aiohttp nhận csv từ các url

import io
import asyncio

import aiohttp
import pandas as pd

async def get_csv_async(client, url):
    # Send a request.
    async with client.get(url) as response:
        # Read entire resposne text and convert to file-like using StringIO().
        with io.StringIO(await response.text()) as text_io:
            return pd.read_csv(text_io)

async def get_all_csvs_async(urls):
    async with aiohttp.ClientSession() as client:
        # First create all futures at once.
        futures = [ get_csv_async(client, url) for url in urls ]
        # Then wait for all the futures to complete.
        return await asyncio.gather(*futures)

urls = [
    # Some random CSV urls from the internet
    'https://people.sc.fsu.edu/~jburkardt/data/csv/hw_25000.csv',
    'https://people.sc.fsu.edu/~jburkardt/data/csv/addresses.csv',
    'https://people.sc.fsu.edu/~jburkardt/data/csv/airtravel.csv',
]

if '__main__' == __name__:
    # Run event loop
    # can just do `csvs = asyncio.run(get_all_csvs_async(urls))` in python 3.7+
    csvs = asyncio.get_event_loop().run_until_complete(get_all_csvs_async(urls))

    for csv in csvs:
        print(csv)

Nếu doanh nghiệp của bạn hoạt động trong một thị trường cạnh tranh và luôn thay đổi, thì có nhiều khả năng bạn sẽ thu thập một lượng lớn dữ liệu để rút ra những hiểu biết hữu ích. Thông thường, dữ liệu này không thể được trích xuất từ ​​​​trang web một cách dễ dàng. Thay vào đó, để đảm bảo nó hoàn chỉnh và có chất lượng cao, bạn phải lặp lại nhiều trang trong khi cạo bằng các công cụ thu thập dữ liệu khác nhau

Mã không đồng bộ đã trở thành lựa chọn hàng đầu cho các lập trình viên muốn xử lý một số lượng lớn URL cùng một lúc. Nó cho phép họ thực hiện nhiều nhiệm vụ hơn và quan trọng nhất là thực hiện nhanh chóng. Trong hướng dẫn này, chúng tôi sẽ tập trung vào việc mô tả cách tiếp cận không đồng bộ để thu thập nhiều URL và bằng cách so sánh nó với cách tiếp cận đồng bộ, chứng minh lý do tại sao nó có thể có lợi hơn

Bạn cũng có thể xem một trong các video của chúng tôi để có hình ảnh minh họa về cùng một hướng dẫn quét web

Quét web không đồng bộ là gì?

Quét web không đồng bộ, còn được gọi là không chặn hoặc đồng thời, là một kỹ thuật đặc biệt cho phép bạn bắt đầu một nhiệm vụ có khả năng kéo dài mà vẫn có cơ hội phản hồi các sự kiện khác, thay vì phải đợi nhiệm vụ dài đó kết thúc

Ngoài ra, với thư viện asyncio cung cấp một số lượng lớn các công cụ để viết mã không chặn và AIOHTTP cung cấp chức năng chính xác hơn nữa cho các yêu cầu HTTP, không có gì ngạc nhiên khi việc quét không đồng bộ lại trở nên phổ biến như vậy đối với các nhà phát triển.  

Hãy nhớ rằng mặc dù bài đăng trên blog này chứng minh các lợi ích của phương pháp không đồng bộ, nhưng việc loại bỏ nhiều URL cùng một lúc cũng có thể đạt được thông qua nhiều luồng và quy trình. Để biết thêm chi tiết, hãy xem một trong những hướng dẫn từng bước khác của chúng tôi.  

Đồng bộ hóa so với. không đồng bộ. Có gì khác biệt?

So với không đồng bộ, cách tiếp cận đồng bộ để thu thập nhiều URL trang web đề cập đến hoạt động chạy một yêu cầu tại một thời điểm. Về bản chất, khi sử dụng mã này, người dùng sẽ xử lý một URL và chỉ chuyển sang xử lý URL tiếp theo sau khi URL trước đó đã xử lý xong.  

Do đó, sự khác biệt chính giữa hai cách tiếp cận là mã đồng bộ ngăn yêu cầu tiếp theo chạy trong khi mã không đồng bộ cho phép bạn cạo dữ liệu từ nhiều trang web cùng một lúc. Điều này dẫn chúng ta đến lợi ích chính của phương pháp không đồng bộ – hiệu quả về thời gian cao. Bạn không còn phải đợi quá trình quét một trang kết thúc trước khi bắt đầu trang khác.  

Bây giờ bạn đã hiểu rõ về sự khác biệt chính giữa mã đồng bộ và mã không đồng bộ, chúng ta có thể chuyển sang phần hướng dẫn quét web và tạo mã Python mẫu cho từng cách tiếp cận

Hướng dẫn cạo đồng bộ

Trong hướng dẫn này, chúng tôi sẽ cạo các URL được xác định trong url. csv bằng cách sử dụng phương pháp đồng bộ. Đối với trường hợp sử dụng cụ thể này, mô-đun “yêu cầu” của python là một công cụ lý tưởng. Hãy bắt đầu bằng cách tạo một tệp Python trống với chức năng chính

def main():
    print('Saving the output of extracted information')

main()

Theo dõi hiệu suất của tập lệnh của bạn luôn là một ý tưởng hay. Do đó, bước tiếp theo là thêm mã theo dõi thời gian thực thi tập lệnh.  

Đầu tiên, ghi lại thời gian ngay từ đầu kịch bản. Sau đó, nhập bất kỳ mã nào cần đo – trong trường hợp này, chúng tôi đang sử dụng một câu lệnh “in” duy nhất. Cuối cùng, tính toán thời gian đã trôi qua. Điều này có thể được thực hiện bằng cách lấy thời gian hiện tại và trừ đi thời gian khi bắt đầu tập lệnh. Khi chúng tôi biết bao nhiêu thời gian đã trôi qua, chúng tôi có thể in nó trong khi làm tròn số float kết quả đến 2 số thập phân cuối cùng

import time


def main():
    start_time = time.time()

    print('Saving the output of extracted information')

    time_difference = time.time() - start_time
    print(f'Scraping time: %.2f seconds.' % time_difference)

Bây giờ, việc chuẩn bị đã hoàn tất, đã đến lúc đọc tệp csv chứa URL. Ở đó, bạn sẽ thấy một cột duy nhất được gọi là url, cột này sẽ chứa các URL phải được quét để lấy dữ liệu

Tiếp theo, chúng ta phải mở các url. csv. Sau đó, tải nó bằng mô-đun csv và lặp qua từng URL từ tệp csv

import csv
import time


def main():
    start_time = time.time()

    print('Saving the output of extracted information')
    with open('urls.csv') as file:
        csv_reader = csv.DictReader(file)
        for csv_row in csv_reader:
            # the url from csv can be found in csv_row['url']
            print(csv_row['url'])

    time_difference = time.time() - start_time
    print(f'Scraping time: %.2f seconds.' % time_difference)

Tại thời điểm này, công việc gần như đã hoàn thành - tất cả những gì còn lại phải làm là cạo nó, mặc dù trước khi bạn làm điều đó, hãy xem dữ liệu bạn đang cạo

Tựa đề của cuốn sách “Ánh sáng trên gác mái” có thể được trích ra từ một

thẻ, được bao bọc bởi một

gắn thẻ với lớp product_main

Đối với thông tin sản phẩm, nó có thể được tìm thấy trong một bảng có lớp sọc bảng, bạn có thể xem trong phần công cụ dành cho nhà phát triển

Bây giờ, hãy sử dụng những gì chúng ta đã học và tạo một hàm cạo

Chức năng cạo đưa ra yêu cầu tới URL mà chúng tôi đã tải từ tệp csv. Khi yêu cầu được thực hiện, nó sẽ tải HTML phản hồi bằng cách sử dụng mô-đun BeautifulSoup. Sau đó, chúng tôi sử dụng kiến ​​thức về nơi dữ liệu được lưu trữ trong thẻ HTML để trích xuất tên sách vào biến book_name và thu thập tất cả thông tin sản phẩm vào từ điển product_info

import csv
import time
import requests as requests
from bs4 import BeautifulSoup


def scrape(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')
    book_name = soup.select_one('.product_main').h1.text
    rows = soup.select('.table.table-striped tr')
    product_info = {row.th.text: row.td.text for row in rows}

def main():
    start_time = time.time()

    print('Saving the output of extracted information')
    with open('urls.csv') as file:
        csv_reader = csv.DictReader(file)
        for csv_row in csv_reader:
            scrape(csv_row['url'])

    time_difference = time.time() - start_time
    print(f'Scraping time: %.2f seconds.' % time_difference)


main()

URL bị loại bỏ; . Vì vậy, đã đến lúc thêm một chức năng khác - save_product

save_product có hai tham số. tên sách và từ điển thông tin sản phẩm. Vì tên sách chứa dấu cách, trước tiên chúng tôi thay thế chúng bằng dấu gạch dưới. Cuối cùng, chúng tôi tạo một tệp JSON và kết xuất tất cả thông tin chúng tôi có vào đó. Đảm bảo rằng bạn tạo một thư mục dữ liệu trong thư mục tập lệnh nơi tất cả các tệp JSON sẽ được lưu

import csv
import json
import time
import requests
from bs4 import BeautifulSoup


def save_product(book_name, product_info):
    json_file_name = book_name.replace(' ', '_')
    with open(f'data/{json_file_name}.json', 'w') as book_file:
        json.dump(product_info, book_file)


def scrape(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')
    book_name = soup.select_one('.product_main').h1.text
    rows = soup.select('.table.table-striped tr')
    product_info = {row.th.text: row.td.text for row in rows}
    save_product(book_name, product_info)


def main():
    start_time = time.time()

    print('Saving the output of extracted information')
    with open('urls.csv') as file:
        csv_reader = csv.DictReader(file)
        for csv_row in csv_reader:
            scrape(csv_row['url'])

    time_difference = time.time() - start_time
    print(f'Scraping time: %.2f seconds.' % time_difference)


main()

Bây giờ, đã đến lúc chạy tập lệnh và xem dữ liệu. Ở đây, chúng ta cũng có thể thấy thời gian cạo mất bao nhiêu – trong trường hợp này là 17. 54 giây

Hướng dẫn cạo không đồng bộ

Đối với bước tiếp theo, chúng ta hãy xem hướng dẫn Python không đồng bộ. Đối với trường hợp sử dụng này, chúng tôi sẽ sử dụng mô-đun aiohttp

Hãy bắt đầu bằng cách tạo một tệp python trống với chức năng chính. Lưu ý rằng chức năng chính được đánh dấu là không đồng bộ. Chúng tôi sử dụng vòng lặp asyncio để ngăn tập lệnh thoát cho đến khi chức năng chính hoàn thành

import asyncio


async def main():
    print('Saving the output of extracted information')


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Một lần nữa, bạn nên theo dõi hiệu suất của tập lệnh của mình. Với mục đích đó, hãy viết mã theo dõi thời gian thực thi tập lệnh

Như với ví dụ đầu tiên, hãy ghi lại thời gian khi bắt đầu tập lệnh. Sau đó, nhập bất kỳ mã nào bạn cần đo (hiện tại là một câu lệnh in duy nhất). Cuối cùng, tính toán thời gian đã trôi qua bằng cách lấy thời gian hiện tại và trừ đi thời gian khi bắt đầu tập lệnh. Khi chúng tôi có bao nhiêu thời gian đã trôi qua, chúng tôi in nó trong khi làm tròn số float kết quả thành 2 số thập phân cuối cùng

import asyncio
import time


async def main():
    start_time = time.time()

    print('Saving the output of extracted information')

    time_difference = time.time() - start_time
    print(f'Scraping time: %.2f seconds.' % time_difference)


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Đã đến lúc đọc tệp csv chứa URL. Tệp sẽ chứa một cột duy nhất được gọi là url. Ở đó, bạn sẽ thấy tất cả các URL cần được lấy dữ liệu

Tiếp theo, chúng tôi mở các url. csv, sau đó tải nó bằng mô-đun csv và lặp qua từng URL trong tệp csv. Ngoài ra, chúng tôi cần tạo một tác vụ không đồng bộ cho mọi URL mà chúng tôi sẽ thu thập

________số 8_______

Sau này trong chức năng, chúng tôi đợi tất cả các tác vụ cạo hoàn thành trước khi tiếp tục

import asyncio
import csv
import time


async def main():
    start_time = time.time()

    tasks = []
    with open('urls.csv') as file:
        csv_reader = csv.DictReader(file)
        for csv_row in csv_reader:
            task = asyncio.create_task(scrape(csv_row['url']))
            tasks.append(task)

    print('Saving the output of extracted information')
    await asyncio.gather(*tasks)

    time_difference = time.time() - start_time
    print(f'Scraping time: %.2f seconds.' % time_difference)


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Tất cả những gì còn lại là cạo. Nhưng trước khi làm điều đó, hãy nhớ xem qua dữ liệu bạn đang cạo

Tiêu đề của cuốn sách có thể được trích xuất từ ​​một

thẻ, được bao bọc bởi một

gắn thẻ với lớp product_main

Về thông tin sản xuất, nó có thể được tìm thấy trong một bảng có lớp sọc bảng

Bây giờ, hãy tạo một chức năng cạo

Chức năng cạo đưa ra yêu cầu tới URL mà chúng tôi đã tải từ tệp csv. Khi yêu cầu được thực hiện, nó sẽ tải HTML phản hồi bằng cách sử dụng mô-đun BeautifulSoup. Sau đó, chúng tôi sử dụng kiến ​​thức về nơi dữ liệu được lưu trữ trong thẻ HTML để trích xuất tên sách vào biến book_name và thu thập tất cả thông tin sản phẩm vào từ điển product_info

import asyncio
import csv
import time
import aiohttp as aiohttp
from bs4 import BeautifulSoup


async def scrape(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            body = await resp.text()
            soup = BeautifulSoup(body, 'html.parser')
            book_name = soup.select_one('.product_main').h1.text
            rows = soup.select('.table.table-striped tr')
            product_info = {row.th.text: row.td.text for row in rows}


async def main():
    start_time = time.time()

    tasks = []
    with open('urls.csv') as file:
        csv_reader = csv.DictReader(file)
        for csv_row in csv_reader:
            task = asyncio.create_task(scrape(csv_row['url']))
            tasks.append(task)

    print('Saving the output of extracted information')
    await asyncio.gather(*tasks)

    time_difference = time.time() - start_time
    print(f'Scraping time: %.2f seconds.' % time_difference)


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

URL bị loại bỏ; . Để làm được điều đó, bạn cần thêm một chức năng khác – save_product

save_product có hai tham số. tên sách và từ điển thông tin sản phẩm. Vì tên sách chứa dấu cách, trước tiên chúng tôi thay thế chúng bằng dấu gạch dưới. Cuối cùng, chúng tôi tạo một tệp json và đổ tất cả thông tin chúng tôi có vào đó

import time


def main():
    start_time = time.time()

    print('Saving the output of extracted information')

    time_difference = time.time() - start_time
    print(f'Scraping time: %.2f seconds.' % time_difference)
0

Cuối cùng, bạn có thể chạy tập lệnh và xem dữ liệu

So sánh hiệu suất của đồng bộ hóa và không đồng bộ

Bây giờ, chúng ta đã cẩn thận xem qua các quy trình gửi cả yêu cầu đồng bộ và không đồng bộ, chúng ta có thể chạy lại yêu cầu và so sánh hiệu suất của hai tập lệnh.  

Sự khác biệt về thời gian là rất lớn – trong khi mã quét web không đồng bộ có thể thực thi tất cả các tác vụ trong khoảng 3 giây, thì phải mất gần 16 giây cho tác vụ đồng bộ. Điều này chứng tỏ rằng cạo không đồng bộ thực sự có lợi hơn do hiệu quả thời gian rõ rệt của nó

kết thúc

Việc thu thập dữ liệu công khai chất lượng cao thường yêu cầu quét hàng chục hoặc thậm chí hàng trăm trang web. Để giúp bạn hiểu cách thực hiện điều này một cách hiệu quả, chúng tôi đã so sánh hai cách tiếp cận khác nhau để thu thập nhiều URL của trang web bằng Python – đồng bộ và không đồng bộ (không chặn). Bằng cách khám phá sự khác biệt chính của chúng và cung cấp các đoạn mã mẫu, chúng tôi đã chứng minh rằng mã không đồng bộ có lợi hơn khi gửi nhiều yêu cầu.  

Nếu bạn có bất kỳ câu hỏi nào về hướng dẫn cụ thể này hoặc bất kỳ chủ đề nào khác liên quan đến quét web, vui lòng liên hệ với chúng tôi tại hello@oxylabs. io

Các câu hỏi thường gặp

Python có không đồng bộ không?

Python rất linh hoạt và hỗ trợ cả lập trình đồng bộ và không đồng bộ. Sự khác biệt chính giữa hai cách tiếp cận này là mã đồng bộ thực thi một yêu cầu tại một thời điểm trong khi mã không đồng bộ cho phép xử lý các tác vụ không bị chặn, giúp giảm đáng kể thời gian cạo

Làm cách nào để kiểm tra thời gian phản hồi aiohttp?

Một cách tiếp cận đơn giản sẽ là khai báo và khởi tạo bộ đếm thời gian trước khi gửi yêu cầu và sau khi đọc phản hồi của nó rồi trừ cái này cho cái kia. Sự khác biệt này sẽ đại diện cho thời gian đáp ứng.  

Làm cách nào để lặp qua nhiều tài liệu HTML trong BeautifulSoup Python?

Để lặp qua nhiều trang trong BeautifulSoup, bạn cần khai báo một vòng lặp for với một đối tượng BeautifulSoup bên trong nó để nó được khởi tạo cho mỗi trang bạn đang lặp qua

Giới thiệu về tác giả

Yelyzaveta Nechytailo

Nội dung quản lí

Yelyzaveta Nechytailo là Người quản lý nội dung tại Oxylabs. Sau khi làm việc với tư cách là một nhà văn về thời trang, thương mại điện tử và truyền thông, cô quyết định thay đổi con đường sự nghiệp của mình và đắm chìm trong thế giới công nghệ đầy hấp dẫn. Và tin hay không, cô ấy hoàn toàn thích nó. Vào cuối tuần, có thể bạn sẽ thấy Yelyzaveta thưởng thức một tách trà xanh tại một quán cà phê ấm cúng, lướt mạng xã hội hoặc xem phim truyền hình điều tra say sưa.

Tất cả thông tin trên Blog Oxylabs được cung cấp trên cơ sở "nguyên trạng" và chỉ dành cho mục đích thông tin. Chúng tôi không đại diện và từ chối mọi trách nhiệm liên quan đến việc bạn sử dụng bất kỳ thông tin nào có trên Oxylabs Blog hoặc bất kỳ trang web của bên thứ ba nào có thể được liên kết trong đó. Trước khi tham gia vào các hoạt động cạo dưới bất kỳ hình thức nào, bạn nên tham khảo ý kiến ​​​​của cố vấn pháp lý của mình và đọc kỹ các điều khoản dịch vụ của trang web cụ thể hoặc nhận giấy phép cạo