HomeLập trìnhPythonHướng dẫn cạo...

Hướng dẫn cạo web Python – Cách cạo dữ liệu từ bất kỳ trang web nào bằng Python


Quét web là quá trình trích xuất dữ liệu cụ thể từ internet một cách tự động. Nó có nhiều trường hợp sử dụng, chẳng hạn như lấy dữ liệu cho dự án máy học, tạo công cụ so sánh giá hoặc bất kỳ ý tưởng sáng tạo nào khác yêu cầu lượng dữ liệu khổng lồ.

Mặc dù về mặt lý thuyết, bạn có thể thực hiện trích xuất dữ liệu theo cách thủ công, nhưng nội dung rộng lớn của Internet khiến phương pháp này không thực tế trong nhiều trường hợp. Vì vậy, biết cách xây dựng một công cụ quét web có thể hữu ích.

Mục đích của bài viết này là hướng dẫn bạn cách tạo một công cụ quét web bằng Python. Bạn sẽ tìm hiểu cách kiểm tra trang web để chuẩn bị quét, trích xuất dữ liệu cụ thể bằng BeautifulSoup, chờ kết xuất JavaScript bằng Selenium và lưu mọi thứ trong tệp JSON hoặc CSV mới.

Nhưng trước tiên, tôi nên cảnh báo bạn về tính hợp pháp của việc quét web. Mặc dù hành động thu thập dữ liệu là hợp pháp nhưng dữ liệu bạn có thể trích xuất có thể là bất hợp pháp khi sử dụng. Đảm bảo rằng bạn không gây rối với bất kỳ:

  • Nội dung có bản quyền – vì đó là tài sản trí tuệ của ai đó nên nó được luật pháp bảo vệ và bạn không thể sử dụng lại nội dung đó.
  • Dữ liệu cá nhân – nếu thông tin bạn thu thập có thể được sử dụng để nhận dạng một người thì đó được coi là dữ liệu cá nhân và đối với công dân EU, thông tin đó được bảo vệ theo GDPR. Trừ khi bạn có lý do hợp pháp để lưu trữ dữ liệu đó, tốt hơn hết là bỏ qua nó hoàn toàn.

Nói chung, bạn phải luôn đọc các điều khoản và điều kiện của trang web trước khi cạo để đảm bảo rằng bạn không đi ngược lại chính sách của họ. Nếu bạn không chắc chắn về cách tiến hành, hãy liên hệ với chủ sở hữu trang web và yêu cầu sự đồng ý.

Bạn sẽ cần gì cho cái cạp của mình?

Để bắt đầu xây dựng trình quét web của riêng bạn, trước tiên bạn cần cài đặt Python trên máy của mình. Ubuntu 20.04 và các phiên bản Linux khác được cài đặt sẵn Python 3.

Để kiểm tra xem bạn đã cài đặt Python trên thiết bị của mình chưa, hãy chạy lệnh sau:

python3 -v

Nếu bạn đã cài đặt Python, bạn sẽ nhận được kết quả như sau:

Python 3.8.2

Ngoài ra, đối với trình quét web của chúng tôi, chúng tôi sẽ sử dụng các gói Python BeautifulSoup (để chọn dữ liệu cụ thể) và Selenium (để hiển thị nội dung được tải động). Để cài đặt chúng, chỉ cần chạy các lệnh sau:

pip3 install beautifulsoup4

pip3 install selenium

Bước cuối cùng là đảm bảo rằng bạn đã cài đặt Google Chrome và Trình điều khiển Chrome trên máy của mình. Những điều này sẽ cần thiết nếu chúng ta muốn sử dụng Selenium để cạo nội dung được tải động.

Cách kiểm tra trang

Bây giờ bạn đã cài đặt mọi thứ, đã đến lúc bắt đầu dự án cạo của chúng tôi một cách nghiêm túc.

Bạn nên chọn trang web bạn muốn cạo dựa trên nhu cầu của bạn. Hãy nhớ rằng mỗi trang web có cấu trúc nội dung khác nhau, vì vậy bạn sẽ cần điều chỉnh những gì bạn học được ở đây khi bạn bắt đầu tự tìm hiểu. Mỗi trang web sẽ yêu cầu những thay đổi nhỏ đối với mã.

