Tất cả bài viết

Scraping các trang tuyển dụng không vấp phải giới hạn 50 lượt lưu

Scraping các trang tuyển dụng đã trở thành một trong những tác vụ khó khăn nhất trên web mở vào năm 2026. Dưới đây là những thay đổi và cách các đội ngũ talent intelligence tiếp tục thu thập dữ liệu.

Thử thách

Một báo cáo đánh giá hiệu năng (benchmark) vào tháng 6 năm 2026 từ ApplyArc đã thử nghiệm năm công cụ scraper LinkedIn trên 200 lượt lấy dữ liệu công việc thực tế. Ba công cụ đã khiến tài khoản bị gắn cờ hoặc bị bóp băng thông (throttled) một cách âm thầm sau khoảng 50 lượt lưu. Chỉ có hai công cụ vượt qua một cách suôn sẻ.

Báo cáo đánh giá đó đã phản ánh toàn bộ thực trạng. Các trang tuyển dụng từng là những mục tiêu dễ dàng. Giờ đây, chúng là một trong những mục tiêu khó khăn nhất trên web mở.

Nếu bạn đang xây dựng bất kỳ sản phẩm nào phụ thuộc vào dữ liệu tin tuyển dụng (hoạch định nhân sự, đối chuẩn lương, bản đồ tài năng, tín hiệu tuyển dụng cho nghiên cứu cổ phiếu), lớp thu thập dữ liệu của bạn đang phải đối đầu với một loạt các cơ chế phòng vệ chưa từng tồn tại cách đây hai năm. Indeed đưa ra CAPTCHA đối với các session lạ. LinkedIn liên kết các tín hiệu phía trình duyệt qua các lượt xoay vòng IP. Glassdoor thực hiện rate-limit theo từng ASN, chứ không phải theo từng IP. ZipRecruiter đẩy dải lương và ngày đăng tuyển vào JavaScript, vốn chỉ render nếu các header của bạn trông giống như của một người dùng thực tế chứ không phải một script.

Vì vậy, giới hạn 50 lượt lưu không phải là vấn đề riêng của LinkedIn. Đó là đặc tính chung của toàn bộ danh mục này.

Tại sao các trang tuyển dụng ngày càng khó thu thập dữ liệu

Ba yếu tố đã thay đổi vào năm 2026, và chúng cộng dồn lại với nhau.

Đầu tiên là việc phát hiện bot đã chuyển sang phân tích hành vi. Các kiểm tra tĩnh (User-Agent, uy tín IP, số request mỗi giây) từng đủ để ngăn chặn các scraper nghiệp dư. Nhưng giờ thì không còn nữa. Các cơ chế phòng vệ ngày nay theo dõi cách bạn di chuyển qua trang web: bạn tải những trang nào theo thứ tự nào, bạn dành bao nhiêu thời gian, liệu bạn có tải lại cùng một gói JS mà một trình duyệt thực tế sẽ lưu vào bộ nhớ đệm (cache) hay không. Chúng tôi đã viết về sự chuyển dịch này trong bài viết Phát hiện bot đã chuyển sang phân tích hành vi. Các trang tuyển dụng đã áp dụng cơ chế này từ sớm vì khách truy cập của họ thực hiện một số lượng nhỏ các hành động lặp đi lặp lại (tìm kiếm, nhấp chuột, đọc, lưu), và điều đó khiến một script dễ dàng bị phát hiện khi nó bỏ qua một nửa quy trình.

Thứ hai là kích thước của proxy pool không còn quan trọng nữa. Một pool IP dân cư gồm 50 triệu địa chỉ cũng không giúp ích gì khi cơ chế phòng vệ là liên kết fingerprint ở lớp kết nối cộng với uy tín của ASN. Chúng tôi đã đề cập đến vấn đề này trong bài viết Tại sao kích thước proxy pool không còn quan trọng. Giải pháp hiệu quả là chọn đúng điểm thoát (exit) cho trang web mục tiêu, chứ không phải là sở hữu nhiều điểm thoát hơn bất kỳ ai khác.

Thứ ba là khía cạnh pháp lý. Cả Indeed và LinkedIn đều có các đội ngũ pháp lý sẵn sàng khởi kiện. Kỷ nguyên chạy một scraper công khai từ IP nhà riêng đã kết thúc đối với bất kỳ ai có kế hoạch thương mại hóa dữ liệu thu thập được.

Quy trình thu thập dữ liệu hiện nay

Đối với công việc talent intelligence vào năm 2026, mô hình hoạt động hiệu quả là một kiến trúc phân tách (split stack): một lượt fetch được render bằng trình duyệt thực tế cho các trang tuyển dụng được bảo vệ, kết hợp với việc lựa chọn điểm thoát cẩn thận để bạn không truy cập từ cùng một nhà cung cấp như mọi bot khác.

Với một nền tảng như FourA, đó là hai sản phẩm giao tiếp với nhau.

Trình duyệt xử lý phần render: gửi một URL với unblocker: true, nhận lại HTML đã render, cookie, và ảnh chụp màn hình từ một session trình duyệt thực tế. JS được thực thi, các trường tải chậm (lazy-loaded) được điền đầy đủ, và request vượt qua các kiểm tra ở lớp kết nối vốn thường chặn hầu hết các client cơ bản. Việc lựa chọn proxy diễn ra ngầm: nền tảng chọn một điểm thoát cho mỗi request và trả về ID base36 ẩn danh của nó trong response (tại r.proxy ở cấp cao nhất trên Single/Browser, hoặc r.session.proxy trên Auto), nhờ đó các lệnh gọi tiếp theo có thể tái sử dụng cùng một điểm thoát khi bạn cần duy trì tính liên tục của session. Đối với hầu hết các tác vụ trên trang tuyển dụng, Auto là điểm khởi đầu phù hợp, nó điều phối Single, Proxy, và Browser dựa trên nhu cầu của từng mục tiêu, giúp mã nguồn của bạn không phải tự xử lý việc đó.

import requests

r = requests.post(
    "https://api.foura.ai/api/auto",
    headers={"Authorization": "Bearer pk_live_..."},
    json={
        "url": "https://www.example-jobs.com/search?q=data+engineer&l=Remote",
        "validate": {
            "status": {"accept": [200]},
            "data":   {"accept": ["data-testid=\"job-card\""],
                       "fail":   ["Just a moment", "captcha"]},
        },
    },
).json()

# r["data"] or r["body"]   — rendered content (Auto picks Single→"data" or Browser→"body" per host)
# r["session"]              — { "proxy": "<base36 id>", "cookies": [...], "userAgent": "..." }
# Reuse r["session"]["proxy"] on the next call to stick to the same exit, or pass it
# via `ignoreProxies: [<id>]` to force a different one.

Hai lưu ý về những gì giải pháp này thực sự mang lại cho bạn.

Giới hạn 50 lượt lưu kiểu ApplyArc phần lớn là vấn đề về session, chứ không phải vấn đề về pool. Một session trình duyệt thực tế, được xoay vòng một cách hợp lý, sẽ duy trì lâu hơn nhiều trước khi kích hoạt bộ giới hạn tần suất (rate-limiter) so với một HTTP client thô. Và response mang một ID proxy ẩn danh thay vì một điểm thoát thô, nhờ đó mã nguồn của bạn luôn đơn giản và bạn không phải theo dõi điểm thoát nào đã xử lý request nào.

Lưu ý thứ hai là về những gì KHÔNG có trong đoạn mã. Việc loại bỏ trùng lặp (deduplicating) giữa các trang tuyển dụng (cùng một vị trí data engineer trên LinkedIn, Indeed, và trang tuyển dụng riêng của công ty, với ba tiêu đề hơi khác nhau) là vấn đề của bạn, chứ không phải của lớp thu thập dữ liệu. Chúng tôi đã chứng kiến nhiều đội ngũ đánh giá thấp việc này. Quá trình chuẩn hóa (normalisation) tiêu tốn nhiều thời gian kỹ thuật hơn so với việc fetch dữ liệu, và đó là nơi hầu hết các sản phẩm talent intelligence cạnh tranh với nhau.

Kết quả

Một đội ngũ talent intelligence theo dõi 200 công ty trên ba trang tuyển dụng cần khoảng 50.000 lượt fetch trang mỗi tuần: kết quả tìm kiếm, trang chi tiết công việc, và thỉnh thoảng làm mới trang của công ty. Các con số bạn muốn đạt được với khối lượng công việc đó:

  • Tỷ lệ thành công trên 95% đối với các mục tiêu thuộc nhóm Indeed, trong đó thành công có nghĩa là HTML đã render với dải lương và ngày đăng tuyển được điền đầy đủ.
  • Chi phí cho mỗi công việc dưới $0,004 từ đầu đến cuối, bao gồm cả việc render và lựa chọn điểm thoát.
  • Tần suất làm mới từ 6 đến 12 giờ đối với các vị trí đang tuyển dụng, để các dashboard tín hiệu tuyển dụng của bạn không bị tụt hậu so với thị trường.

Các con số này mang tính chất minh họa, dựa trên báo cáo từ các đội ngũ đang vận hành mô hình kiến trúc phân tách này. Chi phí thực tế của bạn phụ thuộc vào trang tuyển dụng bạn nhắm tới và mức độ quyết liệt của bạn khi lọc các bài đăng mới.

Kết luận chính

Các trang tuyển dụng hiện nay có độ khó gần với ad-tech và bán vé hơn là e-commerce thông thường. Đó là một sự chuyển dịch thực sự, và nó giải thích tại sao các thư viện scraping hoạt động tốt vào năm 2024 lại liên tục vấp phải cùng một rào cản vào năm 2026.

Các đội ngũ vượt qua được giới hạn này không còn coi "scraper" là một đơn vị công việc duy nhất. Họ coi session, điểm thoát (exit), và loại bỏ trùng lặp là ba vấn đề riêng biệt, và họ mua cơ sở hạ tầng cho hai vấn đề đầu tiên để các kỹ sư của họ có thể dành thời gian trong tuần cho vấn đề thứ ba. Dữ liệu tin tuyển dụng rẻ nhất là dữ liệu bạn không phải thu thập lại sau khi bị gắn cờ.