Cơ hội trúng 1 TB lưu lượng truy cập dân cư, iPhone 18 Pro Max cùng + 33 giải thưởng khácNạp $50 = 1 vé tham giaTham gia
Doanh nghiệp

Zillow scraper: cách thu thập dữ liệu bất động sản bằng Python

Zillow scraper: cách thu thập dữ liệu bất động sản bằng Python

Tóm tắt bài viết này bằng AI:

Việc so sánh các tin đăng bất động sản trên khắp thành phố bằng tay rất tốn thời gian khi giá nhà thay đổi liên tục và bảng tính ngày hôm qua đã nhanh chóng lỗi thời. Một công cụ thu thập dữ liệu Zillow (Zillow scraper) sẽ tự động lấy thông tin bất động sản từ các trang tin đăng và sắp xếp thành các bản ghi chuẩn hóa, giúp bạn dễ dàng so sánh, cập nhật và xuất dữ liệu mà không cần tạo lại bảng tính thủ công.

Tóm tắt nhanh (TL;DR)

  • Kiểm tra quyền truy cập trước: điều khoản sử dụng được công bố của Zillow nghiêm cấm các truy vấn tự động, bao gồm cả hoạt động cào dữ liệu (scraping). Hãy chọn một chương trình cấp dữ liệu chính thức hoặc một nguồn khác mà bạn được phép thu thập và tái sử dụng trước khi lên kế hoạch trích xuất trực tiếp.

  • Thử nghiệm trên một mẫu nhỏ. Đối chiếu giá cả, địa chỉ và thông tin chi tiết của bất động sản đã trích xuất với trang nguồn.

  • Xử lý cẩn thận các trang kết quả tiếp theo. Theo dõi phân trang (pagination), loại bỏ các bản ghi trùng lặp và kiểm tra nguyên nhân của các giá trị bị khuyết.

  • Chọn cấu hình proxy phù hợp với tác vụ. Hãy cân nhắc sử dụng proxy dân cư xoay vòng để kiểm soát vị trí địa lý và phiên kết nối, hoặc proxy di động để có lưu lượng không giới hạn. Lưu ý rằng không loại proxy nào đảm bảo quyền truy cập vào Zillow.

  • Xác thực dữ liệu trước khi xuất file. Lưu giá chào bán (asking price) tách biệt với giá ước tính (Zestimate) và giữ lại ngày giờ thu thập của từng bản ghi.

💡

Xem trước các cột trong file CSV
zpid,address,listing_status,asking_price,bedrooms,bathrooms,source_url,collected_at

Công cụ Zillow scraper có thể trích xuất những dữ liệu gì

Một trình thu thập dữ liệu Zillow có thể lấy nhiều thông tin khác nhau từ kết quả tìm kiếm, trang chi tiết của từng bất động sản và hồ sơ môi giới. Kết quả tìm kiếm thường cung cấp thông tin tóm tắt của tin đăng, trong khi trang chi tiết bất động sản bổ sung phần mô tả, định giá ước tính hoặc lịch sử biến động giá. Các trường thông tin liên hệ cần được kiểm tra riêng vì số liên lạc của môi giới hoặc sàn giao dịch khác với thông tin của chủ sở hữu nhà.

Trước khi thu thập dữ liệu Zillow, hãy xác định rõ cấu trúc dữ liệu đầu ra. Ví dụ Python dưới đây sử dụng sơ đồ dữ liệu chỉ dành cho các bất động sản đang rao bán (FOR_SALE):

Trường dữ liệu

Định dạng

Ý nghĩa trong ví dụ này

zpid

Chuỗi bắt buộc

Mã định danh bất động sản, được lưu dưới dạng văn bản và dùng để loại bỏ trùng lặp.

address

Chuỗi bắt buộc

Địa chỉ dạng văn bản được lấy từ bản ghi tìm kiếm.

listing_status

Chuỗi bắt buộc

FOR_SALE; ví dụ này loại trừ các trạng thái tin đăng khác.

asking_price

Số hoặc null

Giá chào bán, được giả định tính bằng USD trong ví dụ này.

bedrooms

Số hoặc null

Số phòng ngủ được công bố; số 0 vẫn là một giá trị hợp lệ.

bathrooms

Số hoặc null

Số phòng tắm được công bố, bao gồm cả các giá trị thập phân nếu có.

zestimate

Số hoặc null

Giá trị bất động sản ước tính theo giả định USD của ví dụ, được lưu tách biệt với giá chào bán.

description

Chuỗi hoặc null

Nội dung mô tả được trích xuất từ tài liệu chi tiết của bất động sản đã chọn.

source_url

Chuỗi bắt buộc

Đường dẫn URL của bất động sản gắn liền với bản ghi tìm kiếm.

search_source_url

Chuỗi bắt buộc

Đường dẫn URL xác định tài liệu tìm kiếm đã cung cấp bản ghi đó.

collected_at

Dấu thời gian kèm múi giờ

Thời điểm ghi nhận tài liệu tìm kiếm.

detail_collected_at

Dấu thời gian hoặc null

Thời điểm ghi nhận riêng của tài liệu chi tiết; nhận giá trị null khi không có tài liệu chi tiết nào được xử lý.

synthetic

Boolean (Logic)

Đánh dấu các bản ghi có nguồn gốc từ dữ liệu đầu vào giả lập (synthetic). Giá trị false không phải là bằng chứng độc lập về tính xác thực.

