Trong hành trình này, chúng ta chuyển sự chú ý sang việc sử dụng trình duyệt headless để lấy dữ liệu từ các trang web động. Một trình duyệt headless là một trình duyệt không có giao diện người dùng đồ họa có thể mô phỏng các tương tác của người dùng như nhấp chuột, cuộn trang và điền vào biểu mẫu. Chức năng này cho phép chúng ta lấy dữ liệu từ các trang web yêu cầu tương tác của người dùng. Có những công cụ nào có sẵn cho mục đích này, và làm thế nào chúng ta có thể sử dụng chúng một cách hiệu quả? Trên đường đi, chúng ta sẽ gặp phải nhiều thách thức, cũng như khám phá ra những mẹo và thủ thuật quý báu để vượt qua chúng.
Một trong những câu hỏi phổ biến nhất về việc lấy dữ liệu từ trang web là: "Tại sao công cụ lấy dữ liệu của tôi không thể nhìn thấy dữ liệu mà tôi thấy trong trình duyệt web của mình?
Bên trái là những gì chúng ta thấy trong trình duyệt, trong khi bên phải là những gì công cụ lấy dữ liệu của chúng ta trả về. Vậy, dữ liệu còn lại đi đâu?
Trang web động sử dụng các công nghệ web được điều khiển bởi JavaScript phức tạp để chuyển việc xử lý sang phía máy khách. Nói cách khác, chúng cung cấp dữ liệu và logic cho người dùng nhưng yêu cầu họ được tạo ra để hiển thị trang web hoàn chỉnh. Đây là lý do tại sao công cụ lấy dữ liệu của chúng ta chỉ thấy một trang trống.
Dưới đây là một ví dụ đơn giản:
<!DOCTYPE html>
<html>
<head>
<title>Ví dụ trang web động</title>
</head>
<body>
<div id="content">Đang tải...</div>
<script>
var data = {
name: "John Doe",
age: 30,
city: "New York",
};
document.addEventListener("DOMContentLoaded", function () {
var content = document.getElementById("content");
content.innerHTML = "<h1>" + data.name + "</h1>";
content.innerHTML += "<p>Tuổi: " + data.age + "</p>";
content.innerHTML += "<p>Thành phố: " + data.city + "</p>";
});
</script>
</body>
</html>
Khi chúng ta mở trang và vô hiệu hóa JavaScript, chúng ta thấy "Đang tải...". Nhưng khi JavaScript được kích hoạt, chúng ta thấy "John Doe", "Tuổi: 30", và "Thành phố: New York". Điều này là do JavaScript thực thi sau khi trang được tải, vì vậy công cụ lấy dữ liệu của chúng ta chỉ thấy "Đang tải...". Do đó, công cụ lấy dữ liệu không thấy được trang web được hiển thị hoàn chỉnh vì nó thiếu khả năng tải trang bằng JavaScript như một trình duyệt web làm.
Chúng ta có thể lấy dữ liệu từ các trang web động bằng cách tích hợp một trình duyệt thực và điều khiển nó. Có một số công cụ có sẵn cho mục đích này, chẳng hạn như Puppeteer, Selenium và Playwright. Các công cụ này cung cấp các API để điều khiển trình duyệt, cho phép chúng ta mô phỏng hành vi của người dùng.
Các trình duyệt hiện đại như Chrome và Firefox đi kèm với các giao thức tự động hóa tích hợp cho phép các chương trình khác điều khiển chúng.
Hiện nay, có hai giao thức tự động hóa cho các trình duyệt:
Để minh họa thách thức này, hã
y lấy một ví dụ về lấy dữ liệu từ một trang web thực tế. Chúng ta sẽ lấy dữ liệu từ một trang web trực tuyến, chẳng hạn như Trải nghiệm Airbnb. Chúng ta sẽ tóm tắt ngắn gọn nhiệm vụ minh họa và tìm hiểu cách hiển thị trang trải nghiệm được hoàn toàn, ví dụ như https://www.airbnb.com/experiences/1653933, và lấy nội dung được hiển thị hoàn chỉnh để tiếp tục xử lý.
Airbnb là một trong những trang web được tạo ra động bằng React trên toàn thế giới. Điều này có nghĩa là trình thu thập của chúng ta sẽ không thể nhìn thấy trang web được hiển thị hoàn chỉnh. Mà không cần một trình duyệt headless, chúng ta phải phân tích ngược mã của trang web để lấy dữ liệu HTML đầy đủ của nó. Tuy nhiên, với sự xuất hiện của các trình duyệt headless, chúng ta có thể mô phỏng hành vi của người dùng để lấy dữ liệu từ các trang web động, làm cho quy trình của chúng ta đơn giản hơn nhiều.
Bây giờ hãy thử sử dụng bốn công cụ tự động hóa trình duyệt khác nhau: Puppeteer, Selenium, Playwright và Nstbrowser.
Puppeteer là một thư viện Node.js được phát triển bởi nhóm Google Chrome cung cấp một API cấp cao để điều khiển Chrome hoặc Chromium thông qua giao thức DevTools. Nó là một trình duyệt headless, có nghĩa là nó không có giao diện người dùng đồ họa nhưng có thể mô phỏng hành vi của người dùng.
So với Selenium, Puppeteer hỗ trợ ít ngôn ngữ và trình duyệt hơn, nhưng nó triển khai hoàn toàn giao thức CDP và được hưởng lợi từ đội ngũ mạnh mẽ của Google đứng sau nó.
Puppeteer cũng mô tả chính mình là một khách hàng tự động hóa trình duyệt thông thường hơn là phù hợp với thị trường chuyên sâu cho kiểm thử web - điều tốt lành vì các vấn đề về lấy dữ liệu web nhận được sự hỗ trợ chính thức.
Hãy xem xét cách ví dụ của chúng tôi về airbnb.com trong Puppeteer và JavaScript:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://airbnb.com/experiences/1653933');
await page.waitForSelector("h1");
await page.content();
await browser.close();
})();
Selenium là một trong những công cụ tự động hóa lớn đầu tiên được tạo ra để tự động hóa kiểm thử trang web. Nó hỗ trợ hai giao thức điều khiển trình duyệt: WebDriver và CDP (chỉ từ Selenium V4+).
Việc là công cụ cũ nhất trong danh sách ngày hôm nay có nghĩa là Selenium có một cộng đồng khá lớn và nhiều tính năng. Nó được hỗ trợ trong hầu hết mọi ngôn ngữ lập trình và có thể chạy trên gần như mọi trình duyệt web:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.expected_conditions import visibility_of_element_located
chrome_options = Options()
chrome_options.add_argument("--headless")
browser = webdriver.Chrome(chrome_options) # start a web browser
browser.get("https://www.airbnb.com/experiences/1653933") # navigate to URL
# wait for page to load
# by waiting for <h1> element to appear on the page
title = (
WebDriverWait(driver=browser, timeout=10)
.until(visibility_of_element_located((By.CSS_SELECTOR, "h1")))
.text
)
# retrieve fully rendered HTML content
content = browser.page_source
browser.close()
# we then could parse it with beautifulsoup
from bs4 import BeautifulSoup
soup = BeautifulSoup(content, "html.parser")
print(soup.find("h1").text) # you will see the "Drink and Draw"
Playwright là một thư viện tự động hóa trình duyệt đồng bộ và không đồng bộ được Microsoft cung cấp bằng nhiều ngôn ngữ.
Mục tiêu chính của Playwright là kiểm tra ứng dụng web hiện đại một cách đáng tin cậy, mặc dù nó vẫn triển khai tất cả các tính năng tự động hóa trình duyệt thông thường (như Puppeteer và Selenium) và có một cộng đ
ồng lớn về lấy dữ liệu web đang phát triển.
from playwright.sync_api import sync_playwright
with sync_playwright() as pw:
browser = pw.chromium.launch(headless=True)
context = browser.new_context(viewport={"width": 1920, "height": 1080})
page = context.new_page()
page.goto("https://airbnb.com/experiences/1653933") # go to url
page.wait_for_selector('h1')
title = page.inner_text('h1')
print(title) # you will see "Drink and Draw"
Trong đoạn mã trên, chúng ta đầu tiên khởi chạy trình duyệt Chromium, sau đó điều hướng đến một trang trải nghiệm Airbnb. Tiếp theo, chúng ta đợi cho trang tải và sau đó lấy nội dung HTML của trang.
Nstbrowser là một trình duyệt chống phát hiện mạnh mẽ được thiết kế cho các chuyên gia quản lý nhiều tài khoản, cho phép quản lý nhiều tài khoản một cách an toàn và hiệu quả. Nstbrowser tích hợp một loạt các công nghệ tiên tiến, bao gồm trình duyệt chống phát hiện, đa tài khoản, trình duyệt ảo, dấu vân tay trình duyệt và quản lý tài khoản, cung cấp cho người dùng một trải nghiệm duyệt web mới. Dưới đây, chúng ta sẽ sử dụng trình duyệt của nó để lấy nội dung trang web ở chế độ Headless.