Đối với bài viết này, tôi quyết định thu thập thông tin về mười phim đầu tiên trong danh sách 250 phim hàng đầu từ IMDb: https://www.imdb.com/chart/top/.

Đầu tiên, chúng tôi sẽ lấy các tiêu đề, sau đó chúng tôi sẽ đi sâu hơn bằng cách trích xuất thông tin từ các trang của mỗi bộ phim. Một số dữ liệu sẽ yêu cầu kết xuất JavaScript.

Đọc thêm  Python .split() – Tách chuỗi trong Python

Để bắt đầu hiểu cấu trúc của nội dung, bạn nên nhấp chuột phải vào tiêu đề đầu tiên trong danh sách, sau đó chọn “Kiểm tra phần tử”.

e6DE3zczzQa-VSBIynK-fR4oyAjVbpx2PztpEDKbi3K0NII9_lFkFhGQmiOjc_-Y_Kg26cM3pecnSKNiPlLZGpntqVKUrcX9E4gDWaTsolWoCFzQ6EEhj3GruBvrlEizrUffvdjU

Bằng cách nhấn CTRL+F và tìm kiếm trong cấu trúc mã HTML, bạn sẽ thấy rằng chỉ có một đánh dấu trên trang. Điều này rất hữu ích vì nó cung cấp cho chúng tôi thông tin về cách chúng tôi có thể truy cập dữ liệu.

Bộ chọn HTML sẽ cung cấp cho chúng tôi tất cả các tiêu đề từ trang là table tbody tr td.titleColumn a. Đó là bởi vì tất cả các tiêu đề nằm trong một neo bên trong một ô của bảng với lớp “titleColumn”.

Sử dụng bộ chọn CSS này và nhận nội dung của mỗi neo sẽ cung cấp cho chúng tôi các tiêu đề mà chúng tôi cần. Bạn có thể mô phỏng điều đó trong bảng điều khiển trình duyệt từ cửa sổ mới mà bạn vừa mở và bằng cách sử dụng dòng JavaScript:

document.querySelectorAll("table tbody tr td.titleColumn a")[0].innerText

Bạn sẽ thấy một cái gì đó như thế này:

T1pgLUXJHX_s3gubDKvBjwkWeK1neZxiysoneD2Q1NU3Sj_pD8defdKorTlcsiiqShlmPDEeCu3Goo5T9CgzPKCml9dq_kCCu7KUyTx7uSrU8VN9QzJZhO6AwBM-kfQ8r0uNxbn9

Bây giờ chúng ta có bộ chọn này, chúng ta có thể bắt đầu viết mã Python và trích xuất thông tin chúng ta cần.

Các tiêu đề phim từ danh sách của chúng tôi là nội dung tĩnh. Đó là bởi vì nếu bạn nhìn vào nguồn trang (CTRL+U trên trang hoặc nhấp chuột phải rồi chọn Xem nguồn trang), bạn sẽ thấy rằng tiêu đề đã có sẵn ở đó.

Nội dung tĩnh thường dễ cạo hơn vì nó không yêu cầu kết xuất JavaScript. Để trích xuất mười tiêu đề đầu tiên trong danh sách, chúng tôi sẽ sử dụng BeautifulSoup để lấy nội dung và sau đó in nội dung đó ở đầu ra của trình cạp của chúng tôi.

import requests
from bs4 import BeautifulSoup
 
page = requests.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
soup = BeautifulSoup(page.content, 'html.parser') # Parsing content using beautifulsoup
 
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    print(anchor.text) # Display the innerText of each anchor

Đoạn mã trên sử dụng bộ chọn mà chúng ta đã thấy trong bước đầu tiên để trích xuất các neo tiêu đề phim từ trang. Sau đó nó lặp qua mười cái đầu tiên và hiển thị nội dung bên trong của mỗi cái.

Đầu ra sẽ trông như thế này:

RrmEldjCrbz7V1-o4r6UsKNuWkj_yD2cWwfyuMMbdnRn7lk9cI0yhMi85PK4NrvX7L2KY0pY8047f9CmAeXo1W51HvFENMPxxh36ACqu3kNKuoFNNfhB_WSCMntIB-UB0usEU2n5

Khi công nghệ tiên tiến, các trang web bắt đầu tải nội dung của họ một cách linh hoạt. Điều này cải thiện hiệu suất của trang, trải nghiệm của người dùng và thậm chí loại bỏ rào cản bổ sung đối với người dọn dẹp.

Tuy nhiên, điều này làm phức tạp mọi thứ vì HTML được truy xuất từ ​​một yêu cầu đơn giản sẽ không chứa nội dung động. May mắn thay, với Selenium, chúng ta có thể mô phỏng một yêu cầu trong trình duyệt và đợi nội dung động được hiển thị.

Cách sử dụng Selenium cho các yêu cầu

Bạn sẽ cần biết vị trí của chromedriver của mình. Đoạn mã sau giống hệt với đoạn mã được trình bày trong bước thứ hai, nhưng lần này chúng tôi đang sử dụng Selenium để thực hiện yêu cầu. Chúng tôi vẫn sẽ phân tích cú pháp nội dung của trang bằng BeautifulSoup như chúng tôi đã làm trước đây.

from bs4 import BeautifulSoup
from selenium import webdriver
 
option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux. 
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')
# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
 
driver.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser') # Parsing content using beautifulsoup. Notice driver.page_source instead of page.content
 
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    print(anchor.text) # Display the innerText of each anchor

Đừng quên thay thế “YOUR-PATH-TO-CHROMEDRIVER” bằng vị trí mà bạn đã giải nén chromedriver. Ngoài ra, bạn nên chú ý rằng thay vì page.contentkhi chúng tôi đang tạo đối tượng BeautifulSoup, chúng tôi hiện đang sử dụng driver.page_sourcecung cấp nội dung HTML của trang.

Sử dụng mã ở trên, bây giờ chúng ta có thể truy cập từng trang phim bằng cách gọi phương thức nhấp chuột trên mỗi neo.

first_link = driver.find_elements_by_css_selector('table tbody tr td.titleColumn a')[0]
first_link.click()

Điều này sẽ mô phỏng một cú nhấp chuột vào liên kết của bộ phim đầu tiên. Tuy nhiên, trong trường hợp này, tôi khuyên bạn nên tiếp tục sử dụng driver.get instead. Điều này là do bạn sẽ không còn có thể sử dụng click() phương pháp sau khi bạn chuyển sang một trang khác vì trang mới không có liên kết đến chín bộ phim khác.

Đọc thêm  Hàm range() trong Python – Được giải thích bằng các ví dụ về mã

Do đó, sau khi nhấp vào tiêu đề đầu tiên trong danh sách, bạn cần quay lại trang đầu tiên, sau đó nhấp vào tiêu đề thứ hai, v.v. Đây là một sự lãng phí hiệu suất và thời gian. Thay vào đó, chúng tôi sẽ chỉ sử dụng các liên kết được trích xuất và truy cập từng liên kết một.

Đối với “The Shawshank Redemption”, trang phim sẽ là https://www.imdb.com/title/tt0111161/. Chúng tôi sẽ trích xuất năm và thời lượng của bộ phim từ trang, nhưng lần này chúng tôi sẽ sử dụng các chức năng của Selenium thay vì BeautifulSoup làm ví dụ. Trong thực tế, bạn có thể sử dụng một trong hai, vì vậy hãy chọn mục yêu thích của bạn.

Để truy xuất năm và thời lượng của bộ phim, bạn nên lặp lại bước đầu tiên mà chúng tôi đã thực hiện ở đây trên trang của bộ phim.

Bạn sẽ nhận thấy rằng bạn có thể tìm thấy tất cả thông tin trong phần tử đầu tiên với lớp ipc-inline-list (bộ chọn “.ipc-inline-list”) và tất cả các thành phần của danh sách đều chứa thuộc tính role với giá trị presentation (các [role=’presentation’] bộ chọn).

from bs4 import BeautifulSoup
from selenium import webdriver
 
option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux. 
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')
# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
 
page = driver.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser') # Parsing content using beautifulsoup
 