Đối với các tài liệu đã lưu sẵn, thời gian ghi nhận được lấy từ file kê khai đầu vào (input manifest); còn trình tải trực tiếp tùy chọn sẽ ghi lại thời điểm hoàn tất truy vấn. Bộ dữ liệu minh họa đi kèm sử dụng các dấu thời gian giả định.

Các giá trị không bắt buộc bị khuyết sẽ giữ nguyên là null trong JSON và để trống trong CSV. Nếu thiếu các trường bắt buộc hoặc giá trị số bị sai định dạng, script sẽ dừng thực thi thay vì tự tạo ra các giá trị phỏng đoán.

Giá thuê, giá bán gần nhất, diện tích, lịch sử giao dịch và các trường liên hệ của môi giới đòi hỏi quy tắc ánh xạ đã được kiểm chứng bổ sung nên không được xuất trong ví dụ này. Khi mở rộng sơ đồ dữ liệu, hãy luôn tách biệt giá chào bán, giá bán thực tế, tiền thuê và giá ước tính.

Lựa chọn giữa Python, công cụ thu thập dữ liệu và nguồn dữ liệu chính thức của Zillow

Hãy chọn phương pháp dựa trên bộ dữ liệu bạn cần và khả năng bảo trì kỹ thuật của bạn. Nhu cầu xuất bảng tính một lần có thể phù hợp với công cụ đám mây có sẵn, trong khi việc thu thập lặp lại với các trường tùy chỉnh đòi hỏi quyền kiểm soát sâu hơn đối với khâu trích xuất, xác thực và cập nhật dữ liệu.

Phương pháp

Phù hợp nhất cho

Lưu ý chính

Script Python

Tùy chỉnh trích xuất, xác thực và lưu trữ dữ liệu

Bạn tự bảo trì mã nguồn và kiểm tra quyền truy cập vào nguồn dữ liệu.

Yozh Scraper

Trích xuất tái sử dụng qua trình duyệt thông qua dịch vụ cục bộ (local service)

Yêu cầu triển khai hệ thống và có bộ quy tắc trích xuất (preset) đã được kiểm thử trên các trang mục tiêu.

Tiện ích mở rộng trình duyệt hoặc công cụ đám mây

Xuất dữ liệu mà không cần tự bảo trì trình thu thập

Kiểm tra các trường hỗ trợ, giới hạn thu thập, quyền riêng tư và tổng chi phí.

Scraping API được quản lý

Thuê ngoài khâu tải trang hoặc bóc tách dữ liệu

Xác nhận xem phản hồi trả về chứa mã HTML thô hay các bản ghi bất động sản đã có cấu trúc.

Chương trình dữ liệu chính thức hoặc tải dữ liệu nghiên cứu

Các tích hợp được phê duyệt hoặc phân tích thị trường theo khu vực

Kiểm tra độ bao phủ của bộ dữ liệu, điều kiện truy cập và các mục đích sử dụng được phép.

Trước khi quyết định sử dụng một công cụ, hãy thử nghiệm trên một mẫu nhỏ chứa các trường bắt buộc của bạn. Kiểm tra xem công cụ đó lấy được thông tin chi tiết của bất động sản hay chỉ lấy bản tóm tắt tìm kiếm, cách nó xử lý các giá trị bị khuyết và liệu file xuất ra có giữ nguyên mã định danh cùng ngày tháng hay không.

Một sản phẩm của bên thứ ba được quảng bá dưới tên gọi “Zillow API” không nhất thiết là dịch vụ chính thức của Zillow. Công cụ đó có thể chỉ thu thập nội dung trang web rồi phân phối qua endpoint riêng; bản thân tên gọi không xác lập quan hệ liên kết, giấy phép hay quyền thu thập dữ liệu.

Zillow Research cung cấp các chỉ số nhà ở theo khu vực, bao gồm giá trị nhà, giá thuê và lượng hàng tồn kho. Các chương trình dành cho nhà phát triển của Zillow Group bao gồm những sản phẩm cụ thể với điều kiện truy cập riêng. Các tùy chọn này phục vụ những mục đích khác nhau và không nên được xem là quyền truy cập không giới hạn vào mọi tin đăng hay trường thông tin liên hệ.

Cách thu thập dữ liệu Zillow bằng Python

Ví dụ này chuyển đổi các tài liệu tìm kiếm và chi tiết bất động sản đã lưu thành các bản ghi CSV và JSON. Mã nguồn đã được kiểm thử trên các mẫu HTML giả lập và phản hồi HTTP mô phỏng (mock), hoàn toàn không tải các trang Zillow trực tiếp trong quá trình xác thực.

Script đọc một tài liệu tìm kiếm, giữ lại tối đa năm bất động sản FOR_SALE duy nhất và có thể làm giàu dữ liệu cho một bất động sản được chọn từ tài liệu chi tiết tương ứng. Giới hạn năm bản ghi là quy định riêng của ví dụ này, không phải giới hạn kết quả tìm kiếm của Zillow.

💡

Môi trường kiểm thử: Linux x86_64, Python 3.12.14, Requests 2.34.2 và Beautiful Soup 4.15.0. Các thư viện phụ thuộc được cố định phiên bản trong file requirements.txt. Ví dụ được kiểm thử vào ngày 4 tháng 11 năm 2026 và kiểm tra lại vào ngày 5 tháng 11 năm 2026. Tải xuống ví dụ Python, danh sách thư viện cố định, bộ mẫu đã kiểm thử, bài kiểm thử tự động cùng các file xuất CSV và JSON tương ứng.

