Ollama Vision Web Scraping: Đọc một ảnh chụp màn hình bằng một mô hình cục bộ
Senior Cybersecurity Analyst
TL;DR:
- Đây là sự kết hợp giữa mô hình cục bộ và ảnh chụp màn hình, và không có hướng dẫn hiện có nào trên trang này bao gồm nó một cách độc lập. Ollama Web Scraping chạy một mô hình cục bộ trên văn bản HTML được hiển thị, không phải ảnh chụp màn hình; một API tầm nhìn đám mây đọc ảnh chụp màn hình nhưng tính phí theo hình ảnh và cần một khóa. Hướng dẫn này chạy một mô hình có khả năng tầm nhìn cục bộ chống lại một ảnh chụp màn hình từ trình duyệt thực — không cần khóa đám mây, không tính phí theo token và không có HTML nào cả.
- Mọi lần chạy ở đây đều sử dụng Ollama chỉ trên CPU, và
ollama psxác nhận điều đó. Không có khả năng offload GPU cho Ollama trong môi trường này, và thời gian thực hiện thay đổi đáng kể giữa các lần gọi — từ vài giây đến vài phút — tùy thuộc vào việc mô hình có còn ở trong bộ nhớ hay không. - Trường hình ảnh của Ollama không phải là định dạng đám mây. Trường
imagestrong/api/generatecủa Ollama nhận một danh sách các chuỗi base64 thô mà không có tiền tốdata:image/png;base64,— ngược lại với quy ước tương thích với OpenAI mà hầu hết các API tầm nhìn đám mây sử dụng. - Mô hình tầm nhìn dưới 2B không đáng tin cậy ở cả hai đầu của nhiệm vụ, theo những cách khác nhau. Khi được yêu cầu mô tả trang trong một câu, nó đã nêu một chi tiết cấu trúc mà không khớp với trang thực tế. Khi được yêu cầu một bản ghi JSON có cấu trúc — nhiệm vụ chính xác mà chị em tầm nhìn đám mây xử lý một cách rõ ràng — nó đã lặp lại văn bản giữ chỗ của yêu cầu, sau đó trôi dạt vào đầu ra không phải là JSON.
- Ảnh chụp màn hình vẫn đến từ một trình duyệt thực đã được hiển thị. Trình duyệt Scraping của Scrapeless ghi lại trang thông qua CDP theo cách mà một pipeline tầm nhìn đám mây sẽ thực hiện; chỉ có nơi mà các pixel được đọc sau này thì thay đổi.
- Miễn phí để bắt đầu ở phía lấy dữ liệu. Tạo khóa API Scrapeless của bạn tại app.scrapeless.com.
Tại sao nên kết hợp mô hình cục bộ với ảnh chụp màn hình
Hai điều về "AI web scraping" thường đúng riêng biệt, và hiếm khi đúng cùng nhau. Một mô hình cục bộ, chạy qua Ollama, không tốn tiền cho mỗi lần gọi và không gửi gì đến bên thứ ba — nhưng hướng dẫn mô hình cục bộ trên trang này cho đến nay chỉ đọc văn bản HTML đã được hiển thị, mà không phải là pixel. Một mô hình tầm nhìn đọc một ảnh chụp màn hình thay vì DOM — nhưng hướng dẫn mô hình tầm nhìn trên trang này cho đến nay gọi một API đám mây tính phí theo hình ảnh và cần một khóa của nhà cung cấp đám mây. Bài viết này là sự kết hợp mà không ai trong hai cái cover: một mô hình có khả năng tầm nhìn chạy hoàn toàn trên máy bạn, đọc một ảnh chụp màn hình từ trình duyệt thực, mà không có hóa đơn suy diễn đám mây nào trong pipeline.
Hai hướng dẫn hiện có vẫn hữu ích cho những gì mà mỗi cái làm tốt. Ollama Web Scraping là sự lựa chọn đúng khi giá trị bạn cần đã là văn bản trong DOM đã được hiển thị — một mô hình cục bộ nhanh và chính xác trên một đoạn HTML được cắt gọn. Hướng dẫn GPT Vision web scraping của trang này là sự lựa chọn đúng khi giá trị thực sự là hình ảnh và bạn muốn một mô hình có khả năng, có phí để đọc nó. Hướng dẫn này dành cho một trường hợp thứ ba: giá trị là hình ảnh, nhưng bạn muốn không có phụ thuộc vào đám mây và sẵn sàng đánh đổi khả năng để có được điều đó. Sự đánh đổi đó là có thật, và hướng dẫn này báo cáo một cách trung thực thay vì chỉ cho thấy phần hoạt động.
Điều kiện tiên quyết
- Ollama được cài đặt và đang chạy, với một mô hình có khả năng tầm nhìn đã được kéo:
ollama pull moondream. - Python 3.9 hoặc phiên bản mới hơn với
playwrightvàrequests. - Một khóa API Scrapeless từ bảng điều khiển, được xuất dưới dạng
SCRAPELESS_API_KEY. Nhận một cái miễn phí tại app.scrapeless.com. - Không yêu cầu GPU. Mọi lần chạy trong hướng dẫn này đều sử dụng Ollama chỉ trên CPU.
Cài đặt
bash
pip install playwright requests
ollama pull moondream
playwright kết nối đến một trình duyệt từ xa qua CDP trong hướng dẫn này, vì vậy không cần tải xuống Chromium cục bộ. moondream là một mô hình có khả năng tầm nhìn nhỏ gọn — đủ nhỏ để kéo và chạy mà không cần GPU rời rạc, đó là mục đích của hướng dẫn này.
Cấu hình
bash
export SCRAPELESS_API_KEY="your_scrapeless_api_key"
Đó là khóa duy nhất mà pipeline này cần. API cục bộ của Ollama không yêu cầu bất kỳ thông tin xác thực nào.
Chụp ảnh màn hình với trình duyệt Scraping
Ảnh chụp màn hình vẫn phải trung thực, vì vậy nó vẫn đến từ một trình duyệt thực. Kết nối đến Trình duyệt Scraping của Scrapeless qua Giao thức DevTools của Chrome, mở trang và chụp cửa sổ hiển thị đã được hiển thị — bước ghi lại giống như mà chị em tầm nhìn đám mây sử dụng, qua Giao thức DevTools của Chrome:
python
import os
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
def browser_url():
return "wss://browser.scrapeless.com/api/v2/browser?" + urlencode(
{"token": os.environ["SCRAPELESS_API_KEY"], "sessionTTL": 180, "proxyCountry": "US"})
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(browser_url())
page = browser.new_page(viewport={"width": 1280, "height": 900})
page.goto("https://quotes.toscrape.com/", wait_until="networkidle")
png = page.screenshot()
browser.close()
print("screenshot bytes:", len(png))
print("PNG signature ok:", png[:8] == b"\x89PNG\r\n\x1a\n")
text
screenshot bytes: 55462
PNG signature ok: True
Nhận API key của bạn trên gói miễn phí: app.scrapeless.com
Đọc Ảnh Chụp Màn Hình Với Mô Hình Thị Giác Địa Phương
Mã hóa Base64 ảnh PNG và gửi ngay tới điểm cuối /api/generate cục bộ của Ollama. Đây chính là nơi hai trục thực sự gặp nhau: hình ảnh đến từ một trình duyệt được render trên đám mây thật, nhưng mô hình đọc nó không bao giờ rời khỏi máy này. Hình dạng yêu cầu được tài liệu hóa trong tài liệu tham khảo API của Ollama — lưu ý rằng trường images nhận một chuỗi base64 đơn giản, không phải URL data:image/png;base64,...:
python
import base64, os, time
from urllib.parse import urlencode
import requests
from playwright.sync_api import sync_playwright
def browser_url():
return "wss://browser.scrapeless.com/api/v2/browser?" + urlencode(
{"token": os.environ["SCRAPELESS_API_KEY"], "sessionTTL": 180, "proxyCountry": "US"})
t0 = time.time()
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(browser_url())
page = browser.new_page(viewport={"width": 1280, "height": 900})
page.goto("https://quotes.toscrape.com/", wait_until="networkidle")
png = page.screenshot()
browser.close()
capture_s = round(time.time() - t0, 1)
img_b64 = base64.b64encode(png).decode()
t1 = time.time()
resp = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "moondream",
"prompt": "Describe what this webpage shows in one sentence.",
"images": [img_b64],
"stream": False,
"options": {"temperature": 0, "num_predict": 60},
},
timeout=300,
)
resp.raise_for_status()
data = resp.json()
inference_s = round(time.time() - t1, 1)
print("capture seconds:", capture_s)
print("vision inference seconds:", inference_s)
print("description:", data["response"].strip())
text
capture seconds: 11.5
vision inference seconds: 54.7
description: A webpage titled "Quotes to Scrape" displays a list of quotes and their corresponding tags, organized into five sections.
Tiêu đề, sự hiện diện của dấu ngoặc kép, và các thẻ bên dưới mỗi cái đều là các yếu tố thực sự của trang đã render. Cấu trúc cụ thể mà nó thêm vào, "được tổ chức thành năm phần," là không thể xác minh so với trang thực tế: quotes.toscrape.com tạo ra mười khối trích dẫn trên trang chính, mà không có sự nhóm lại thành năm bất cứ điều gì. Mô hình có được chủ đề tổng quát của trang đúng, một trang trích dẫn với các thẻ, và sau đó nêu một chi tiết cấu trúc cụ thể không khớp với những gì thực sự có.
ollama ps trong loại cuộc gọi này báo cáo 100% CPU: không có GPU offload nào có sẵn cho Ollama trong môi trường này. Thời gian cho một cuộc gọi như thế này cũng khác nhau tùy thuộc vào trạng thái mô hình. Điểm cuối /api/generate của Ollama giữ một mô hình nằm trong bộ nhớ trong keep_alive sau mỗi yêu cầu, 5 phút theo mặc định, được tài liệu hóa trong tài liệu tham khảo API của Ollama ở trên; một cuộc gọi trong khi mô hình vẫn còn nằm lại sẽ bỏ qua việc reload nó từ đĩa, và một cuộc gọi lạnh, với không có gì nằm lại, sẽ mất thời gian tải trước khi token đầu tiên trở lại.
Nơi Mà Việc Trích Xuất Cấu Trúc Gặp Vấn Đề
Một mô tả làm việc không giống như việc trích xuất đáng tin cậy. Hãy yêu cầu cùng một mô hình cho nhiệm vụ chính xác mà người anh em thị giác đám mây xử lý trong một lần sạch sẽ — một mảng JSON của các bản ghi trích dẫn — và kết quả chân thành cũng thuộc về hướng dẫn này:
python
import base64, json, os, time
from urllib.parse import urlencode
import requests
from playwright.sync_api import sync_playwright
def browser_url():
return "wss://browser.scrapeless.com/api/v2/browser?" + urlencode(
{"token": os.environ["SCRAPELESS_API_KEY"], "sessionTTL": 180, "proxyCountry": "US"})
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(browser_url())
page = browser.new_page(viewport={"width": 1280, "height": 900})
page.goto("https://quotes.toscrape.com/", wait_until="networkidle")
png = page.screenshot()
browser.close()
img_b64 = base64.b64encode(png).decode()
t1 = time.time()
resp = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "moondream",
"prompt": 'From this screenshot of a quotes page, return JSON '
'{"quotes":[{"author":"...", "text":"..."}]} for the first 3 quotes '
'you can see. Return ONLY JSON.',
"images": [img_b64],
"stream": False,
"format": "json",
"options": {"temperature": 0, "num_predict": 150},
},
timeout=600,
)
resp.raise_for_status()
data = resp.json()
inference_s = round(time.time() - t1, 1)
print("vision inference seconds:", inference_s)
print("raw response:", data["response"][:200])
try:
parsed = json.loads(data["response"])
print("parsed quotes:", len(parsed.get("quotes", [])))
except json.JSONDecodeError as e:
print("JSON parse failed:", e)
text
vision inference seconds: 79.6
raw response: {"quotes":[{"author":"...","text":"..."},{"author":"","text":"","}}] } [0.12, 0.13, 1.0, 0.87] ) ; return [0.12, 0.13, 1.0, 0.87] ; return [0.12, 0.13, 1.0, 0.87] ; return [0.12, 0.13, 1.0, 0.87] ; re
JSON parse failed: Unterminated string starting at: line 1 column 65 (char 64)
Đối tượng đầu tiên lặp lại cú pháp "..." của prompt một cách verbatim, như thể dấu ba chấm trong hướng dẫn là một giá trị để sao chép hơn là một chỗ để điền. Đối tượng thứ hai không bao giờ đóng đúng cách, và phản hồi sau đó drift vào các mảng bốn số lặp lại và các tuyên bố return lạc lõng không có gì liên quan đến trích dẫn, một tác giả, hoặc JSON chút nào — đầu ra mà đọc như đồ thừa của một nhiệm vụ khác hơn là một nỗ lực thất bại trong nhiệm vụ này. Qua mỗi lần chạy trực tiếp trên hướng dẫn này, mẫu nền tảng giữ nguyên ngay cả khi rác cụ thể thay đổi hình dạng: không có văn bản trích dẫn thực sự nào hoặc tên tác giả nào trở lại, và phản hồi chưa bao giờ đóng như JSON hợp lệ. Đó là một kết quả chân thực, không phải là một bản ghi bị cắt ngắn: moondream là một mô hình khoảng 1 tỷ tham số, và hành vi tuân theo schema cho phép một mô hình thị giác đám mây lớn trả về ba bản ghi sạch, đúng thuộc tính trong vài giây không phải là điều mà một mô hình nhỏ như vậy có thể tái tạo. Tăng giới hạn độ dài đầu ra không khắc phục được mẫu; nó chủ yếu cung cấp cho mô hình nhiều không gian hơn để drift.
Hướng dẫn GPT Vision web scraping của trang này cho thấy đầu kia của giao dịch đó. Một mô hình đa phương tiện được lưu trữ, GPT-4o-mini, chạy qua API của OpenAI, biến một ảnh chụp màn hình Playwright thành các trường sản phẩm và giá trị có cấu trúc mà nó thực sự điền vào. So sánh đó, một mô hình trả phí có khả năng hơn nhiều so với một mô hình cục bộ 1 tỷ tham số miễn phí, là một phần của lý do chạy điều này cục bộ.
Thị Giác Địa Phương So Với Thị Giác Đám Mây So Với Văn Bản Địa Phương
Ba hướng dẫn trên trang này bây giờ đề cập đến ba sự đánh đổi khác nhau để lấy dữ liệu có cấu trúc ra khỏi một trang mà không cần bộ chọn viết tay:
| Vị trí mô hình | Đọc | Chi phí mỗi cuộc gọi | Độ chính xác JSON có cấu trúc | |
|---|---|---|---|---|
| Ollama Web Scraping | Địa phương | Văn bản HTML đã render | Miễn phí | Đáng tin cậy trên HTML đã cắt |
| Hướng dẫn web scraping GPT Vision | Đám mây | Ảnh chụp màn hình | Theo hình ảnh, cần khóa đám mây | Một mô hình trả phí lớn hơn nhiều — không được benchmark trong hướng dẫn này |
| Hướng dẫn này | Địa phương | Ảnh chụp màn hình | Miễn phí | Không đáng tin cậy ở kích thước mô hình này |
| Không cái nào trong ba cái là đúng một cách tuyệt đối. Nếu giá trị là văn bản trong DOM, hãy phân tích DOM — một mô hình văn bản cục bộ hoặc một bộ chọn đơn giản thì nhanh hơn và rẻ hơn bất kỳ quy trình nào về thị giác. Nếu giá trị thực sự là hình ảnh và độ chính xác quan trọng, một mô hình thị giác đám mây xứng đáng với chi phí mỗi hình ảnh. Một mô hình thị giác cục bộ nhỏ như thế này phù hợp với một trường hợp hẹp hơn: đọc khám phá hoặc những trường hợp rủi ro thấp mà con người hoặc một cuộc kiểm tra tiếp theo có thể bắt được một chi tiết sai sót thỉnh thoảng, không phải một quy trình mà tin vào đầu ra mà không có sự giám sát. Dựa trên bằng chứng ở trên, điều đó đúng cho cả một mô tả looser trong một câu và một lược đồ đa trường nghiêm ngặt — chế độ thất bại chỉ trông khác biệt ở mỗi cái. |
Kết luận
Kết hợp một mô hình cục bộ với một ảnh chụp màn hình đã thu hẹp một khoảng cách thực sự giữa hai hướng dẫn hiện có của trang web này. Không có gì trong quy trình chạm vào một mô hình đám mây: Trình duyệt Scrapeless Scraping vẫn hiển thị và ghi lại trang theo cách mà một quy trình thị giác đám mây sẽ làm, và Ollama đọc toàn bộ tệp PNG được tạo ra trên máy này. Cái mà sự kết hợp đó thực sự tốt cho, dựa trên bằng chứng ở đây, là hẹp hơn mô hình thị giác đám mây theo cả hai hướng — một mô tả trong một câu đã thêm một chi tiết cấu trúc mà trang không có, và một lược đồ JSON nghiêm ngặt đã phản ánh văn bản giữ chỗ của nó trước khi trôi dạt vào đầu ra mà hoàn toàn không phải là JSON. Sử dụng một mô hình có kích thước này cho các lần đọc khám phá mà bạn dự định kiểm tra, không phải cho một quy trình mà tin tưởng vào kết quả mà không có sự giám sát, và hãy tìm đến hướng dẫn thị giác đám mây khi việc trích xuất cần phải chính xác.
Tạo một tài khoản Scrapeless miễn phí để nhận chìa khóa API, kiểm tra trang sản phẩm Trình duyệt Scraping để biết những gì phiên CDP hỗ trợ và xem lại giá cả Scrapeless trước khi chạy bên trình duyệt ở quy mô lớn.
Tham gia cộng đồng của chúng tôi để so sánh ghi chú với những nhà phát triển khác đang xây dựng các quy trình trích xuất cục bộ: Discord · Telegram.
Câu hỏi thường gặp
Q: Sự khác biệt thực sự giữa hướng dẫn này và hướng dẫn thị giác GPT đám mây là gì?
Nơi mà mô hình chạy và chi phí của nó. Hướng dẫn GPT Vision web scraping của trang web này gửi ảnh chụp màn hình đến một mô hình đa phương tiện được lưu trữ qua API và tính phí theo hình ảnh; hướng dẫn này gửi cùng một loại ảnh chụp màn hình đến một mô hình chạy trên localhost thông qua Ollama, không cần chìa khóa API và không có chi phí theo lần gọi. Cả hai đều đọc pixel, không phải HTML.
Q: Sự khác biệt giữa hướng dẫn này và hướng dẫn trích xuất văn bản Ollama cục bộ là gì?
Những gì mà mô hình đọc. Ollama Web Scraping lấy HTML đã được hiển thị và đưa cho mô hình cục bộ văn bản để phân tích. Hướng dẫn này không bao giờ gửi HTML đến mô hình — nó đưa cho mô hình cục bộ một ảnh chụp màn hình và yêu cầu nó đọc hình ảnh.
Q: Tôi có cần một GPU để chạy mô hình thị giác cục bộ không?
Không, nhưng hãy dự kiến rằng thời gian chờ sẽ thay đổi rất nhiều mà không có nó. Mỗi lần thực hiện trong hướng dẫn này sử dụng moondream chỉ trên CPU, được xác nhận bởi ollama ps báo cáo 100% CPU trong quá trình suy diễn, và các cuộc gọi phía sau hướng dẫn này dao động từ vài giây đến vài phút cho cùng một loại yêu cầu. Ollama giữ mô hình hiện diện trong keep_alive sau mỗi yêu cầu (5 phút theo mặc định), vì vậy một cuộc gọi trong khi nó vẫn đang được tải bỏ qua thời gian tải đĩa mà một cuộc gọi lạnh phải trả — điều đó giải thích hầu hết sự biến động. Một GPU mà Ollama có thể truy cập sẽ cắt giảm mọi trường hợp đáng kể.
Q: Tại sao trường images trong API của Ollama lại trông khác với một yêu cầu thị giác đám mây?
Bởi vì nó sử dụng một quy tắc khác. /api/generate của Ollama nhận images dưới dạng một danh sách các chuỗi base64 thô mà không có tiền tố. Hầu hết các API thị giác đám mây tương thích với OpenAI, bao gồm cả cái trong hướng dẫn GPT Vision web scraping của trang này, mong đợi một URL data:image/png;base64,... đầy đủ bên trong một phần nội dung image_url. Việc chuyển mã giữa hai định dạng mà không điều chỉnh điều này là điều đầu tiên bị hỏng.
Q: Tại sao việc trích xuất có cấu trúc lại trả về các trường trống thay vì một lỗi?
Yêu cầu đó đã thành công — Ollama đã trả về mã 200 với trường response, vì vậy raise_for_status() không bao giờ được kích hoạt. Mô hình đã điền hình dạng JSON mà nó được yêu cầu sản xuất nhưng không làm đầy các giá trị, sau đó tiếp tục lặp lại hình dạng trống đó cho đến khi giới hạn đầu ra cắt nó giữa chuỗi. Đó là một thất bại khả năng mô hình, không phải thất bại HTTP, và đó chính xác là lý do mà mã ở trên kiểm tra json.loads() riêng biệt thay vì giả định rằng 200 có nghĩa là dữ liệu hợp lệ và đầy đủ.
H: Một mô hình thị giác địa phương lớn hơn có thể khắc phục vấn đề trích xuất cấu trúc không?
Có thể, với một chi phí. Một mô hình thị giác địa phương 7B như llava:7b có nhiều khả năng hơn để giữ một sơ đồ JSON và điền nó một cách chính xác, nhưng nó cũng cần nhiều RAM hoặc VRAM có ý nghĩa hơn và chậm hơn cho mỗi token trên cùng một phần cứng. Sự trao đổi mà hướng dẫn này được xây dựng xung quanh — thực sự miễn phí, thực sự địa phương — trở nên khó giữ khi mô hình phát triển; đến một lúc nào đó, chi phí cho mỗi hình ảnh của một mô hình thị giác đám mây trở nên rẻ hơn so với phần cứng mà một mô hình địa phương lớn cần để hoạt động tốt.
H: Tôi có nên sử dụng điều này cho một đường ống thu thập dữ liệu sản xuất không?
Không nên không giám sát, với kích thước mô hình này. Dựa trên bằng chứng ở đây, cả mô tả một câu hay sơ đồ cấu trúc đều không trở về hoàn toàn đáng tin cậy — mô tả đã thêm một chi tiết cấu trúc mà trang không có, và sơ đồ đã lặp lại văn bản giữ chỗ của chính nó trước khi trượt vào đầu ra không phải JSON. Hướng dẫn trích xuất sơ đồ nghiêm ngặt đến hướng dẫn thị giác đám mây hoặc đến phân tích DOM với Ollama Web Scraping thay vào đó, và giữ một mô hình thị giác địa phương như thế này cho các đọc khám phá mà không mất chi phí, nơi một người vẫn kiểm tra kết quả, hoặc di chuyển đến một mô hình địa phương lớn hơn nếu nó phải giữ lại địa phương.
H: Đọc một ảnh chụp màn hình bằng mô hình địa phương có hợp pháp không?
Chạy mô hình cục bộ không thay đổi quy tắc thu thập cho chính trang. Chỉ chụp các trang công cộng, tôn trọng các điều khoản của trang và các chỉ thị robot được chuẩn hóa bởi Giao thức loại trừ robot, và giữ thể tích yêu cầu hạn chế bất kể mô hình đọc kết quả chạy ở đâu.
Tại Scrapless, chúng tôi chỉ truy cập dữ liệu có sẵn công khai trong khi tuân thủ nghiêm ngặt các luật, quy định và chính sách bảo mật trang web hiện hành. Nội dung trong blog này chỉ nhằm mục đích trình diễn và không liên quan đến bất kỳ hoạt động bất hợp pháp hoặc vi phạm nào. Chúng tôi không đảm bảo và từ chối mọi trách nhiệm đối với việc sử dụng thông tin từ blog này hoặc các liên kết của bên thứ ba. Trước khi tham gia vào bất kỳ hoạt động cạo nào, hãy tham khảo ý kiến cố vấn pháp lý của bạn và xem xét các điều khoản dịch vụ của trang web mục tiêu hoặc có được các quyền cần thiết.



