- Bear Blog đã triển khai hệ thống phân tích riêng hoạt động không cần JavaScript phía client do các ràng buộc về tốc độ, hiệu quả và độ ổn định
- Các script phân tích thông thường có thể bị trình chặn quảng cáo chặn, còn chỉ dựa vào log máy chủ thì crawler, scraper và cả parser dựa trên GPT có thể bị trộn lẫn vào, làm méo số liệu truy cập
- Trong CSS của mỗi trang, khi xảy ra
body:hover, border-image sẽ gọi endpoint /hit/{{ post.id }}/, dùng thao tác hover hoặc cuộn trên di động làm tín hiệu đã đọc
- Máy chủ kiểm tra bot, trình duyệt và nền tảng qua user-agent, còn địa chỉ IP chỉ được dùng để xác định quốc gia rồi băm IP+kèm ngày để loại bỏ lượt đọc trùng
- Nếu cùng một IP đọc bằng nhiều thiết bị trong cùng một ngày thì vẫn chỉ được tính một lần, nhưng vẫn có thể thống kê đơn giản số lượt đọc duy nhất theo từng trang mà không lưu thông tin nhận dạng
Tạo sự kiện đọc bằng CSS hover
- Hệ thống phân tích của Bear Blog tuân theo ràng buộc không dùng JavaScript phía client
- Các công cụ phân tích thông thường có thể phần nào đánh giá tính xác thực của lưu lượng hay bot hay không bằng JavaScript phía client, nhưng nhiều trình chặn quảng cáo không chỉ chặn Google Analytics mà còn chặn cả các script phân tích như Fathom hay Plausible
- Nếu chỉ phân tích log máy chủ thì crawler của công cụ tìm kiếm, scraper và parser dựa trên GPT có thể bị trộn vào như lưu lượng thông thường, tạo ra một góc nhìn bị méo
- Bear chèn CSS sau vào mỗi trang để khi người dùng đưa con trỏ lên trang hoặc cuộn trên thiết bị di động thì
body:hover sẽ xảy ra
body:hover {
border-image: url("/hit/{{ post.id }}/?ref={{ request.META.HTTP_REFERER }}");
}
- Khi
body:hover được kích hoạt, URL hit của bài viết đó sẽ được gọi
- Thông tin được thêm lại một cách tường minh trong yêu cầu này là referrer; cách viết
HTTP_REFERER là một lỗi chính tả được giữ lại như thể đã thành chuẩn
- Dựa trên điểm rằng bot không hover, Bear dùng lời gọi dựa trên
body:hover làm tín hiệu của độc giả là con người
- Sau đó trên máy chủ, hệ thống kiểm tra user-agent có phải bot hay không, rồi trích xuất trình duyệt và nền tảng từ chuỗi user-agent
Loại bỏ lượt đọc trùng mà không dùng thông tin nhận dạng
- Ràng buộc thứ hai là không lưu thông tin có thể nhận dạng độc giả trong cookie trình duyệt hay trên máy chủ
- Địa chỉ IP chỉ được dùng để xác định quốc gia, và trước khi lưu sẽ băm cùng với ngày
- Các yêu cầu tiếp theo cho cùng một trang sẽ được so sánh với hash
địa chỉ IP + ngày, nếu trùng thì bị loại
- Với cách này, trong một ngày một địa chỉ IP chỉ được tính là một read cho một trang
- Bản gốc của địa chỉ IP không được lưu, và hash có kèm ngày tạo ra hiệu ứng hết hạn theo đơn vị ngày
user_agent = httpagentparser.detect(self.request.META.get('HTTP_USER_AGENT', None))
if user_agent.get('bot', False):
print('Bot traffic')
return
ip_hash = hashlib.md5(f"{client_ip(self.request)}-{timezone.now().date()}".encode('utf-8')).hexdigest()
country = get_user_location(client_ip(self.request)).get('country_name', '')
device = user_agent.get('platform', {}).get('name', '')
browser = user_agent.get('browser', {}).get('name', '')
referrer = self.request.GET.get('ref', '')
if referrer:
referrer = urlparse(referrer)
referrer = '{uri.scheme}://{uri.netloc}/'.format(uri=referrer)
Hit.objects.get_or_create(
post_id=self.pk,
ip_address=ip_hash,
referrer=referrer,
country=country,
device=device,
browser=browser)
- Hash IP chỉ được dùng để chặn hit trùng trong một ngày, còn mọi lượt xem trang về cơ bản đều được xử lý là duy nhất
- Vào cuối mỗi ngày, một tác vụ nền sẽ xóa hash khỏi log hit để tránh xung đột với cách diễn giải GDPR quá nghiêm ngặt
- Một nhược điểm của cách này là nếu cùng một địa chỉ IP đọc bằng nhiều thiết bị trong cùng một ngày thì vẫn chỉ được tính là một read
- Bear cho rằng những trường hợp như vậy chỉ chiếm một phần nhỏ của lưu lượng, và cách làm dựa trên CSS này mang lại số lượt đọc chính xác hơn theo cách gọn gàng, đơn giản hơn so với nhiều phương thức thu thập phân tích khác
1 bình luận
Ý kiến trên Hacker News
Tôi là tác giả. Hash địa chỉ IP trong ngữ cảnh này chỉ dùng để ngăn lượt xem trùng lặp trong cùng một ngày
Mục đích là để mặc định mỗi lượt xem trang là duy nhất, và vào cuối mỗi ngày một tác vụ worker sẽ xóa hash trong khi vẫn giữ lại thông tin lượt xem. Tôi đã thêm phần chỉnh sửa để làm rõ điều này trong bài
Lần đầu thấy ý tưởng dùng các request phát sinh từ CSS cho phân tích, tôi đã thấy nó cực kỳ ngầu
Có người trên Twitter từng phủ một lưới hình chữ nhật vô hình lên trang, và khi hover vào từng ô thì sẽ tải một ảnh nền riêng, dùng việc đó để theo dõi chuột. Mỗi ảnh nền sẽ gửi một request cụ thể tới server, rồi server diễn giải nó
Cho vui, có một mùa hè tôi đã mở rộng ý tưởng này để tạo ra một “web chat bất đồng bộ chỉ dùng CSS”, không cần JavaScript: https://github.com/kkuchta/css-only-chat
Việc chỉ hash ngày và IP để ẩn danh địa chỉ IP thực chất chỉ là kịch diễn bảo mật
Hash mật mã được thiết kế để tính rất nhanh. Với hashcat, có thể tính 6 tỷ hash MD5 mỗi giây trên MacBook M1 Pro, trong khi IPv4 chỉ có 4 tỷ địa chỉ. Có thể brute-force toàn bộ không gian để tìm ra địa chỉ IP, nên về thực tế cũng gần như là đảo ngược hash
Dù dùng hash an toàn như SHA-256 thay vì MD5 đã bị phá, thì chuyện này vẫn vậy
Kể cả khi bạn hash họ tên đầy đủ của một người, sau đó vẫn có thể trả lời câu hỏi “hash này có khớp với họ tên đầy đủ cụ thể kia không”. Có thể trả lời được câu hỏi đó nghĩa là quá trình ẩn danh có thể bị đảo ngược
Hash địa chỉ IP trong ngữ cảnh này chỉ dùng để ngăn lượt xem trùng lặp trong ngày. Mục đích là để mặc định mỗi lượt xem trang là duy nhất, và vào cuối mỗi ngày tác vụ worker sẽ xóa các hash IP không còn cần thiết nữa
[0] https://news.ycombinator.com/item?id=37596757
Việc lưu salt vĩnh viễn hay xoay vòng định kỳ chỉ là chi tiết triển khai; điểm cốt lõi khi thêm salt vào hash dùng cho phân tích là salt không bao giờ rời khỏi phía client
Theo mô tả trong bài thì có vẻ không có salt. Hoặc có thể họ đang dùng ngày hiện tại như một dạng salt, nhưng vì đó không phải salt ngẫu nhiên nên bất kỳ ai muốn biết “IP x.y.z.w có truy cập vào ngày yy-mm-dd hay không?” đều có thể dễ dàng đoán được
Từ góc nhìn của kẻ tấn công, việc đánh giá kiểu này khá đơn giản. Nếu có dữ liệu trong tay, làm sao để moi ra điều gì đó về một người cụ thể? Nếu không làm được, thì khả năng cao là dữ liệu đó tương đối an toàn để lưu
Tuy nhiên tôi lo rằng chỉ riêng kiểu kịch diễn bảo mật này cũng có thể đủ để vượt qua nhiều luật lệ và quy định liên quan đến quyền riêng tư
Trông có vẻ khéo, nhưng
body:hovernhiều khả năng sẽ gần như bỏ sót hoàn toàn người dùng chỉ dùng bàn phím và các user agent không dùng thiết bị trỏ, tức người dùng công nghệ hỗ trợDù những nhóm này có thể là thiểu số, việc thấy họ bị loại trừ theo bất kỳ cách nào cũng luôn là một tín hiệu rất xấu
Tôi không biết có cách nào chỉ với CSS cơ bản mà có thể phát hiện 100% đáng tin cậy trên mọi user agent rằng “một người thật sự đang đọc bài này” và gửi request HTTP hay không, và tôi cũng khá nghi ngờ điều đó. Một số có thể hoàn toàn không hỗ trợ CSS, hoặc đã tắt việc tải ảnh trang trí từ CSS
Một selector hiện đại có thể hữu ích là
:root:focus-within, nhưng người dùng vẫn phải thực sự đưa focus vào một phần tử tương tác, và không phải mọi user agent đều đảm bảo điều đó. Kể cả@scroll-timeline, loại animation gắn với cuộn hiện đại nhất, cũng có thể làm được, nhưng người dùng trình đọc chữ nổi vẫn nhiều khả năng bị bỏ sót:hover, hay sao? Tất nhiên là trong trường hợp không cắm chuộtVùng này là chiều rộng bao gồm cột nội dung và phần đệm 20px ở hai bên. Vì thế một số người dùng bàn phím sẽ được tính, còn một số người dùng chuột, đặc biệt là người dùng viewport lớn, lại không được tính
Việc “không chỉ những thứ tệ như Google Analytics mà cả Fathom và Plausible cũng gặp khó trong việc ghi nhận hoạt động trên các trình duyệt có chặn quảng cáo” là vì, theo tôi, họ đang cố tồn tại trong một vùng hoang địa độc hại về bản chất
Những người dùng như chúng tôi đã chán ngấy toàn bộ khái niệm đó, nên nếu phân tích bằng CSS trở nên phổ biến thì tôi nghĩ cũng sẽ xuất hiện các cách để lách hoặc chặn nó
Tôi đã tự tay bỏ chặn Piwik/Matomo, Plausible và Fathom trong uBlock. Tôi không thấy những gì họ theo dõi và cách họ theo dõi là có hại. Và chúng cung cấp thông tin hữu ích cho chủ site để “cải thiện dịch vụ”
Ví dụ, Plausible thu thập ít thông tin về tôi hơn cả log nginx hay Apache thông thường. Từ góc nhìn của một blogger, việc biết bài viết có lên HN không, có được link ở đâu đó không, nội dung nào được thấy là có giá trị và nội dung nào bị lờ đi là rất quan trọng. Nhờ vậy họ có thể viết thứ mà mọi người thực sự muốn đọc, và phân phối nó qua những kênh mà họ thực sự có thể hiểu được
access.logcủa webserver vào dịch vụ phân tích không ngăn được điều gì cảNgược lại, chỉ nhìn vào user agent thì gần như không thể lọc hết toàn bộ lưu lượng bot, nên các con số có thể bị thổi phồng
Tôi không biết thực sự có bao nhiêu người sợ điều đó. Có lẽ phản ứng sẽ trải từ “Ừ, rợn thật” đến “Vớ vẩn, chỉ là khoa học viễn tưởng thôi”
Đây là thứ đã được biết đến nhiều thập kỷ nay dưới tên pixel tracker
:hovercó thể lọc được các bot không dùng WebDriver đầy đủ, tức là phần lớn botỞ một khía cạnh nào đó, có lẽ tốt hơn là dùng
@importvớisupportsđể gọi một file.cssgần như rỗng. Trình chặn quảng cáo bắt pixel theo dõi trong suốt 1px rất giỏi, nhưng có thể ít chặn file.csshơn vì không muốn làm vỡ layout. Tuy vậy, như thế thì sẽ mất đi lợi thế thông minh của:hoverĐây là một câu hỏi thực sự tò mò, nhưng tôi lo nó sẽ nghe như kiểu bác bỏ ý kiến: mục tiêu của việc thu thập dữ liệu phân tích trên một blog cá nhân phi thương mại như Bearblog là gì?
Lý do chính để quan tâm tới phân tích là để xem bài viết có được đọc hay không. Bề ngoài, và ở một mức nào đó, đó là vì hư vinh, nhưng thực ra nó liên quan tới mối kết nối giữa người viết và độc giả. Tôi thật sự tò mò độc giả phản ứng với điều gì, và muốn mang đến nhiều hơn những thứ đó. “Những thứ đó” có thể là chủ đề, giọng điệu hoặc độ dài. Nó giúp tôi gọt giũa chất liệu cho phù hợp với độc giả của mình. Rốt cuộc tôi có thể viết về mười hai chủ đề theo hai mươi bốn cách khác nhau. Dĩ nhiên tôi viết điều mình thích, nhưng vẫn tinh chỉnh để nó cộng hưởng tốt hơn với độc giả
Theo nghĩa này, phân tích cũng là một cách để hiểu độc giả. Với những blog có mức độ tương tác cao, phân tích cho tôi một kiểu chân dung mờ của độc giả. Không chỉ biết họ thích gì mà còn biết họ thích vào lúc nào. Tôi có thể thấy họ đọc ngay buổi sáng, vào giờ nghỉ trưa hay đêm muộn, và điều đó giúp tôi quyết định có nên đăng vào một thời điểm cụ thể hay thêm tự tin vào quyết định đó. Tất nhiên tất cả chỉ là thông tin mơ hồ, nhưng nó thực sự giúp tôi kết nối với độc giả một cách chủ động hơn
Tất nhiên nó có thể bị dùng cho quảng cáo và có thể bị lạm dụng, nhưng nếu tôi muốn có phản hồi về những gì mình làm thì nó là thứ thiết yếu
Dù site có 12 người đọc hay 12.000 người đọc thì có thể cũng không có giá trị tiền bạc gì. Nhưng ở góc độ cá nhân, biết mọi người muốn đọc gì từ tôi là điều tốt, giúp tôi thấy thời gian bỏ ra để viết là xứng đáng, và nếu muốn thì tôi cũng có thể điều chỉnh theo hướng phổ biến hơn
Ngay cả blogger cá nhân cũng có thể muốn điều chỉnh nội dung cho phù hợp với độc giả. Việc biết rằng bài về một chủ đề được 500 người đọc còn bài về chủ đề khác chỉ có 3 người đọc là điều hữu ích
Tôi đã thử làm kiểu này vào đầu năm nay, nhưng rồi mất động lực khi đang làm web UI. Cách của tôi không dùng CSS mà chỉ đơn giản là tải một ảnh giả bằng thẻ
https://github.com/nolytics
Tại sao không lấy thông tin này ngay từ máy chủ HTTP?
“Lựa chọn phân tích log máy chủ luôn tồn tại, và bạn có thể hiểu đại khái loại lưu lượng nào đang truy cập máy chủ. Nhưng nhìn chung mọi lưu lượng máy chủ đều trông giống nhau. Về mặt kỹ thuật, bot phải có user agent tự nhận diện là bot, nhưng vì chúng muốn thu thập thông tin như một ‘con người’ dùng trình duyệt nên hiếm khi tự nhận như vậy. Về bản chất, nếu chỉ dùng log máy chủ cho phân tích thì do có quá nhiều crawler của công cụ tìm kiếm, scraper, và giờ cả các trình phân tích dựa trên GPT, nên bạn sẽ thấy lưu lượng bị méo mó”
Dữ liệu phân tích được lưu như thế nào?
Giả sử có một trang thương mại điện tử và có sản phẩm muốn bán. Ngoài phân tích, cũng quyết định tự ghi log trực tiếp một số hành vi như truy cập trang chi tiết sản phẩm khi đã đăng nhập. Vì vậy muốn lưu những thứ như ID người dùng, ID sản phẩm và dấu thời gian
Trên thực tế nên lưu như thế nào? Ban đầu đã ngây thơ nghĩ rằng cứ đưa vào bảng là được. DBA hỏi cần giữ dữ liệu bao lâu, và đã trả lời là ít nhất một tháng. Sau đó họ nói đã hiểu, và có vẻ như đã lên lịch một tác vụ để chuyển dữ liệu cũ hơn sang bảng khác
Thực tế thì những log như vậy được lưu thế nào, và giữ lại trong bao lâu?
Tôi từng làm như vậy trước đây, và cho đến khi đạt khoảng 1 tỷ dòng thì thậm chí còn chưa cần nghĩ đến partition. Dù vậy, tốt hơn là nên partition sớm hơn thế. Trải nghiệm đó không hề dễ chịu
Có thể thực hiện tổng hợp nhanh hơn rất nhiều, và cũng xử lý tốt nhiều cột chỉ xuất hiện lác đác. Ví dụ, sự kiện
paidcó thuộc tínhamount, còn sự kiệnpage_viewcó thuộc tínhurlĐiểm hay của TimescaleDB là nó tự xử lý việc tạo materialized view cho các tổng hợp mà bạn quan tâm, như số lượt xem sản phẩm theo giờ. Nếu có quá nhiều sự kiện và muốn tránh cơ sở dữ liệu phình to, bạn cũng có thể chọn “bỏ” bản thân các sự kiện đi và chỉ giữ lại phần tổng hợp