Thiết lập môi trường Python và xác định cấu trúc đầu ra

Giải nén các file ví dụ đi kèm, mở thư mục chứa file và tạo môi trường ảo Python:

python
python3 -m venv .venv

source .venv/bin/activate

python -m pip install -r requirements.txt

Dữ liệu đầu ra bao gồm mã định danh bất động sản, địa chỉ, trạng thái tin đăng, giá chào bán, số phòng ngủ, số phòng tắm, URL nguồn và dấu thời gian ghi nhận. Thông tin chi tiết tùy chọn của bất động sản bao gồm giá ước tính Zestimate và phần mô tả.

Hãy lưu thông tin xác thực tách biệt khỏi mã nguồn script. Phần cấu hình proxy HTTP tùy chọn sẽ đọc biến môi trường ZILLOW_PROXY_URL; nếu để trống biến này, chương trình sẽ kết nối trực tiếp. Trình khách Python này không kế thừa cài đặt proxy từ trình duyệt Chrome trên máy tính.

File sample.json đi kèm chỉ định hai file HTML giả lập cùng thời điểm lưu mẫu của chúng. Quá trình xử lý trang lưu sẵn sẽ bảo toàn các dấu thời gian đó thay vì lấy ngày chạy trình phân tích làm ngày thu thập dữ liệu.

Kiểm tra cấu trúc một trang tìm kiếm và một trang chi tiết bất động sản

Trước khi quyết định cách trích xuất dữ liệu từ Zillow, hãy kiểm tra các trang nằm trong phạm vi bạn được cấp quyền. So sánh phản hồi HTML gốc với trang đã được kết xuất (render) trên trình duyệt để xác định chính xác vị trí chứa thông tin cần thiết.

Trình phân tích mẫu tìm kiếm khối JSON được nhúng bên trong thẻ:

html
<script id="__NEXT_DATA__" type="application/json">

Sau đó, script kiểm tra các cấu trúc đường dẫn cụ thể sau:

bash
Search records:

props.pageProps.searchPageState.cat1.searchResults.listResults

Property details:

props.pageProps.componentProps.gdpClientCache

Đây là các cấu trúc giả định được áp dụng cho bộ mẫu đi kèm, không phải giao diện cố định vĩnh viễn của Zillow. Hãy đối chiếu chúng với các tài liệu nguồn được phép của bạn trước khi áp dụng trình phân tích.

Nếu các trường dữ liệu chỉ xuất hiện sau khi JavaScript chạy xong, một truy vấn HTTP thông thường kèm trình phân tích HTML sẽ không thể tái tạo được nội dung đó. Khi này bạn có thể cần đến công cụ tự động hóa trình duyệt, nhưng việc kết xuất trang bằng trình duyệt tự nó không tạo ra quyền truy cập hợp lệ hay đảm bảo kết nối thành công.

Tải trang và xác thực phản hồi

Hàm fetch_html() trong script đầy đủ sẽ kiểm tra mã trạng thái HTTP, URL cuối cùng và kiểu nội dung (Content-Type) trước khi trả về mã HTML:

python
def fetch_html(url):
    url = source_url(url)
    proxy = os.getenv("ZILLOW_PROXY_URL")

    if proxy and urlsplit(proxy).scheme != "http":
        raise ValueError("This example supports an HTTP proxy endpoint only")

    with requests.Session() as session:
        session.trust_env = False

        if proxy:
            session.proxies = {"https": proxy}

        with session.get(
            url,
            timeout=(5, 20),
            allow_redirects=False,
        ) as response:
            if response.status_code != 200:
                raise ValueError(
                    f"Stopped: HTTP {response.status_code}; no automatic retry"
                )

            if response.url != response.request.url:
                raise ValueError("Unexpected final URL")

            source_url(response.url)
            media_type = (
                response.headers.get("Content-Type", "")
                .split(";")[0]
                .lower()
                .strip()
            )

            if media_type not in {"text/html", "application/xhtml+xml"}:
                raise ValueError("Expected an HTML response")

            return response.text

Giá trị timeout cho phép tối đa 5 giây để thiết lập kết nối và 20 giây chờ đọc dữ liệu khi luồng truyền bị ngắt quãng; đây không phải là tổng thời gian chạy tối đa của toàn bộ truy vấn. Tài liệu chính thức của thư viện Requests có giải thích rõ sự khác biệt này.

Ngay cả khi nhận được phản hồi HTTP 200, bạn vẫn cần kiểm tra nội dung bên trong. Một trang kiểm tra bảo mật (challenge page), tài liệu ngoài dự kiến hoặc cấu trúc dữ liệu bị thay đổi không được phép biến thành một lần xuất file "thành công" nhưng chứa 0 bản ghi. Hàm tiếp theo sẽ kiểm tra sự tồn tại của khối JSON dự kiến:

python
def page_props(html):
    soup = BeautifulSoup(html, "html.parser")
    node = soup.find("script", id="__NEXT_DATA__")

    if node is None:
        raise ValueError(
            "Expected JSON block absent: inspect the returned page"
        )

    try:
        props = json.loads(node.get_text())["props"]["pageProps"]
    except (KeyError, TypeError, json.JSONDecodeError) as exc:
        raise ValueError("Invalid or changed page-data structure") from exc

    if not isinstance(props, dict):
        raise ValueError("Expected a pageProps object")

    return props

Trích xuất bản ghi tin đăng và thông tin chi tiết của bất động sản đã chọn

Ví dụ này chỉ giữ lại các bản ghi được đánh dấu rõ là FOR_SALE, yêu cầu phải có mã định danh cùng địa chỉ, đồng thời loại bỏ các mã ZPID trùng lặp trước khi áp dụng giới hạn năm bản ghi. Các tin đăng cho thuê và nhà đã bán bị loại trừ để tránh việc giá thuê hoặc giá bán cũ bị gắn nhầm thành giá chào bán hiện tại.

Các trường số không bắt buộc sẽ giữ giá trị None khi bị khuyết. Nếu xuất hiện chuỗi ký tự bất thường, số âm hoặc số không hợp lệ, chương trình sẽ dừng xử lý ngay thay vì âm thầm chuyển đổi sai dữ liệu:

python
def number(value, positive=False):
    if value is None:
        return None

    if (
        isinstance(value, bool)
        or not isinstance(value, (int, float))
        or not math.isfinite(value)
        or value < 0
        or (positive and value == 0)
    ):
        raise ValueError(
            "Unexpected numeric value; review the field mapping"
        )

    return value

Chỉ nên nạp thêm trang chi tiết tùy chọn khi bạn thực sự cần các trường dữ liệu bổ sung. Đường dẫn URL và mã định danh bất động sản của trang chi tiết phải khớp với bản ghi tìm kiếm đã chọn. Trình phân tích sẽ lưu giá trị Zestimate tách biệt khỏi giá chào bán và ghi lại dấu thời gian riêng cho trang chi tiết.

Đây là nguyên tắc cốt lõi để thu thập dữ liệu Zillow chính xác: bảo toàn đúng ý nghĩa của từng trường và tuyệt đối không tự bịa ra các giá trị bị thiếu.

Chạy thử trên mẫu nhỏ và kiểm tra kết quả

Chạy toàn bộ script trên các trang HTML giả lập được cung cấp:

shell
python zillow_sample.py sample.json --output run-01

Thư mục đầu ra phải là một thư mục mới chưa tồn tại. Bộ mẫu kiểm thử sẽ trả về kết quả:

bash
Saved 2 unique listings to run-01

Một bản ghi kết quả sẽ có cấu trúc như dưới đây (toàn bộ thông tin bất động sản bên dưới đều là dữ liệu giả định):

json
{
  "zpid": "9000000001",
  "address": "100 Example Lane, Sample City, TX 00000",
  "listing_status": "FOR_SALE",
  "asking_price": 395000,
  "bedrooms": 3,
  "bathrooms": 2,
  "source_url": "https://www.zillow.com/homedetails/example-property/9000000001_zpid/",
  "search_source_url": "https://www.zillow.com/synthetic-test-city/",
  "collected_at": "2026-11-04T10:00:00+00:00",
  "zestimate": 400000,
  "description": "Synthetic property description for parser testing only.",
  "detail_collected_at": "2026-11-04T10:01:00+00:00",
  "synthetic": true
}

Một số cột trích từ file xuất CSV cho thấy cách các giá trị bị khuyết được giữ nguyên:

bash
zpid,listing_status,asking_price,bedrooms,bathrooms,zestimate
9000000001,FOR_SALE,395000,3,2,400000
9000000002,FOR_SALE,,0,1,

Hãy đối chiếu mã định danh, giá cả, các trường để trống và số lượng bản ghi trùng lặp với dữ liệu đầu vào. Bản ghi thứ hai chủ đích giữ nguyên mức giá bị khuyết trong khi vẫn lưu số phòng ngủ bằng 0.

Đối với hoạt động thu thập trực tiếp đã được cấp phép, hãy truyền vào một file kê khai (manifest) riêng chứa các URL đã xác minh kèm cờ --live; ở chế độ này, script sẽ từ chối dữ liệu đầu vào giả lập. Bạn có thể chạy lại bộ kiểm thử đi kèm bằng lệnh:

shell
python -m unittest -v

Việc vượt qua các bài kiểm thử này xác nhận hành vi xử lý của code đúng như thiết kế, chứ không đảm bảo quyền truy cập thực tế vào Zillow hiện tại. Hãy luôn kiểm tra cấu trúc trang thực tế trên một mẫu được phép trước khi mở rộng quy mô thu thập.

Ghi chú xác thực

Tất cả 13 bài kiểm thử tự động đều đã vượt qua vào ngày 4 tháng 11 năm 2026 và được kiểm tra lại thành công vào ngày 5 tháng 11. Các bài kiểm tra sử dụng mẫu HTML giả lập và phản hồi HTTP mô phỏng.

Hạng mục kiểm tra

Kết quả

Phạm vi mẫu thử

Một tài liệu tìm kiếm và một tài liệu chi tiết tương ứng.

Bản ghi thô ban đầu

Ba mục tìm kiếm trước khi lọc trùng lặp.

Bản ghi đã xuất

Hai bất động sản duy nhất sau khi loại bỏ một bản ghi trùng.

Bản ghi bị từ chối

Không có trong bộ mẫu minh họa đi kèm.

Bản ghi khuyết thông tin

Một bản ghi được giữ lại có chứa các trường tùy chọn bị trống.

Các giá trị bị khuyết

asking_price, zestimate, description và detail_collected_at nhận giá trị null trong bản ghi đó.

Đối chiếu file xuất

File CSV và JSON tạo mới khớp hoàn toàn với các file mẫu đi kèm.

Dấu thời gian chi tiết (detail_collected_at) bị trống có nghĩa là không có tài liệu chi tiết nào được xử lý cho bất động sản đó; điều này không đồng nghĩa với lỗi truy vấn trang chi tiết.

Các bài kiểm thử riêng biệt đã kiểm tra những tình huống lỗi dự kiến, bao gồm phản hồi từ chối truy cập, URL hoặc kiểu nội dung bất thường, thiếu mã định danh, JSON sai cú pháp và cấu trúc trang bị thay đổi. Khả năng tương thích trực tiếp với Zillow, phân trang và kết nối qua nhà cung cấp proxy không nằm trong phạm vi kiểm thử tự động này.

Sử dụng Yozh Scraper để trích xuất dữ liệu lặp lại

Yozh Scraper trên GitHub cung cấp một dịch vụ tự triển khai (self-hosted) hỗ trợ kết xuất trình duyệt, bóc tách trường dữ liệu và chạy tác vụ bất đồng bộ. Bạn có thể tái sử dụng bộ quy tắc trích xuất qua nhiều truy vấn trong khi vẫn để Python phụ trách khâu xác thực và lưu trữ.

Yozh Scraper hỗ trợ kết xuất bằng trình duyệt, thiết lập quy tắc bóc tách và lưu các mẫu cấu hình (preset) tái sử dụng. Hướng dẫn này không đi kèm preset Zillow đã kiểm chứng trên trang trực tiếp. Hãy xác thực các trường cần thiết trên tài liệu nguồn được phép trước khi dùng preset tùy chỉnh để thu thập dữ liệu định kỳ như một trình thu thập web Zillow (Zillow web scraper).

  1. Khởi động dịch vụ và kiểm tra trạng thái hoạt động. Sau khi sao chép (clone) kho lưu trữ, hãy chép file .env.example thành .env và chạy lệnh docker compose up --build. Trong một cửa sổ terminal khác, hãy kiểm tra địa chỉ http://localhost:8000/api/v1/health. Phản hồi hoạt động tốt chỉ xác nhận dịch vụ đã sẵn sàng, chưa khẳng định việc bóc tách dữ liệu bất động sản thành công.

  2. Tạo và kiểm chứng preset tùy chỉnh. Mở trang http://localhost:8000/docs và tạo một preset bằng các quy tắc CSS hoặc XPath đã kiểm tra. Đặt tên preset bắt đầu bằng tiền tố user_, ví dụ user_zillow_property. Kiểm thử thông qua endpoint POST /api/v1/presets/{name}/test trên một mẫu được phép, sau đó kiểm tra thêm trên một trang bất động sản khác trước khi tái sử dụng bộ quy tắc.

  3. Chạy thử một tác vụ nhỏ. Gửi URL của một bất động sản qua POST /api/v1/scrape/preset/page bằng cấu hình preset đã lưu. Lưu lại mã job_id trả về; mã này dùng để định danh tác vụ chạy ngầm chứ chưa xác nhận quá trình trích xuất đã hoàn tất thành công.

  4. Xác thực các trường dữ liệu bất động sản. Ánh xạ kết quả vào cùng sơ đồ dữ liệu như trong ví dụ Python: mã định danh bất động sản, địa chỉ, trạng thái tin đăng, giá chào bán, số phòng ngủ, số phòng tắm và URL nguồn. Thêm dấu thời gian thu thập, kiểm tra kiểu dữ liệu số và giữ nguyên các giá trị tùy chọn bị khuyết. Đối chiếu kết quả với trang nguồn trước khi tăng khối lượng thu thập.

  5. Lấy kết quả từ Python. Truy vấn định kỳ (poll) endpoint GET /api/v1/scrape/{job_id} với độ trễ hợp lý và giới hạn tổng thời gian chờ (timeout), đồng thời xử lý các tác vụ bị lỗi hoặc bị hủy. Sau khi tác vụ hoàn tất, hãy gọi GET /api/v1/scrape/{job_id}/results, kiểm tra các lỗi và cảnh báo, sau đó xác thực các bản ghi trước khi xuất file.

💡

Để xem hướng dẫn cài đặt chi tiết hơn và cách dùng công cụ kiểm thử trực quan tùy chọn, hãy tham khảo hướng dẫn về thu thập dữ liệu màn hình (screen scraping).

Xử lý phân trang và kết quả tìm kiếm không đầy đủ

Một lần tìm kiếm trên Zillow nên được xem là một mẫu dữ liệu xác định, chứ không phải danh sách toàn bộ bất động sản trong khu vực. Ranh giới địa lý, bộ lọc tin đăng, cơ chế phân trang và lỗi thu thập đều có thể ảnh hưởng đến những bản ghi nào sẽ xuất hiện, ngay cả khi trang tìm kiếm hiển thị tổng số lượng lớn hơn nhiều.

Trước khi mở rộng quy mô thu thập, hãy định nghĩa rõ một bản ghi đại diện cho đối tượng nào: một mảnh đất kèm nhà, một căn hộ riêng lẻ (unit), cả tòa nhà hay một tin đăng. Không nên gộp chung cả tòa chung cư và các căn hộ đang trống bên trong chỉ vì chúng có cùng một địa chỉ đường phố.

Ví dụ Python ở trên chỉ đọc một trang tìm kiếm và chưa tích hợp phân trang. Để mở rộng, hãy kiểm tra giao diện tìm kiếm được phép và xác minh cách trang tiếp theo được gọi. Giữ nguyên tọa độ giới hạn địa lý, bộ lọc và thứ tự sắp xếp ban đầu trong suốt quá trình chạy, đồng thời lưu lại các thông số đó kèm theo bộ dữ liệu.

Hãy dừng lại khi trang kết quả bị lặp lại, không trả về mã định danh mới nào, hoặc khi đạt đến giới hạn số trang, số bản ghi hay thời gian chạy mà bạn đã cấu hình. Ghi lại lý do dừng để có thể phân biệt rõ giữa một mẫu giới hạn chủ đích và một lần thu thập bị lỗi giữa chừng.

Loại bỏ các kết quả trùng lặp giao nhau bằng mã ZPID (nếu mã này định danh đúng thực thể bạn cần) hoặc bằng một mã định danh bất động sản, căn hộ hay tin đăng đã kiểm chứng khác. Tránh chỉ dựa vào mỗi địa chỉ văn bản.

Ở mỗi lần chạy, hãy ghi nhận đầy đủ bốn chỉ số:

  • Bản ghi thô (Raw records): tổng số mục trả về trước khi lọc trùng lặp và xác thực.

  • Bản ghi duy nhất (Unique records): số mục còn lại sau khi đã loại bỏ trùng lặp.

  • Bản ghi bị từ chối (Rejected records): số mục bị loại bỏ kèm theo lý do cụ thể.

  • Bản ghi khuyết thông tin (Incomplete records): các mục hợp lệ được giữ lại nhưng thiếu trường tùy chọn; những mục này vẫn được tính vào số bản ghi duy nhất.

Hãy báo cáo phạm vi bao phủ thực tế được chứng minh qua phương pháp đã kiểm thử của bạn. Các tài liệu hướng dẫn khác nhau thường nêu các giới hạn tìm kiếm và cách tiếp cận khác nhau, vì vậy đừng coi một con số cố định nào là giới hạn kết quả chung cho mọi tìm kiếm trên Zillow.

Làm sạch dữ liệu Zillow và xuất ra CSV hoặc JSON

Các bản ghi trích xuất được chỉ thực sự hữu ích khi ý nghĩa của từng cột không bị sai lệch trong quá trình sắp xếp, lọc và so sánh. Trước khi xuất file, hãy chuẩn hóa đồng nhất từng trường dữ liệu và kiểm tra đối chiếu một vài bản ghi với trang nguồn.

  • Lưu mã bưu chính (ZIP code) dưới dạng chuỗi văn bản. Các chữ số 0 ở đầu rất quan trọng, vì vậy hãy chọn định dạng cột là văn bản (Text) khi nhập file CSV vào Excel.

  • Tách biệt các thành phần của giá. Lưu số tiền, đơn vị tiền tệ, loại giá và kỳ hạn thuê vào các trường độc lập. Giá chào bán, giá bán gần nhất và giá ước tính Zestimate phải nằm ở các cột riêng biệt hoặc các bản ghi được gắn nhãn rõ ràng.

  • Giữ lại đơn vị đo diện tích. Lưu con số đo lường kèm theo đơn vị (ví dụ: feet vuông - sqft), để các phép tính sau này sử dụng cùng một hệ quy chiếu tương đương.

  • Phân biệt rõ giữa giá trị bị khuyết và số 0. Sử dụng giá trị null trong JSON hoặc ô trống trong CSV cho các thông tin không có sẵn. Số phòng ngủ bằng 0 có thể mô tả một căn hộ studio; không được tự động biến số 0 này thành giá trị bị thiếu.

  • Bảo toàn nguồn gốc dữ liệu (provenance). Luôn đính kèm URL nguồn và dấu thời gian thu thập để mọi bản ghi đều có thể truy xuất và kiểm tra lại.

Định dạng CSV rất phù hợp cho các bản ghi bất động sản dạng bảng phẳng (flat records). Đối với lịch sử biến động giá hoặc lịch sử tin đăng lồng nhau (nested), hãy lưu trong JSON hoặc xuất ra một bảng riêng liên kết với bất động sản qua mã định danh.

Khi theo dõi biến động thị trường định kỳ, hãy lưu thêm các bản ghi mới kèm ngày tháng thay vì ghi đè lên giá trị cũ. Việc một tin đăng biến mất trong lần chạy sau không chứng minh rằng bất động sản đó đã bán: thay đổi bộ lọc, thu thập thiếu trang hoặc chủ nhà gỡ tin đều có thể là nguyên nhân.

Bản ghi JSON mẫu từ bộ file kiểm thử đi kèm

Đây là bản ghi thứ hai được tạo bởi ví dụ Python. Mã định danh bất động sản, địa chỉ, đường dẫn URL và dấu thời gian ghi nhận đều là giả định. Mức giá chào bán bị khuyết được giữ nguyên là null, trong khi số phòng ngủ bằng 0 vẫn được lưu là số 0:

json
{
  "zpid": "9000000002",
  "address": "200 Example Lane, Sample City, TX 00000",
  "listing_status": "FOR_SALE",
  "asking_price": null,
  "bedrooms": 0,
  "bathrooms": 1,
  "source_url": "https://www.zillow.com/homedetails/second-example/9000000002_zpid/",
  "search_source_url": "https://www.zillow.com/synthetic-test-city/",
  "collected_at": "2026-11-04T10:00:00+00:00",
  "zestimate": null,
  "description": null,
  "detail_collected_at": null,
  "synthetic": true
}