import json
from urllib.parse import quote
from urllib.parse import urlencode
import requests
from requests.exceptions import HTTPError
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui
import WebDriverWait
from selenium.webdriver.support.expected_conditions import visibility_of_element_located
from selenium.webdriver.chrome.service import Service as ChromeService
# get_debugger_port: Get the debugger port
def get_debugger_port(url: str):
try:
resp = requests.get(url).json()
if resp['data'] is None:
raise Exception(resp['msg'])
port = resp['data']['port']
return port
except HTTPError:
raise Exception(HTTPError.response)
def exec_selenium(debugger_address: str):
options = webdriver.ChromeOptions()
options.add_experimental_option("debuggerAddress", debugger_address)
# Replace with the corresponding version of WebDriver path.
chrome_driver_path = r'/Users/xxx/Desktop/chrome_web_driver/chromedriver_mac_arm64/chromedriver'
service = ChromeService(chrome_driver_path)
driver = webdriver.Chrome(service=service, options=options)
driver.get("https://www.airbnb.com/experiences/1653933")
# wait for page to load
# by waiting for <h1> element to appear on the page
title = (
WebDriverWait(driver=driver, timeout=10)
.until(visibility_of_element_located((By.CSS_SELECTOR, "h1")))
.text
)
content = driver.page_source
# we then could parse it with beautifulsoup
from bs4 import BeautifulSoup
soup = BeautifulSoup(content, "html.parser")
print(soup.find("h1").text) # you will see the "Drink and Draw"
driver.close()
driver.quit()
def create_and_connect_to_browser():
host = '127.0.0.1'
api_key = 'Your API Key Here' # got it from https://app.nstbrowser.io/
config = {
'headless': True, # open browser in headless mode
'remoteDebuggingPort': 9222,
'userAgent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
# userAgent supportted since v0.15.0
'fingerprint': { # required
'name': 'custom browser',
'platform': 'mac', # support: windows, mac, linux
'kernel': 'chromium', # only support: chromium
'kernelMilestone': '113', # support: 113, 115, 118, 120
'hardwareConcurrency': 4, # support: 2, 4, 8, 10, 12, 14, 16
'deviceMemory': 4, # support: 2, 4, 8
'proxy': '', # input format: schema://user:password@host:port eg: http://user:password@localhost:8080
}
}
query = urlencode({
'x-api-key': api_key, # required
'config': quote(json.dumps(config))
})
url = f'http://{host}:8848/devtool/launch?{query}'
print('devtool url: ' + url)
port = get_debugger_port(url)
debugger_address = f'{host}:{port}'
print("debugger_address: " + debugger_address)
exec_selenium(debugger_address)
create_and_connect_to_browser()
Sau khi chạy thành công, chúng ta sẽ thấy kết quả sau trong cửa sổ console:

Hy vọng rằng, qua hướng dẫn này, bạn đã học được hai điểm chính sau đây: