نتایج گوگل
صفحهبندی
صفحهبندی کرسری (keyset) در سرویس نتایج گوگل؛ معنای total و next_cursor، چرا کرسر را نباید ساخت یا تفسیر کرد، و محدودهٔ limit.
همهٔ اندپوینتهای فهرست در این سرویس به شکل یکسان صفحهبندی میشوند: هر پاسخ تعداد محدودی ردیف دارد، بهعلاوهٔ یک کرسر (cursor) برای صفحهٔ بعد.
شکل پاسخ
{"ok": true, "rows": [...], "total": 412, "next_cursor": "eyJpZCI6NDl9.8f2c…"}
| فیلد | یعنی چه |
|---|---|
rows | ردیفهای همین صفحه |
total | تعداد همهٔ ردیفهای پیدا شده، نه اندازهٔ صفحه |
next_cursor | فقط وقتی صفحهٔ دیگری ممکن باشد |
total اندازهٔ صفحه نیست. اگر rows شما ۵۰ تا دارد ولی total ۴۱۲ است، یعنی ۴۱۲
ردیف منطبق وجود دارد و شما ۵۰ تای اول را میبینید. برای نوار پیشرفت از total استفاده
کنید، نه از طول rows.قاعدهٔ next_cursor
next_cursor فقط وقتی هست که صفحهٔ دیگری وجود داشته باشد — و در دو حالت نیست:
| حالت | چرا |
|---|---|
| صفحهٔ آخر | چیزی باقی نمانده |
| صفحهٔ ناقص | آنقدر ردیف نبود که صفحهٔ دیگری لازم باشد |
«نبودن» را یعنی «تمام شد» بخوانید، نه خطا. پایان پیمایش را نبودن
next_cursor اعلام میکند، نه پاسخ خالی. حلقهٔ درست تا وقتی next_cursor هست ادامه میدهد و وقتی نیست تمام میشود.استفاده از کرسر
کرسر را همانطور که گرفتید در درخواست بعدی بفرستید:
NEXT=$(curl -s -H "X-API-Key: $KEY" \
"https://serp.rasaad.ir/api/v1/operator/results?domain=example.ir&keyword=shoes" \
| jq -r '.next_cursor')
curl -s -H "X-API-Key: $KEY" \
"https://serp.rasaad.ir/api/v1/operator/results?domain=example.ir&keyword=shoes&cursor=$NEXT"
کرسر مبهم و امضاشده است. آن را تفسیر نکنید، از درونش چیزی بیرون نکشید و خودتان
نسازید. یک کرسر جعلی، منقضی یا خراب
400 میگیرد — هرگز بیصدا به صفحهٔ اول برنمیگردد. اگر کد شما 400 را با «شروع از اول» مدیریت میکند، درست کار میکند؛ اگر آن را نادیده میگیرد، بینهایت حلقه میزنید.چرا keyset و نه offset
صفحهبندی از نوع کرسری (keyset) است، نه مبتنی بر offset.
اگر وسط پیمایش ردیفی اضافه یا حذف شود:
| روش | چه اتفاقی میافتد |
|---|---|
OFFSET | ممکن است ردیفی را رد کنید یا تکرار کنید |
| کرسر | ردیفی را جا نمیاندازد و تکرار نمیکند |
اگر دادهها مرتب اضافه و کم میشوند، این تفاوت واقعی است. با
OFFSET در صفحهٔ پنجم ممکن است ردیفی را که در صفحهٔ چهارم دیدهاید دوباره ببینید، چون ترتیب فهرست عوض شده. کرسر این مشکل را ندارد.limit
| مسیر | پیشفرض | بازه |
|---|---|---|
| همهٔ اندپوینتهای فهرست | 50 | 1 تا 200 |
GET /targets/{id}/history | 200 | 1 تا 1000 |
تاریخچهٔ هدف سقف بزرگتری دارد چون معمولاً برای رسم نمودار بلندمدت خوانده میشود، نه برای فهرست تعاملی.
سقف بزرگتر را برای کار سنگین استفاده کنید، نه برای دور زدن صفحهبندی. اگر یک تاریخچهٔ بزرگ را با چند درخواست
limit=1000 میخوانید، دارید محدودیت نرخ را مصرف میکنید. برای نمودار بلندمدت، یکبار کامل بخوانید و خودتان نگه دارید.الگوی کامل پیمایش
def paginate(session, url, key, limit=200):
"""همهٔ ردیفها را با دنبال کردن next_cursor میخواند."""
cursor = None
while True:
params = {"limit": limit}
if cursor:
params["cursor"] = cursor
r = session.get(url, headers={"X-API-Key": key}, params=params)
r.raise_for_status()
body = r.json()
yield from body["rows"]
cursor = body.get("next_cursor")
if not cursor:
return
سه نکته که این الگو رعایت میکند:
| نکته | چرا |
|---|---|
body.get("next_cursor") | نبودن کلید هم حالت «تمام شد» است |
raise_for_status() | 400 یعنی کرسر خراب است، نه پایان فهرست |
429 را مدیریت کنید | اگر 429 گرفتید، صبر کنید و همان صفحه را دوباره بخواهید |
کلید
کرسر را نسازید، تفسیر نکنید، فقط پس بفرستیدnext_cursor را با .get() بخوانید، نه با براکت ([...]). در صفحهٔ آخر کلید وجود ندارد و دسترسی با براکت KeyError میدهد. نبودن کلید، پایان عادی فهرست است.