Các nguyên tắc kiểm tra ở trên cũng áp dụng cho những trường dữ liệu bạn có thể bổ sung sau này; còn script đi kèm sử dụng sơ đồ rút gọn chỉ dành cho tin rao bán (FOR_SALE) đã định nghĩa ở phần trước.

Chọn loại proxy và cài đặt phiên kết nối phù hợp với công việc

Proxy kiểm soát tuyến mạng và địa chỉ IP đầu ra mà trình thu thập dữ liệu của bạn sử dụng. Hãy chọn loại kết nối dựa trên dung lượng băng thông, vị trí địa lý và tính liên tục của phiên làm việc; lưu ý rằng không loại proxy nào đảm bảo quyền truy cập vào Zillow.

Loại kết nối

Mức độ phù hợp với tác vụ

Mức giá

Dân cư xoay vòng (Rotating residential)

Nhắm mục tiêu theo vị trí địa lý kèm phiên cố định (sticky sessions) lên đến 24 giờ cho các bước truy vấn nối tiếp nhau; phù hợp nhất cho cào dữ liệu web, thu thập dữ liệu AI, tự động hóa, SEO và nghiên cứu thị trường.

Gói tiêu chuẩn: $2/GB kèm định vị địa lý miễn phí

Di động (Mobile)

IP sạch từ nhà mạng di động với lưu lượng không giới hạn giúp kiểm soát chi phí băng thông khi phải kết xuất trình duyệt, tải script và hình ảnh lặp đi lặp lại; phù hợp nhất cho cào dữ liệu web, tự động hóa, SEO và nghiên cứu thị trường.

LTE/5G từ $1.7/ngày với hỗ trợ giao thức HTTP, SOCKS5 và UDP.

ISP tĩnh (Static ISP)

Một địa chỉ IP cố định phù hợp cho các tác vụ theo dõi lặp lại cần tuyến mạng ổn định cùng băng thông không giới hạn; phù hợp nhất cho cào dữ liệu web, kiểm thử QA, tự động hóa và truy cập theo khu vực cố định.

$5.29/tháng với thời gian hoạt động (uptime) 99.9%.

Trung tâm dữ liệu (Datacenter)

Proxy trung tâm dữ liệu IPv4/IPv6 chuyên dụng tốc độ cao với băng thông không giới hạn; phù hợp nhất cho cào dữ liệu web, tự động hóa, giám sát hệ thống, tích hợp API và thu thập dữ liệu quy mô lớn.

$2.18/tháng với hỗ trợ giao thức HTTP và SOCKS5.

Đối với trình thu thập dựa trên trình duyệt, một cấu hình đã được kiểm chứng trong tài liệu là Ubuntu 24.04, Playwright Chromium và proxy HTTP có xác thực bằng tên người dùng/mật khẩu. Hãy thiết lập server, username và password thông qua hàm browser.new_context(proxy=...), đồng thời nạp thông tin đăng nhập từ biến môi trường. Tài liệu của Playwright quy định phần xác thực proxy HTTP tách biệt với hỗ trợ SOCKSv5, vì vậy đừng mặc định rằng SOCKS5 có mật khẩu sẽ hoạt động theo cùng một cách trong Chromium.

Hãy mở một trang kiểm tra IP ngay bên trong ngữ cảnh trình duyệt (browser context) đó để xác nhận IP đầu ra và vị trí địa lý. Sau đó, chạy thử nghiệm trích xuất trên một mẫu đại diện được phép và kiểm tra kỹ các bản ghi trả về trước khi tăng tải.

🔥

Sử dụng công cụ kiểm tra địa chỉ IP của CyberYozh App để đánh giá độ tin cậy thực tế bằng các hệ thống chống gian lận hàng đầu.

Cách thu thập dữ liệu Zillow kèm số điện thoại

Khả năng hiển thị của trường thông tin liên hệ phụ thuộc vào từng tin đăng, nguồn dữ liệu và phương pháp trích xuất. Trước khi bổ sung tính năng lấy số điện thoại vào trình thu thập Zillow, hãy xác minh rằng nguồn được phép có công khai trường liên hệ đó và nêu rõ số điện thoại thuộc về ai.

Thông tin liên hệ của môi giới và sàn giao dịch khác với thông tin của chủ nhà. Một số điện thoại hiển thị bên cạnh tin đăng không được tự động gán vào trường owner_phone (số điện thoại chủ nhà) trong bộ dữ liệu của bạn. Hãy kiểm tra tên và nhãn vai trò đi kèm để lưu lại đúng mối quan hệ được công bố thay vì phỏng đoán người nghe máy.

Đối với mỗi thông tin liên hệ chuyên nghiệp được phép thu thập, hãy lưu lại đầy đủ:

  • Số điện thoại: lưu dưới dạng chuỗi văn bản, bao gồm cả số máy lẻ (extension) nếu có hiển thị.

  • Tên và vai trò: lưu chính xác như thông tin được xác định trên trang nguồn.

  • URL nguồn: trang cụ thể chứa thông tin liên hệ đó.

  • Dấu thời gian thu thập: thời điểm thông tin được ghi nhận.

Hãy tách riêng từng liên hệ nếu có nhiều số điện thoại trên cùng một trang để số cá nhân của môi giới không bị gắn nhầm với tên của công ty môi giới. Các số điện thoại bị khuyết phải được giữ nguyên là null trong JSON hoặc để trống trong CSV; hãy ghi nhận lỗi bóc tách riêng biệt thay vì tự điền vào chỗ trống bằng thông tin phỏng đoán.

Việc thông tin được hiển thị công khai trên web không đồng nghĩa với sự cho phép cho mọi mục đích thu thập, tái sử dụng hay tiếp thị liên hệ (outreach). Hãy xem xét kỹ điều kiện truy cập của trang nguồn và các quy định pháp lý hiện hành về quyền riêng tư trước khi thu thập hoặc sử dụng thông tin liên hệ của các chuyên gia môi giới.

🔥

Thuê ngay số điện thoại di động ảo hoặc số điện thoại dân cư tương thích với các dịch vụ trực tuyến phổ biến.

Khắc phục các sự cố thường gặp khi thu thập dữ liệu Zillow

Hãy xác định đúng khâu gặp sự cố trước khi thay đổi cấu hình. Sự cố kết nối liên quan đến định tuyến mạng, xác thực proxy hoặc giao thức truyền tải; hạn chế truy cập từ chối hoặc giới hạn tần suất truy vấn; còn lỗi trích xuất xảy ra khi bộ phân tích không thể đọc đúng cấu trúc nội dung trả về.

Dấu hiệu lỗi

Nguyên nhân có thể

Bước kiểm tra tiếp theo

Phản hồi 403 hoặc trang xác minh (challenge page)

Từ chối truy cập hoặc kiểm tra lưu lượng tự động

Kiểm tra nội dung phản hồi (response body) để xác định dịch vụ nào đã trả về mã lỗi. Tạm dừng các lần thử lại liên tiếp và kiểm tra phương thức truy cập được phép.

Phản hồi 429

Vượt quá giới hạn tần suất truy vấn (rate limit)

Tuân thủ thời gian chờ trong header Retry-After nếu có. Giảm tần suất gửi yêu cầu và số luồng chạy song song, tăng dần thời gian nghỉ giữa các lần thử lại và giới hạn tổng số lần thử.

HTTP 200 nhưng không có bản ghi nào

Nội dung trả về ngoài dự kiến hoặc lệch cấu trúc bóc tách

Kiểm tra URL cuối cùng, kiểu nội dung (Content-Type) và tài liệu HTML đã lưu. Xác nhận các trường dữ liệu dự kiến thực sự tồn tại trước khi sửa bộ chọn (selector).

Danh sách tin đăng thu thập bị thiếu

Do bộ lọc, phân trang, giới hạn thu thập hoặc các truy vấn bị lỗi

Đối chiếu ranh giới địa lý và bộ lọc; kiểm tra lại số trang đã quét, số bản ghi bị loại và lý do dừng chạy.

Bất động sản bị trùng lặp

Các vùng tìm kiếm giao nhau hoặc trang kết quả bị lặp lại

Lọc trùng bằng mã định danh đã kiểm chứng (như zpid) đồng thời giữ nguyên các căn hộ riêng biệt và lịch sử ghi nhận theo từng mốc ngày tháng.

Thiếu số điện thoại

Trường liên hệ không có sẵn trên trang hoặc ánh xạ sai bộ chọn

Kiểm tra tài liệu nguồn và nhãn vai trò liên hệ. Giữ nguyên giá trị rỗng thay vì tự suy đoán.

Giá bị sai hoặc bị trống

Lấy nhầm trường giá hoặc lỗi chuyển đổi định dạng số

Kiểm tra riêng biệt giá chào bán, giá thuê, giá bán thực tế và Zestimate. Xác nhận lại đơn vị tiền tệ và kỳ hạn tính giá thuê.

Trình phân tích dừng chạy sau khi trang web cập nhật

Cấu trúc HTML hoặc khối JSON nhúng đã thay đổi

So sánh với các file mẫu đã lưu trước đó, cập nhật lại selector hoặc đường dẫn khóa JSON bị ảnh hưởng và kiểm thử lại trên một mẫu nhỏ.

Đối với các lỗi kết nối mạng, hãy kiểm tra lại địa chỉ máy chủ proxy, thông tin xác thực và tuyến định tuyến thực tế trong trình khách gửi yêu cầu. Sau bất kỳ chỉnh sửa nào, hãy luôn đối chiếu các giá trị trích xuất được với trang nguồn trước khi khởi chạy lại tác vụ thu thập quy mô lớn.

Xây dựng bộ dữ liệu có thể kiểm chứng

Hãy chọn nguồn dữ liệu được phép, xác thực trên một mẫu nhỏ và bảo toàn nguyên vẹn ý nghĩa của từng trường khi xuất file. Chỉ mở rộng quy mô sau khi đã đối chiếu kết quả đầu ra với trang nguồn. Hãy thử nghiệm bộ quy tắc trích xuất đã kiểm chứng của bạn trong Yozh Scraper trước khi thiết lập quy trình thu thập định kỳ.

🦔

Nếu quá trình thu thập dữ liệu yêu cầu vị trí IP đầu ra cụ thể hoặc địa chỉ IP duy trì ổn định, hãy chọn gói kết nối phù hợp trong danh mục proxy và kiểm thử trực tiếp với cấu hình trích xuất của bạn.

 

Helpful?

Share article

Câu hỏi thường gặp về Zillow Scraper