सेट-ऑफ-मार्क प्रांप्टिंग: एक ब्राउज़र एजेंट को कुछ ऐसा दें जिसे वह क्लिक कर सके
Lead Scraping Automation Engineer
TL;DR:
- एक दृष्टि मॉडल ने लाइव कैटलॉग पृष्ठ पर पिक्सेल समन्वय के लिए अनुरोध किया लेकिन हर बार लक्ष्य को चूक गया — एक सत्र में 0 में से 5 परीक्षण, तीन में 0 में से 15 — और यह यादृच्छिक नहीं था: तापमान 0 पर यह हर परीक्षण में लगभग एक ही बिंदु लौटाता था।
- सेट-ऑफ-मार्क प्रॉम्प्टिंग — प्रत्येक क्लिक्स करने योग्य तत्व को नंबरिंग करना और मॉडल से समन्वय के बजाय एक अनुक्रमांक मांगना — उसी सत्र में 5 में से 5 और कुल मिलाकर 15 में से 15 अंकित किया, उसी पृष्ठ पर उसी मॉडल के साथ।
- मार्किंग स्वतंत्र नहीं है: प्रॉम्प्ट 1,343 से 2,100 टोकन (+56.4%) में बढ़ गया, परीक्षण में मॉडल की दर पर लगभग $0.00021 प्रति निर्णय।
- एक दूरस्थ CDP ब्राउज़र पर,
page.set_viewport_size()लेआउट व्यूपोर्ट का आकार नहीं बदलता। तीन स्वतंत्र रन में बारह नए सत्रों में,innerWidthकॉल द्वारा अपरिवर्तित रहा और वास्तविक विंडो का आकार 800 px से 3,840 px तक था — कभी भी अनुरोधित आकार नहीं — जबकि स्क्रीनशॉट हमेशा बिल्कुल उसी आकार में था जो मांगा गया था। - यही मूल कारण है: छवि जिसके बारे में मॉडल तर्क करता है और जगह जहां आपका क्लिक पड़ता है विभिन्न समन्वय फ़्रेम हैं, और स्थानांतरण प्रति सत्र बदलता है। DOM के माध्यम से हल किया गया एक अनुक्रमांक इससे सभी तरह से प्रभावित नहीं होता है।
- Scrapeless मुफ्त योजना इस गाइड में क्लाउड-ब्राउज़र चलाती है।
एक ब्राउज़र एजेंट को "दूसरी किताब खोलो" को किसी विशेष स्थान पर क्लिक में बदलना होता है। वह परिवर्तन — ग्राउंडिंग — वह जगह है जहां अधिकांश एजेंट रन चुपचाप गलत हो जाते हैं। मॉडल पृष्ठ को सही ढंग से पढ़ता है, अपनी योजना को सही ढंग से समझाता है, और फिर कहीं ऐसा क्लिक करता है जो वह अभी वर्णित नहीं करता।
usual Fix सेट-ऑफ-मार्क प्रॉम्प्टिंग है: प्रत्येक क्लिक्स करने योग्य तत्व पर एक क्रमांकित बॉक्स बनाएं, मॉडल को तस्वीर और नंबरों की सूची दें, और इसे 60 के बजाय उत्तर देने दें (564, 623)। यह तकनीक माइक्रोसॉफ्ट के सेट-ऑफ-मार्क पेपर से आई है, और अब यह अधिकांश एजेंट स्टैक्स में किसी न किसी रूप में है।
जो चीज़ खोजना कठिन है वह है एक माप। यह गाइड दोनों तरीकों को एक ही लाइव पृष्ठ पर उसी मॉडल के खिलाफ बनाता है, प्रत्येक को एकल सत्र में पांच बार चलाता है, और क्या वास्तव में हुआ उसका रिपोर्ट करता है — दूरस्थ-ब्राउज़र विवरण सहित जो बताता है क्यों समन्वय संस्करण इतनी स्थिरता से विफल होता है।
Why Coordinates Fail
विफल संस्करण से शुरू करें, क्योंकि इसकी विफलता का आकार दिलचस्प हिस्सा है।
कार्य: एक लाइव पुस्तक कैटलॉग पर, Soumission नामक पुस्तक के लिए उत्पाद पृष्ठ खोलें। मॉडल एक स्क्रीनशॉट प्राप्त करता है और एक क्लिक बिंदु के लिए पूछा जाता है।
python
import base64, json, os, urllib.request
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
TARGET = "https://books.toscrape.com/"
TASK = "open the product page for the book titled 'Soumission'"
MODEL = "google/gemini-2.5-flash-lite"
CDP = "wss://browser.scrapeless.com/api/v2/browser?" + urlencode({
"token": os.environ["SCRAPELESS_API_KEY"], "sessionTTL": 300, "proxyCountry": "US"})
def ask(png, prompt):
body = {"model": MODEL, "max_tokens": 200, "temperature": 0,
"messages": [{"role": "user", "content": [
{"type": "text", "text": prompt},
{"type": "image_url", "image_url": {
"url": "data:image/png;base64," + base64.b64encode(png).decode()}}]}]}
req = urllib.request.Request(
"https://openrouter.ai/api/v1/chat/completions", data=json.dumps(body).encode(),
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}",
"Content-Type": "application/json"})
d = json.load(urllib.request.urlopen(req, timeout=120))
return d["choices"][0]["message"]["content"].strip(), d["usage"]
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(CDP)
page = browser.new_page()
page.set_viewport_size({"width": 1280, "height": 1400})
page.goto(TARGET, wait_until="domcontentloaded")
answer, usage = ask(page.screenshot(),
f"This is a 1280x1400 browser screenshot. To {TASK}, give the pixel coordinates "
f'of the element to click. Reply with JSON only: {{"x":int,"y":int}}')
print("model answered:", answer)
c = json.loads(answer[answer.index("{"):answer.rindex("}") + 1])
landed = page.evaluate(
"([x,y]) => { const e = document.elementFromPoint(x,y);"
"return e ? (e.closest('a')?.getAttribute('href') || '<'+e.tagName+'>') : null }",
[c["x"], c["y"]])
print(f"click ({c['x']},{c['y']}) lands on -> {landed}")
print("prompt tokens:", usage["prompt_tokens"])
browser.close()
मॉडल मना नहीं करता, बचाव नहीं करता या कोई गलत चीज वापस नहीं करता। यह तुरंत और आत्मविश्वास से उत्तर देता है:
text
model answered: {"x": 564, "y": 623}
click (564,623) lands on -> <BODY>
prompt tokens: 1343
<BODY> का मतलब है कि क्लिक कुछ भी नहीं लैंड हुआ — खाली पृष्ठ पृष्ठभूमि, कोई लिंक नहीं। एजेंट अब रिपोर्ट करेगा कि उसने क्लिक किया, एक नेविगेशन का इंतज़ार करेगा जो कभी नहीं होता, और एक योजना के साथ आगे बढ़ेगा जो चुपचाप नहीं हुई एक कदम पर आधारित है।
इसे एक ही सत्र में पांच बार चलाएं और विफलता औसत नहीं होती:
| परीक्षण | उत्तर | तत्व हिट | परिणाम |
|---|---|---|---|
| 1 | (564, 623) | <BODY> |
MISS |
| 2 | (563, 630) | <BODY> |
MISS |
| 3 | (563, 630) | <BODY> |
MISS |
| 4 | (563, 630) | <BODY> |
MISS |
| 5 | (564, 623) | <BODY> |
MISS |
0 में से 5। एक अस्थिर विफलता जीवित रहने योग्य है — आप तीन बार नमूना लेते हैं, बहुमत लेते हैं, और आगे बढ़ते हैं। यह विफलता स्थिर है। तापमान 0 पर मॉडल लगभग कुछ पिक्सेल के भीतर एक ही बिंदु लौटाता है, और हर पुनरावृत्ति उसी मृत स्थान में लैंड होती है। आत्म-सुसंगतता मतदान तीन बार गलत उत्तर लौटाएगा और इसे आम सहमति कहेगा।
तीन अलग-अलग सत्रों में पूरे तुलना को दोहराने ने हर बार वही फैसला दिया: समन्वय के लिए 0 में से 15। सत्रों के बीच जो बदला वह केवल कैसे यह चूक गया — एक सत्र में हर क्लिक लक्ष्य के बगल में उत्पाद पर उतर गया बजाय खाली पृष्ठभूमि पर। जिस लक्ष्य पर यह हिट हुआ वह सत्र के साथ बदल गया; इसका चूकना नहीं बदला।
The Frame Mismatch Underneath
स्पष्ट व्याख्या यह है कि दृष्टि मॉडल केवल सटीक समन्वय में खराब हैं, जिसे GUI-grounding साहित्य लंबाई में समर्थन करता है। एक दूरस्थ ब्राउज़र पर इसके शीर्ष पर एक दूसरा कारण ढेर में है, और इसके बारे में जानना महत्वपूर्ण है क्योंकि यह भी उन योजनाओं को तोड़ता है जो बिल्कुल मॉडल शामिल नहीं करते।
अभी और बाद में पृष्ठ से पूछें कि उसे कैसा लगता है:
python
import os
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
CDP = "wss://browser.scrapeless.com/api/v2/browser?" + urlencode({
"token": os.environ["SCRAPELESS_API_KEY"], "sessionTTL": 300, "proxyCountry": "US"})
with sync_playwright() as p:
for run in range(1, 5):
browser = p.chromium.connect_over_cdp(CDP)
page = browser.new_page()
before = page.evaluate("() => [innerWidth, innerHeight]")
page.set_viewport_size({"width": 1280, "height": 900})
page.goto("https://books.toscrape.com/", wait_until="domcontentloaded")
after = page.evaluate("() => [innerWidth, innerHeight]")
shot = page.screenshot()
print(f"run{run}: innerWH before={before} after={after} png_bytes={len(shot)}")
browser.close()
चार नए सत्र:
text
run1: innerWH before=[1550, 1050] after=[1550, 1050] png_bytes=196373
run2: innerWH before=[1240, 560] after=[1240, 560] png_bytes=116304
run3: innerWH before=[1680, 1002] after=[1680, 1002] png_bytes=171659
run4: innerWH before=[3840, 1152] after=[3840, 1152] png_bytes=5289
set_viewport_size() का लेआउट व्यूपोर्ट पर कोई प्रभाव नहीं था — innerWidth और innerHeight सभी चार रन में कॉल के पहले और बाद में समान हैं। वास्तविक विंडो के आकार को भी आपने चुना नहीं है। एक ही स्क्रिप्ट के पुनरावृत्त रन 2,560×1,408, 1,920×1,070, 1,440×839, 800×570 और 3,840×1,050 सहित विभिन्न आकार लौटाए; तीन रन में बारह सत्रों में लेआउट व्यूपोर्ट कभी भी अनुरोधित आकार में नहीं था, और चौड़ाई 800 px से 3,840 px तक भिन्न थी।
स्क्रीनशॉट, इस बीच, हर बार मांगी गई सटीक आकार में था। run4 में यह क्या करता है, देखें: एक 3,840 पिक्सल की विंडो के लिए बिछाया गया पृष्ठ, 1,280 पिक्सल की कैनवास में कैद किया गया, एक 5 KB PNG उत्पन्न करता है जबकि अन्य रन 116–196 KB उत्पन्न करते हैं। फसल ज्यादातर खाली लेआउट को पकड़ती है। वह स्क्रीनशॉट वही है जिसके बारे में मॉडल को तर्क करने के लिए कहा गया था।
स्थानीय रूप से लॉन्च किए गए ब्राउज़र के खिलाफ वही स्क्रिप्ट उस तरह से व्यवहार करती है जैसा आप उम्मीद करेंगे, जो विकास में इसे चूकना आसान बनाता है:
text
local innerWH: [1280, 1400]
after set_viewport_size: [1280, 900]
स्थानीय रूप से कॉल काम करता है और फ़्रेम सहमत होते हैं। एक दूरस्थ ब्राउज़र से जुड़ें और कॉल चुपचाप काम करना बंद कर देता है।
तो पृष्ठ 3,840 पिक्सल की विंडो के लिए खुद को बिछाता है, मॉडल को दिया गया PNG 1,280 पिक्सल चौड़ा है, और जो समन्वय मॉडल उस PNG से पढ़ता है वह तब एक DOM के खिलाफ खर्च किया जाता है जो व्यापक ज्यामिति का उपयोग करता है। दोनों फ़्रेम एक ऐसे कारक द्वारा संबंधित हैं जो हर बार जब आप एक सत्र खोलते हैं तो बदलता है — जो ठीक इसी वजह से है कि चूक एक सत्र में एक पड़ोसी उत्पाद पर लैंड करती है और दूसरे में खाली पृष्ठभूमि पर। जब connect_over_cdp पहले से चल रहे ब्राउज़र से जुड़ता है, तो Playwright उस ब्राउज़र का क्लाइंट होता है न कि उसका मालिक, और दृश्यपटल अनुकरण उन संदर्भों की एक विशेषता है जिन्हें यह स्वयं बनाता है — एक अंतर Playwright का संदर्भ दस्तावेज़ स्पष्ट रूप से खींचता है।
मॉडल बेतरतीब तरीके से अनुमान नहीं लगा रहा है। यह छवि को सही ढंग से पढ़ रहा है और छवि के फ़्रेम में उत्तर दे रहा है, और फिर वह संख्या एक अलग में खर्च होती है।
तत्वों को चिह्नित करना इसके बजाय
सेट-ऑफ-मार्क फ़्रेम की समस्या को हटाता है क्योंकि यह इसके पार एक समन्वय को कभी नहीं ले जाता। मॉडल एक अनुक्रमांक लौटाता है; अनुक्रमांक को पृष्ठ द्वारा स्वयं हल किया जाता है, पृष्ठ के अपने ज्यामिति में।
चिह्नन पास इंटरैक्टिव तत्वों को चलाता है, उन पर लागू फ़िल्टर करता है जिन्हें उपयोगकर्ता वास्तव में क्लिक कर सकता है, प्रत्येक पर एक संख्या बॉक्स खींचता है, और एक समानांतर सूची लौटाता है। इसे mark.js के रूप में सहेजें:
javascript
() => {
const sel = 'a[href], button, input, select, textarea, [role="button"], [onclick]';
const out = [];
document.querySelectorAll('#som-layer').forEach(n => n.remove());
const layer = document.createElement('div');
layer.id = 'som-layer';
layer.style.cssText = 'position:fixed;inset:0;pointer-events:none;z-index:2147483647';
document.body.appendChild(layer);
const vw = innerWidth, vh = innerHeight;
let i = 0;
for (const el of document.querySelectorAll(sel)) {
const r = el.getBoundingClientRect();
if (r.width < 8 || r.height < 8) continue;
if (r.bottom < 0 || r.top > vh || r.right < 0 || r.left > vw) continue;
const cs = getComputedStyle(el);
if (cs.visibility === 'hidden' || cs.display === 'none' || cs.opacity === '0') continue;
const box = document.createElement('div');
box.style.cssText = `position:absolute;left:${r.left}px;top:${r.top}px;width:${r.width}px;height:${r.height}px;border:2px solid #E11D48;box-sizing:border-box`;
const tag = document.createElement('div');
tag.textContent = i;
tag.style.cssText = `position:absolute;left:${r.left}px;top:${Math.max(0, r.top - 14)}px;background:#E11D48;color:#fff;font:bold 12px monospace;padding:0 4px;line-height:14px`;
layer.append(box, tag);
out.push({ i, tag: el.tagName.toLowerCase(),
name: (el.innerText || el.getAttribute('aria-label') || el.value || '').trim().slice(0, 60),
x: Math.round(r.left + r.width / 2), y: Math.round(r.top + r.height / 2) });
i++;
}
return out;
}
आकार और दृश्यता फ़िल्टर जितने दिखते हैं, उससे ज्यादा महत्वपूर्ण हैं। बिना width < 8 || height < 8 परीक्षण के, आप ट्रैकिंग पिक्सेल और संकुचित रैपर को चिह्नित करते हैं; दृश्यपटल के सीमाओं के परीक्षण के बिना, आप एक लंबे पृष्ठ के पूरे फ़ुटर को चिह्नित करते हैं और मॉडल को चीजों के लिए नंबर देते हैं जिन्हें वह नहीं देख सकता। दोनों मेनू उत्पन्न करते हैं जहां अनुक्रमांक और चित्र असहमत होते हैं, जो सभी चिह्नों की अनुपस्थिति से भी बदतर है।
ओवरले एक position:fixed परत है जिसमें pointer-events:none है, इसलिए यह उस पृष्ठ के ऊपर चित्रित करता है जिस पर आप क्लिक करने जा रहे हैं और इसके नीचे कुछ भी फिर से प्रवाहित नहीं करता है।
हर तत्व का केंद्र getBoundingClientRect() से चिह्नन समय पर पृष्ठ के समन्वय प्रणाली में कैद किया जाता है। यही संख्या है जिसका उपयोग क्लिक करेगा, और यह कभी भी छवि के माध्यम से राउंड ट्रिप नहीं करता है।
पूरा लूप
चिह्नन, स्क्रीनशॉट लेना, अनुक्रमांक पूछना, और उस पर क्लिक करना:
python
import base64, json, os, pathlib, urllib.request
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
TARGET = "https://books.toscrape.com/"
TASK = "open the product page for the book titled 'Soumission'"
MODEL = "google/gemini-2.5-flash-lite"
MARK_JS = pathlib.Path("mark.js").read_text()
CDP = "wss://browser.scrapeless.com/api/v2/browser?" + urlencode({
"token": os.environ["SCRAPELESS_API_KEY"], "sessionTTL": 300, "proxyCountry": "US"})
def ask(png, prompt):
body = {"model": MODEL, "max_tokens": 200, "temperature": 0,
"messages": [{"role": "user", "content": [
{"type": "text", "text": prompt},
{"type": "image_url", "image_url": {
"url": "data:image/png;base64," + base64.b64encode(png).decode()}}]}]}
req = urllib.request.Request(
"https://openrouter.ai/api/v1/chat/completions", data=json.dumps(body).encode(),
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}",
"Content-Type": "application/json"})
d = json.load(urllib.request.urlopen(req, timeout=120))
return d["choices"][0]["message"]["content"].strip(), d["usage"]
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(CDP)
page = browser.new_page()
page.goto(TARGET, wait_until="domcontentloaded")
marks = page.evaluate(MARK_JS)
shot = page.screenshot()
page.evaluate("() => document.querySelectorAll('#som-layer').forEach(n => n.remove())")
print(f"marked {len(marks)} interactive elements")
menu = "\n".join(f"[{m['i']}] <{m['tag']}> {m['name']}" for m in marks)
answer, usage = ask(shot,
f"The screenshot has numbered red marks on every clickable element.\n{menu}\n\n"
f"To {TASK}, which mark do you click? Reply with JSON only: "
'{"index":int}. Do not explain.')
print("model answered:", answer)
idx = json.loads(answer[answer.index("{"):answer.rindex("}") + 1])["index"]
chosen = next(m for m in marks if m["i"] == idx)
print(f"chose [{idx}] {chosen['name']!r}")
with page.expect_navigation(wait_until="domcontentloaded"):
page.mouse.click(chosen["x"], chosen["y"])
print("landed on:", page.url)
print("prompt tokens:", usage["prompt_tokens"])
browser.close()
आउटपुट:
text
marked 85 interactive elements
model answered: {"index": 60}
chose [60] 'Soumission'
landed on: https://books.toscrape.com/catalogue/soumission_998/index.html
prompt tokens: 2100
पांच परीक्षण, एक ही सत्र, एक ही मॉडल, एक ही तापमान:
| परीक्षण | अनुक्रमांक | लेबल | लैंड किया गया | परिणाम |
|---|---|---|---|---|
| 1 | 60 | प्रस्तुतिकरण | soumission_998 |
हिट |
| 2 | 60 | प्रस्तुतिकरण | soumission_998 |
हिट |
| 3 | 60 | प्रस्तुतिकरण | soumission_998 |
हिट |
| 4 | 60 | प्रस्तुतिकरण | soumission_998 |
हिट |
| 5 | 60 | प्रस्तुतिकरण | soumission_998 |
हिट |
5 में से 5, उस सत्र में जहां समन्वय ने 0 में से 5 प्राप्त किए, और सभी तीन सत्रों में 15 में से 15।
निर्धारण अब दूसरी तरफ कट जाता है। वही गुण जो समन्वय विफलता को असंगत बनाता है — तापमान 0 पर एक स्थिर उत्तर — चिह्नित संस्करण को भरोसेमंदी से सही बनाता है बजाय कि विश्वसनीयता से गलत।
सत्रों के बीच संख्याओं की अपेक्षा करें। क्योंकि लेआउट दृश्यपटल जो कुछ भी दूरस्थ विंडो हो सकता है, उसी स्क्रिप्ट ने एक सत्र में 85 तत्वों को चिह्नित किया और अनुक्रमांक 60 चुना, और दूसरे में 52 तत्वों और अनुक्रमांक 43। दोनों soumission_998 पर उतरे। गणनाएँ सत्र-निर्भर हैं; गंतव्य नहीं है, यही DOM के माध्यम से हल करने का लाभ है।
यह कितनी लागत है
चिह्नन, संकेत में तत्व मेनू जोड़ता है, और मेनू पृष्ठ के साथ बढ़ता है:
| दृष्टिकोण | संकेत टोकन | निर्णय प्रति लागत |
|---|---|---|
| कच्चा स्क्रीनशॉट → समन्वय | 1,343 | $0.00014 |
| सेट-ऑफ-मार्क → अनुक्रमांक | 2,100 | $0.00021 |
| डेल्टा | +757 (+56.4%) | +49.1% |
कच्चे-स्क्रीनशॉट संकेत एक निश्चित 1,343 टोकन है क्योंकि छवि एक निश्चित आकार है; चिह्नित संकेत इस पर निर्भर करता है कि आपने कितने तत्वों को चिह्नित करने का निर्णय लिया। 85 चिह्नित तत्वों के साथ एक पृष्ठ के लिए 56 प्रतिशत अधिक संकेत, एक दर पर जहां एक निर्णय की लागत लगभग एक सेंटी के सौवें का एक हिस्सा है।
यह पैमाना ऊपर दृश्यता फ़िल्टर के लिए असली तर्क है: प्रत्येक तत्व जिसे आप चिह्नित करने से इंकार करते हैं, वह टोकन है जिसे आप खर्च नहीं करते हैं और अनुक्रमांक जिसे मॉडल गलती से नहीं चुन सकता है। 0 से 100% के बीच सही चीज़ क्लिक करने में एजेंट के प्रदर्शन में गिरावट के खिलाफ, यह कोई निकट कॉल नहीं है।
जहां यह अभी भी टूटता है
Set-of-Mark ग्राउंडिंग को ठीक करता है। यह सब कुछ नहीं ठीक करता।
कैनवास और WebGL सतहों में चिह्नित करने के लिए कोई तत्व नहीं होते। एक मानचित्र, एक चार्टिंग कैनवास, या एक खेल पिक्सल में रेंडर होता है जिसके नीचे कोई DOM संरचना नहीं होती। चिह्नित करना कुछ नहीं खोजता और आप फिर से समन्वय में वापस जाते हैं, या फिर उस पहुँचता संरचना के हुक्स में जो घटक उजागर करता है।
चिह्न दृश्यदिशा-बंधित होते हैं। जो कुछ भी नीचे की ओर है, उसे डिज़ाइन द्वारा चिह्नित नहीं किया गया है, इसलिए एक एजेंट को जो तत्व नीचे चाहिए, उसे स्क्रॉल करना और फिर से चिह्नित करना होगा। चिह्नित करने को एक अवलोकन प्रक्रिया के रूप में मानें, एक बार की सेटअप के रूप में नहीं।
घने पृष्ठ लंबे मेनू का उत्पादन करते हैं। 400 नियंत्रणों वाला एक पृष्ठ 400-लाइन मेनू उत्पन्न करता है, और टोकन बिल हर चक्र के चरण में आ जाता है। चिह्नित करने से पहले भूमिका, क्षेत्र या कार्य के निकटता के आधार पर फ़िल्टर करें।
सूचकांक सिर्फ लेबल के उतना ही अच्छा है। एक तत्व जिसका पहुँच योग्य नाम खाली है, वह [31] <a> के रूप में दिखाई देता है और मॉडल के साथ कुछ भी अनुमान लगाने के लिए नहीं होता। यह वही नामकरण समस्या है जिससे स्क्रीन रीडर प्रभावित होते हैं, और समाधान वही है जो द एक्सेसिबल नेम और डिस्क्रिप्शन कंप्यूटेशन पहले से निर्दिष्ट करता है: aria-label को प्राथमिकता दें, शीर्षक या निकट टेक्स्ट पर वापस गिरें।
हर नौवहन के बाद फिर से चिह्नित करना अनिवार्य है। सूचकांक स्थिति संबंधी होते हैं और अगले रेंडर में फिर से सौंपे जाते हैं। पृष्ठ संक्रमण के दौरान एक सूचकांक को बरकरार रखना एक बग है जो एक ग्राउंडिंग विफलता की तरह दिखता है।
क्लाउड ब्राउज़र पर इसे चलाना
ऊपर सब कुछ Scrapeless स्क्रैपिंग ब्राउज़र के खिलाफ CDP पर चलाया गया, यही कारण है कि दृश्यदिशा की खोज सतह पर आई - एक स्थानीय chromium.launch() आपको वह दृश्यदिशा देता है जिसे आपने मांगा और समस्या को छुपाता है जब तक आप डिप्लॉय नहीं करते।
कनेक्ट करना एक वेब्सॉक्ट यूआरएल है:
python
import os
from urllib.parse import urlencode
CDP = "wss://browser.scrapeless.com/api/v2/browser?" + urlencode({
"token": os.environ["SCRAPELESS_API_KEY"],
"sessionTTL": 300,
"proxyCountry": "US",
})
print(CDP.split("?")[0])
proxyCountry निकासी क्षेत्र को पिन करता है, जो किसी भी कैटलॉग के लिए महत्वपूर्ण है जो कीमतों या उपलब्धता को स्थानीयकरण करता है। शेष लूप अपरिवर्तित है - Playwright's API वही है चाहे इसे ब्राउज़र लॉन्च किया हो या एक में संलग्न किया गया हो, क्योंकि दोनों Chrome DevTools प्रोटोकॉल के अंतर्गत बोलते हैं। अगर नीचे का प्रोटोकॉल अपरिचित है, तो CDP प्राइमर परिवहन को कवर करता है, और कंप्यूटर-उपयोग एजेंट लूप उस अवलोकन-निर्णय-क्रिया चक्र को कवर करता है जिसमें यह ग्राउंडिंग चरण स्लॉट होता है।
ऊपर की मापों से दो आदतें निकलती हैं। पृष्ठ से innerWidth और innerHeight पढ़ें न कि आपने जो दृश्यदिशा मांगी है माना - दूरस्थ सत्र में वे अलग-अलग नंबर होते हैं। और हर क्लिक को एक तत्व के माध्यम से हल करें जो पृष्ठ ने आपको दिया, कभी भी एक छवि से गणितीय रूप से तय किए गए समन्वय के माध्यम से नहीं।
निष्कर्ष
माप बेजोड़ है। एक ही पृष्ठ पर, एक ही मॉडल और एक ही प्रॉम्प्ट के साथ, पिक्सेल समन्वय मांगने पर सही तत्व मिला 15 में से 0 बार; सूचकांक मांगने पर यह मिला 15 में से 15 बार। समन्वय विफलता कोई शोर नहीं था जो दोबारा सैंपलिंग से सही हो जाता - तापमान 0 पर इसने हर बार वही बिंदु लौटाया और हर बार एक ही तरह से चूक गया।
मॉडल की ग्राउंडिंग कमजोरी के नीचे एक साधारण बग बैठा है: एक दूरस्थ ब्राउज़र पर स्क्रीनशॉट और DOM एक ही समन्वय प्रणाली में नहीं होते, और set_viewport_size() उन्हें वहां नहीं रखेगा। तत्वों को नंबर देना दोनों समस्याओं से बच जाता है कुछ सैकड़ों टोकनों की कीमत में।
प्रबंधित ब्राउज़र के खिलाफ लूप बनाने के लिए तैयार हैं? Scrapeless के साथ मुफ्त शुरू करें - मुफ्त योजना इस गाइड में हर रन को कवर करती है, और मूल्य निर्धारण वहीं से स्केल करता है।
FAQ
Q: मेरा ब्राउज़र एजेंट सही तत्व पर क्लिक क्यों नहीं करता, जबकि यह सही को संदर्भित करता है?
क्योंकि वर्णन करना और स्थिति जानना अलग-अलग क्षमताएँ हैं। मॉडल पृष्ठ को सही ढंग से पढ़ता है और फिर एक सटीक संख्या को निकालना होता है, और छवि से सटीक संख्याएँ इसकी सबसे कमजोर आउटपुट होती हैं। एक दूरस्थ ब्राउजर जोड़ें, जहाँ स्क्रीनशॉट और DOM विभिन्न समन्वय फ्रेम का उपयोग करते हैं, और त्रुटि कभी-कभी नहीं रह जाती है और प्रणालीगत हो जाती है - ऊपर के रन में यह 15 प्रयासों में से हर एक पर चूक गया।
Q: सेट-ऑफ-मार्क क्या DOM या पहुँचता वृक्ष भेजने से बेहतर है?
वे विभिन्न आधे हल करते हैं। एक पाठ प्रतिनिधित्व मॉडल को बताता है कि क्या मौजूद है; अंक उसे बताते हैं कि वे चीजें उस चित्र पर कहां बैठी हैं, जिस पर यह नजर डाल रहा है, और इसे एक टोकन देते हैं जो एक वास्तविक तत्व में वापस हल होता है। अंक भी छोटे रहते हैं — प्रत्येक तत्व के लिए एक अनुक्रमांक और एक संक्षिप्त लेबल — जबकि एक साधारण सूची पृष्ठ पर कच्चा HTML पांच अंकों के टोकन में चला जाता है।
प्रश्न: अंकन से कितने टोकन जुड़े हैं?
परीक्षित पृष्ठ पर, 757 टोकन, प्रॉम्प्ट को 1,343 से 2,100 तक ले जाना — 85 चिह्नित तत्वों के लिए लगभग 56% अधिक। कच्चा-स्क्रीनशॉट प्रॉम्प्ट निश्चित है क्योंकि चित्र का आकार निश्चित है; चिह्नित प्रॉम्प्ट चिह्नों की संख्या के साथ स्केल करता है, इसलिए दृश्यता और आकार फ़िल्टर लागत नियंत्रण के रूप में काम कर रहे हैं जैसे कि सटीकता नियंत्रण।
प्रश्न: क्या यह एक रिमोट या क्लाउड ब्राउज़र पर काम करता है?
यहां यह बेहतर काम करता है, और यह वहां अधिक आवश्यक है। क्योंकि अनुक्रमांक getBoundingClientRect() के माध्यम से पृष्ठ के अंदर हल होता है, यह स्क्रीनशॉट आकार और रिमोट विंडो के वास्तविक लेआउट दृश्य पोर्ट के बीच असामंजस्य से अप्रभावित है — असामंजस्य जो हर बार समन्वय दृष्टिकोण को विफल कर देता है।
प्रश्न: क्या मुझे हर क्रिया के बाद फिर से अंकन करने की आवश्यकता है?
हाँ। अनुक्रमांक वर्तमान में दृश्य तत्वों पर दस्तावेज़ क्रम में असाइन किए जाते हैं, इसलिए उन्हें किसी भी नेविगेशन, स्क्रॉल, या DOM अद्यतन के बाद फिर से असाइन किया जाता है। प्रत्येक अवलोकन चरण का हिस्सा के रूप में अंकित करें; पिछले स्क्रीनशॉट से अनुक्रमांक का पुन: उपयोग करना इस तकनीक को गलत तरीके से लागू करने का सबसे आम तरीका है।
प्रश्न: उन पृष्ठों पर क्या होता है जिनमें अंकित करने के लिए कोई क्लिक करने योग्य तत्व नहीं हैं?
कैनवास, WebGL, और वीडियो सतह बिना किसी DOM संरचना के बिना रेंडर करते हैं, इसलिए अंकन कुछ उपयोगी नहीं लौटाता है। उन पर एक अलग रणनीति की आवश्यकता है - पहुंच कार्य जो घटक उन्हें प्रदान करते हैं, या एक समन्वय दृष्टिकोण जिसमें फ्रेम असामंजस्य को स्पष्ट रूप से सही किया गया है।
स्क्रैपलेस में, हम केवल सार्वजनिक रूप से उपलब्ध डेटा का उपयोग करते हैं, जबकि लागू कानूनों, विनियमों और वेबसाइट गोपनीयता नीतियों का सख्ती से अनुपालन करते हैं। इस ब्लॉग में सामग्री केवल प्रदर्शन उद्देश्यों के लिए है और इसमें कोई अवैध या उल्लंघन करने वाली गतिविधियों को शामिल नहीं किया गया है। हम इस ब्लॉग या तृतीय-पक्ष लिंक से जानकारी के उपयोग के लिए सभी देयता को कोई गारंटी नहीं देते हैं और सभी देयता का खुलासा करते हैं। किसी भी स्क्रैपिंग गतिविधियों में संलग्न होने से पहले, अपने कानूनी सलाहकार से परामर्श करें और लक्ष्य वेबसाइट की सेवा की शर्तों की समीक्षा करें या आवश्यक अनुमतियाँ प्राप्त करें।



