Bỏ để qua phần nội dung

Cách cạo dữ liệu web ẩn

Việc thu thập dữ liệu từ web hiện đại thường có thể giống như một trò chơi trốn tìm. Mặc dù một thập kỷ trước, hầu hết thông tin đều có sẵn trong HTML, nhưng ngày nay các nhà phát triển thích ẩn và làm xáo trộn dữ liệu, hiển thị dữ liệu một cách linh hoạt bằng JavaScript.

Điều này đưa ra một thách thức thú vị cho người dọn dẹp. Mặc dù chúng tôi không còn có thể dựa vào việc phân tích nội dung HTML thô nữa, nhưng rất nhiều dữ liệu vẫn còn đó trên trang – chúng tôi chỉ cần biết nơi để tìm.

Trong hướng dẫn toàn diện này, chúng ta sẽ khám phá nhiều phương pháp, công cụ và kỹ thuật khác nhau có thể được sử dụng để trích xuất dữ liệu web ẩn.

Dữ liệu web ẩn là gì?

Dữ liệu web ẩn đề cập đến bất kỳ dữ liệu nào không hiển thị trực tiếp trong nguồn HTML thô của trang. Điêu nay bao gôm:

  • Dữ liệu được tải động qua JavaScript sau khi tải trang. Ví dụ: hiển thị nội dung của một <div> bằng cách chèn các phần tử HTML được tạo động.

  • Dữ liệu được lưu trữ trong các biến và đối tượng JavaScript được nhúng trong <script> thẻ. Thông thường các đối tượng JSON chứa toàn bộ tập dữ liệu.

  • Nội dung HTML được tạo dựa trên hành động của người dùng thông qua các yêu cầu AJAX. Ví dụ: mở rộng chuỗi nhận xét hoặc phân trang cuộn vô hạn.

  • Dữ liệu và siêu dữ liệu yêu cầu API nội bộ được giao diện người dùng sử dụng để hoạt động. Ví dụ: mã thông báo CSRF, thông tin người dùng, bộ đệm tạm thời.

  • Dữ liệu bị xáo trộn và mã hóa nhằm mục đích ngăn chặn những kẻ phá hoại truy cập vào nó.

Chủ đề chung là dữ liệu này không có sẵn trong HTML gốc được trả về từ máy chủ mà được tạo sau đó bởi JavaScript chạy trên trang.

Các trang web động hiện đại phụ thuộc rất nhiều vào kỹ thuật này để xây dựng trải nghiệm giao diện người dùng nhanh chóng. Tất cả dữ liệu có thể được ẩn đi và hiển thị một cách duyên dáng theo từng phần nhỏ nếu cần.

Thật không may, điều này có nghĩa là người dọn dẹp phải làm việc chăm chỉ hơn một chút để có được dữ liệu đó. Hãy xem xét một số cách chúng ta có thể làm điều đó một cách hiệu quả.

Tìm dữ liệu ẩn trong HTML

Bước đầu tiên là xác nhận xem dữ liệu chúng ta muốn có thực sự bị ẩn ở đâu đó trong trang JavaScript hay không.

Đây là một phương pháp đơn giản để kiểm tra:

  • Tải trang đích trong trình duyệt và tìm mã định danh dữ liệu duy nhất mà chúng tôi muốn loại bỏ. Ví dụ: tên sản phẩm hoặc ID.

  • Tắt JavaScript trong trình duyệt và tải lại trang. Điều này có thể được thực hiện trong các công cụ dành cho nhà phát triển.

  • Kiểm tra xem mã định danh duy nhất có còn tồn tại trong mã nguồn HTML thô hay không.

Nếu dữ liệu biến mất – rất có thể dữ liệu đó sẽ được JavaScript hiển thị động khi tải trang.

Bây giờ chúng ta phải tìm hiểu kỹ nguồn HTML để tìm xem nó tạo ra nội dung đó ở đâu và như thế nào.

Trích xuất dữ liệu từ thẻ

Một trong những nơi phổ biến nhất để dữ liệu ẩn cư trú là bên trong <script> thẻ.

Đây có thể là các đối tượng JSON, biến JavaScript, toàn bộ tập dữ liệu hoặc mã thao tác trang.

Ví dụ:

<html>
<body>

  <div id="product"></div>

  <script>
    // product data as javascript object 
    var data = {
      "product": {
        "name": "Super Product",
        "price": 99.99
      }
    }

    // data rendered on page load
    document.getElementById("product").innerHTML = data.product.name + ": £" + data.product.price;

  </script>

</body>  
</html>

Ở đây, dữ liệu sản phẩm thực tế được lưu trữ trong một biến đối tượng JavaScript có tên là data.

sản phẩm <div> bắt đầu trống và được điền động khi tải trang.

Vì vậy, để trích xuất dữ liệu này, trước tiên chúng ta phải tìm các dữ liệu liên quan <script> thẻ trong HTML thô. Điều này có thể được thực hiện với bất kỳ thư viện phân tích cú pháp HTML nào như BeautifulSoup hoặc Parsel:

# extract scripts from HTML with BeautifulSoup
from bs4 import BeautifulSoup

html = # page HTML 
soup = BeautifulSoup(html, ‘html.parser‘)

scripts = soup.find_all(‘script‘)

Tiếp theo chúng ta phải trích xuất dữ liệu từ nội dung tập lệnh một cách cụ thể.

Phương pháp 1: Tải dưới dạng JSON

Nếu dữ liệu là một đối tượng JSON hợp lệ, chúng ta chỉ cần tải nó trực tiếp bằng Python json mô-đun:

import json

# find script with data variable 
script = soup.find(‘script‘, text=lambda t: ‘data =‘ in t)

# load json directly
data = json.loads(script.string)

print(data[‘product‘])
# {‘name‘: ‘Super Product‘, ‘price‘: 99.99}

Điều này hoạt động tốt nếu thẻ script chỉ định type="application/json".

Phương pháp 2: So khớp Regex

Đối với dữ liệu phức tạp hơn, chúng tôi sẽ phải tự phân tích mã JavaScript thô. Đây là nơi các biểu thức thông thường có ích.

Chúng tôi có thể quét mã và trích xuất các phần khớp với mẫu - như đối tượng dữ liệu của chúng tôi.

import re
import json

script = soup.find(‘script‘, text=lambda t: ‘data =‘ in t)

# match the data object by surrounding syntax 
match = re.search(r‘data = ({.+})‘, script.string)

# load matched json 
data = json.loads(match.group(1))

print(data[‘product‘])  
# {‘name‘: ‘Super Product‘, ‘price‘: 99.99}

Điều quan trọng là tạo một mẫu biểu thức chính quy một cách cẩn thận để xác định duy nhất tập dữ liệu mà chúng ta muốn từ phần còn lại của mã.

Phương pháp 3: Phân tích cú pháp JavaScript

Để quét nâng cao, chúng tôi có thể muốn phân tích mã JavaScript đầy đủ – bao gồm các biến, hàm và đối tượng.

Điều này cho phép trích xuất bất kỳ dữ liệu nào trong khi vẫn duy trì cấu trúc và bối cảnh ban đầu.

Chúng ta có thể sử dụng các thư viện như PyJavascriptJs2Py để diễn giải JavaScript bằng Python.

Ví dụ với PyJavascript:

import javascript

script = soup.find(‘script‘, text=lambda t: ‘data =‘ in t)

# init JavaScript interpreter 
js = javascript.Interpreter()

# run script to define data variable
js.execute(script.string)

# access parsed data object
print(js.context[‘data‘][‘product‘])
# {‘name‘: ‘Super Product‘, ‘price‘: 99.99} 

Điều này cho phép chúng tôi khai thác toàn bộ môi trường JavaScript, ngoài các bộ dữ liệu mà chúng tôi muốn.

Quét dữ liệu API từ JavaScript

API hỗ trợ hầu hết các hành vi động trên các trang web hiện đại. JavaScript đưa ra yêu cầu tải dữ liệu, gửi biểu mẫu hoặc kích hoạt tương tác.

Bằng cách đào sâu vào mã trang, chúng tôi có thể tìm thấy các điểm cuối API này và bắt chước các yêu cầu trích xuất dữ liệu.

Ví dụ: đây là một tập lệnh đơn giản tải dữ liệu sản phẩm từ một /api/products/123 điểm cuối:

async function loadProduct(){

  let response = await fetch(‘/api/products/123‘);

  let product = await response.json();

  // render product data to page
  document.getElementById("product").innerHTML = product.name;

}

loadProduct();

Khi chúng tôi xác định được tập lệnh này trong HTML, chúng tôi có thể:

  • Trích xuất URL API từ fetch() cuộc gọi

  • Phân tích các định dạng yêu cầu và phản hồi AJAX

  • Sao chép yêu cầu API trực tiếp bằng Python với các thư viện như Yêu cầu

Điều này cho phép lấy dữ liệu từ các API mà JavaScript dựa vào mà không cần thực thi bất kỳ mã trình duyệt nào.

Tìm dữ liệu trong các biến JavaScript

Dữ liệu trang cũng thường được lưu trữ trực tiếp trong các biến JavaScript.

Ví dụ:

// javascript data
var products = [
  {name: "Product 1", price: 19.99}, 
  {name: "Product 2", price: 24.99}
];

function renderProducts(){
  // loop through products and render HTML
} 

Ở đây danh sách sản phẩm đầy đủ được lưu trữ trong một biến gọi là products.

Để giải nén điều này, trước tiên chúng ta phải tìm tên biến phù hợp với cấu trúc dữ liệu mục tiêu của chúng ta. Chúng ta có thể sử dụng cách tiếp cận biểu thức chính quy tương tự:

import re
import json

# find products variable
script = soup.find(‘script‘, text=lambda t: ‘var products =‘ in t)

# match products json
match = re.search(r‘var products = ({.+});‘, script.string)  
data = json.loads(match.group(1))

print(data)
# [{name: "Product 1", price: 19.99}, {name: "Product 2", price: 24.99}]

Nếu cấu trúc dữ liệu phức tạp hơn, chúng ta có thể phân tích cú pháp toàn bộ môi trường JavaScript để truy cập bất kỳ biến nào trong phạm vi.

Quét nội dung được tải qua AJAX

Các trang web thường xuyên tải nội dung động qua AJAX sau khi tải trang.

Ví dụ: mở rộng chuỗi nhận xét, phân trang hoặc tab cuộn vô hạn.

Nội dung này không có trong HTML ban đầu nhưng được yêu cầu từ máy chủ nếu cần.

Chúng ta có thể loại bỏ các đoạn mã AJAX này bằng cách:

  • Giám sát các yêu cầu mạng trên trang để xác định URL AJAX.

  • Xây dựng lại các yêu cầu AJAX và gửi chúng trực tiếp từ mã Python.

  • Phân tích cú pháp phản hồi AJAX chứa dữ liệu HTML/JSON.

Ví dụ: hãy xem xét tập lệnh này tải dữ liệu được phân trang khi cuộn:

// initially loaded page data
var results = [ /* initial page of data */]; 

// paginate on scroll
window.addEventListener(‘scroll‘, function() {

  var page = results.length / 20 + 1;

  // request next page
  fetch(‘/data?page=‘ + page)
    .then(res => res.json())
    .then(data => {
      results.push(...data);

      // render new data
    });

});

Ở đây chúng ta có thể thấy nó đang yêu cầu các trang từ /data điểm cuối và nối thêm nội dung vào results biến.

Chúng tôi có thể sao chép các yêu cầu này và trích xuất dữ liệu trực tiếp, tránh phải phân tích cú pháp HTML được hiển thị đầy đủ.

Thực thi JavaScript với trình duyệt không đầu

Để có được nội dung động tối ưu, chúng tôi có thể tạo một trình duyệt không có giao diện người dùng hoàn chỉnh, tải trang và truy cập trực tiếp vào môi trường JavaScript.

Điều này cho phép đánh giá mã, tải nội dung động và truy cập mọi dữ liệu, chức năng hoặc thành phần DOM có sẵn trên trang trực tiếp.

Đây là một ví dụ với Playwright bằng Python:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:

  browser = p.chromium.launch()
  page = browser.new_page()

  page.goto(‘https://targetpage.com‘)

  # evaluate browser context to get data
  data = page.evaluate(‘window.products‘) 

  browser.close()

print(data)

Chìa khóa đang sử dụng page.evaluate() để chạy mã JavaScript tùy chỉnh trong ngữ cảnh của trang được tải.

Điều này cho phép chúng tôi có toàn quyền truy cập để loại bỏ mọi dữ liệu bị ẩn khác.

Nhược điểm là phải khởi chạy một phiên bản trình duyệt đầy đủ – chậm hơn so với các yêu cầu HTTP trực tiếp. Vì vậy, phương pháp này nên được sử dụng một cách tiết kiệm cho các trang phức tạp.

Dữ liệu bị xáo trộn và mã hóa

Các trang web thường cố tình làm xáo trộn JavaScript của họ để ngăn chặn việc thu thập dữ liệu.

Một số ví dụ bao gồm:

  • Giảm thiểu tên biến và tên hàm thành các ký tự vô nghĩa như a, b, fn1()

  • Chia tập dữ liệu thành nhiều biến và tập lệnh

  • Mã hóa/mã hóa dữ liệu để con người không thể đọc được

  • Tự động tập hợp dữ liệu trong thời gian chạy từ các phần bị phân mảnh

  • Các kỹ thuật bảo vệ mã như đóng gói, che giấu, chống gỡ lỗi, thực thi VM

Điều này có thể làm cho việc phân tích cú pháp JavaScript trở nên rất phức tạp. Những thay đổi nhỏ về mã có thể dễ dàng làm hỏng trình dọn dẹp của chúng tôi.

Có một số cách để xử lý các trang bị xáo trộn nhiều:

  • Sử dụng các trình duyệt không có giao diện người dùng như Playwright hoặc Puppeteer để tải mã đã thực thi thay vì phân tích trực tiếp nguồn bị xáo trộn.

  • Theo dõi quá trình thực thi mã để hiểu cách tập hợp dữ liệu – ví dụ: sử dụng các công cụ dành cho nhà phát triển trình duyệt hoặc ủy quyền lưu lượng truy cập trình duyệt.

  • Phân tích cách người dùng thực tương tác với trang để xác định nguồn dữ liệu.

  • Mẫu khớp với các cấu trúc dữ liệu đã biết – như tên sản phẩm, giá, ID – để xác định các phần mã có liên quan ngay cả khi các biến bị xáo trộn.

  • Để mã hóa, hãy thử định vị các khóa mã hóa hoặc thuật toán giải mã kỹ thuật đảo ngược.

Theo thời gian, chúng tôi có thể xây dựng khả năng phục hồi bằng cách phát triển các công cụ dọn dẹp để thích ứng với những thay đổi về mã hóa.

Quét các API ẩn bằng proxy

API web ẩn thường sử dụng các kỹ thuật chống quét nâng cao như giới hạn tốc độ IP, phát hiện hình ảnh xác thực và bot để ngăn truy cập.

Đây là nơi proxy rất hữu ích cho việc cạo. Bằng cách định tuyến các yêu cầu thông qua IP dân cư, chúng tôi có thể bỏ qua nhiều biện pháp bảo vệ và truy cập API trên quy mô lớn.

Một số mẹo để quét bằng proxy:

  • Sử dụng xoay vòng proxy thông thường để tránh bị chặn trên các IP cụ thể

  • Cho phép xoay vòng proxy dựa trên khu vực hoặc ISP để có tính đa dạng rộng rãi

  • Sử dụng proxy kết nối ngược cung cấp hàng nghìn IP duy nhất để duyệt qua

  • Giới hạn tỷ lệ yêu cầu trên mỗi proxy để bắt chước hành vi của người dùng thực

  • Sử dụng ủy quyền proxy để mạo danh thiết bị thực, không chỉ IP ẩn danh

  • Giám sát và xử lý các khối phổ biến như captcha, chặn trang, 429s

Với thiết lập proxy phù hợp, chúng tôi thực tế có thể truy cập bất kỳ trang web mục tiêu hoặc API ẩn nào.

Dịch vụ thu thập dữ liệu ẩn

Ngoài ra còn có các dịch vụ thu thập dữ liệu được quản lý được xây dựng nhằm mục đích trích xuất dữ liệu được hiển thị bằng JavaScript.

Chúng cung cấp khả năng tự động hóa trình duyệt, quản lý proxy và thực thi JavaScript.

Một số ví dụ bao gồm:

CạoBee – API trình duyệt và proxy có thể đánh giá JS trong các trang.

ScraperAPI – API trình duyệt không đầu với tính năng xoay proxy tự động.

Apify – Thời gian chạy tác nhân để tự động hóa trình duyệt trên quy mô lớn.

ScrapOps – Trình xây dựng tự động hóa trình duyệt trực quan với tính năng trích xuất JS.

phế liệu – API quét không bị chặn với hàng triệu proxy dân cư kết nối ngược.

Các dịch vụ này xử lý tất cả sự phức tạp của việc hiển thị trang động và giúp việc thu thập dữ liệu ẩn trở nên dễ dàng.

Chìa khóa chính

Dưới đây là những điểm chính để cạo dữ liệu trang web ẩn:

  • Kiểm tra các trang không có JavaScript để xác nhận dữ liệu được tải động

  • Trích xuất và phân tích các tập lệnh, biến và đối tượng JSON từ HTML

  • Phân tích và sao chép các yêu cầu AJAX để truy cập các API ẩn

  • Sử dụng proxy và trình duyệt không có đầu khi cần cho các trang JS nặng

  • So khớp mẫu và mã được mã hóa bằng kỹ thuật đảo ngược

  • Điều chỉnh công cụ dọn dẹp để xử lý các biện pháp bảo vệ chống bot

Với các kỹ thuật phù hợp, trên thực tế, mọi dữ liệu trang web công cộng đều có thể được trích xuất. Chúng ta chỉ cần biết nơi để tìm!

tags:

Tham gia vào cuộc đối thoại

Chúng tôi sẽ không công khai email của bạn. Các ô đánh dấu * là bắt buộc *