totalScrapedInfo = [] # In this list we will save all the information we scrape
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    driver.get('https://www.imdb.com/' + anchor['href']) # Access the movie’s page
    infolist = driver.find_elements_by_css_selector('.ipc-inline-list')[0] # Find the first element with class ‘ipc-inline-list’
    informations = infolist.find_elements_by_css_selector("[role="presentation"]") # Find all elements with role=’presentation’ from the first element with class ‘ipc-inline-list’
    scrapedInfo = {
        "title": anchor.text,
        "year": informations[0].text,
        "duration": informations[2].text,
    } # Save all the scraped information in a dictionary
    totalScrapedInfo.append(scrapedInfo) # Append the dictionary to the totalScrapedInformation list
    
print(totalScrapedInfo) # Display the list with all the information we scraped

Bước quan trọng tiếp theo trong việc quét web là trích xuất nội dung được tải động. Bạn có thể tìm thấy nội dung như vậy trên mỗi trang của phim (chẳng hạn như https://www.imdb.com/title/tt0111161/) trong phần Danh sách Biên tập.

Nếu bạn xem bằng cách sử dụng kiểm tra trên trang, bạn sẽ thấy rằng bạn có thể tìm thấy phần đó dưới dạng phần tử có thuộc tính data-testid thiết lập như firstListCardGroup-editorial. Nhưng nếu bạn tìm trong nguồn trang, bạn sẽ không tìm thấy giá trị thuộc tính này ở bất kỳ đâu. Đó là vì phần Danh sách biên tập được IMDB tải động.

Trong ví dụ sau, chúng tôi sẽ loại bỏ danh sách biên tập của từng bộ phim và thêm nó vào kết quả hiện tại của chúng tôi về tổng thông tin đã loại bỏ.

Để làm điều đó, chúng tôi sẽ nhập thêm một vài gói để có thể chờ tải nội dung động của chúng tôi.

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
 
option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux. 
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')
# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
 
page = driver.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser') # Parsing content using beautifulsoup
 
totalScrapedInfo = [] # In this list we will save all the information we scrape
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    driver.get('https://www.imdb.com/' + anchor['href']) # Access the movie’s page 
    infolist = driver.find_elements_by_css_selector('.ipc-inline-list')[0] # Find the first element with class ‘ipc-inline-list’
    informations = infolist.find_elements_by_css_selector("[role="presentation"]") # Find all elements with role=’presentation’ from the first element with class ‘ipc-inline-list’
    scrapedInfo = {
        "title": anchor.text,
        "year": informations[0].text,
        "duration": informations[2].text,
    } # Save all the scraped information in a dictionary
    WebDriverWait(driver, 5).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='firstListCardGroup-editorial']")))  # We are waiting for 5 seconds for our element with the attribute data-testid set as `firstListCardGroup-editorial`
    listElements = driver.find_elements_by_css_selector("[data-testid='firstListCardGroup-editorial'] .listName") # Extracting the editorial lists elements
    listNames = [] # Creating an empty list and then appending only the elements texts
    for el in listElements:
        listNames.append(el.text)
    scrapedInfo['editorial-list'] = listNames # Adding the editorial list names to our scrapedInfo dictionary
    totalScrapedInfo.append(scrapedInfo) # Append the dictionary to the totalScrapedInformation list
    
print(totalScrapedInfo) # Display the list with all the information we scraped

Đối với ví dụ trước, bạn sẽ nhận được đầu ra sau:

Đọc thêm  Cách bắt đầu với Firebase bằng Python
geHhbKeeP2ATtz-OnIx9MATB3UvXcrobnO4eUNOLrzQll9ebPlq_2PqKaT_oT6e-3h7NmRkRh_9mrDuSvuW3Wbs3sRi1iuM3paCa8HBpTqWrZuSQc8sIu5y4EVZ_5j-60TmPs71Z

Cách lưu nội dung đã cạo

Bây giờ chúng tôi có tất cả dữ liệu mình muốn, chúng tôi có thể lưu nó dưới dạng tệp .json hoặc .csv để dễ đọc hơn.

Để làm điều đó, chúng tôi sẽ chỉ sử dụng các gói JSON và CVS từ Python và ghi nội dung của chúng tôi vào các tệp mới:

import csv
import json
 
...
        
file = open('movies.json', mode="w", encoding='utf-8')
file.write(json.dumps(totalScrapedInfo))
 
writer = csv.writer(open("movies.csv", 'w'))
for movie in totalScrapedInfo:
    writer.writerow(movie.values())

Mẹo và thủ thuật cạo

Mặc dù hướng dẫn của chúng tôi cho đến nay đã đủ nâng cao để xử lý các tình huống kết xuất JavaScript, nhưng vẫn còn nhiều điều cần khám phá trong Selenium.

Trong phần này, tôi sẽ chia sẻ một số mẹo và thủ thuật có thể hữu ích.

1. Thời gian yêu cầu của bạn

Nếu bạn spam một máy chủ với hàng trăm yêu cầu trong một thời gian ngắn, rất có thể đến một lúc nào đó, mã captcha sẽ xuất hiện hoặc thậm chí IP của bạn có thể bị chặn. Thật không may, không có cách giải quyết nào trong Python để tránh điều đó.

Do đó, bạn nên đặt một số khoảng thời gian chờ giữa mỗi yêu cầu để lưu lượng truy cập trông tự nhiên hơn.

import time
import requests
 
page = requests.get('https://www.imdb.com/chart/top/') # Getting page HTML through request
time.sleep(30) # Wait 30 seconds
page = requests.get('https://www.imdb.com/') # Getting page HTML through request

2. Xử lý lỗi

Vì các trang web là động và chúng có thể thay đổi cấu trúc bất kỳ lúc nào, nên việc xử lý lỗi có thể hữu ích nếu bạn thường xuyên sử dụng cùng một công cụ quét web.

try:
    WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CSS_SELECTOR, "your selector")))
    break
except TimeoutException:
    # If the loading took too long, print message and try again
    print("Loading took too much time!")

Cú pháp thử và lỗi có thể hữu ích khi bạn đang đợi một phần tử, giải nén phần tử đó hoặc thậm chí khi bạn chỉ thực hiện yêu cầu.

3. Chụp ảnh màn hình

Nếu bạn cần lấy ảnh chụp màn hình của trang web mà bạn đang quét bất cứ lúc nào, bạn có thể sử dụng:

driver.save_screenshot(‘screenshot-file-name.png’)

Điều này có thể giúp gỡ lỗi khi bạn đang làm việc với nội dung được tải động.

4. Đọc tài liệu

Cuối cùng nhưng không kém phần quan trọng, đừng quên đọc tài liệu từ Selenium. Thư viện này chứa thông tin về cách thực hiện hầu hết các tác vụ bạn có thể thực hiện trong trình duyệt.

Sử dụng Selenium, bạn có thể điền vào biểu mẫu, nhấn nút, trả lời tin nhắn bật lên và làm nhiều điều thú vị khác.

Nếu bạn đang đối mặt với một vấn đề mới, tài liệu của họ có thể là người bạn tốt nhất của bạn.

Suy nghĩ cuối cùng

Mục đích của bài viết này là cung cấp cho bạn phần giới thiệu nâng cao về quét web bằng Python với Selenium và BeautifulSoup. Mặc dù vẫn còn nhiều tính năng từ cả hai công nghệ để khám phá, nhưng giờ đây bạn đã có cơ sở vững chắc về cách bắt đầu tìm hiểu.

Đôi khi việc quét web có thể rất khó khăn vì các trang web bắt đầu ngày càng gây ra nhiều trở ngại hơn cho nhà phát triển. Một số trở ngại này có thể là mã Captcha, khối IP hoặc nội dung động. Vượt qua chúng chỉ bằng Python và Selenium có thể khó hoặc thậm chí là không thể.

Vì vậy, tôi cũng sẽ cung cấp cho bạn một giải pháp thay thế. Hãy thử sử dụng API quét web để giải quyết tất cả những thách thức đó cho bạn. Nó cũng sử dụng proxy luân phiên để bạn không phải lo lắng về việc thêm thời gian chờ giữa các yêu cầu. Chỉ cần nhớ luôn kiểm tra xem dữ liệu bạn muốn có thể được trích xuất và sử dụng hợp pháp hay không.



Zik.vn – Biên dịch & Biên soạn Lại

spot_img

Create a website from scratch

Just drag and drop elements in a page to get started with Newspaper Theme.

Buy Now ⟶

Bài viết liên quang

DMCA.com Protection Status