Phân trang kết quả tìm kiếm Google với Ngữ cảnh yêu cầu có thể theo dõi
Expert Network Defense Engineer
TL;DR:
- Thay đổi phân trang API tìm kiếm Google làm thay đổi phần đã thu thập. Giữ
startvà toàn bộ yêu cầu với mỗi phản hồi thay vì coi các trang như những lô hàng thay thế cho nhau. - Tiếp tục chỉ trong một kế hoạch đã được định nghĩa và một hợp đồng phản hồi đã được xem xét. Ví dụ kiểm tra liên kết tiếp theo đã được trả về trước khi thay đổi bù đắp và dừng lại trên dữ liệu tiếp diễn không rõ ràng.
- Giữ công việc đang chờ và các quan sát trùng lặp hiển thị. Lưu mỗi phản hồi trang thô; việc loại bỏ trùng lặp thuộc về một cái nhìn đã được tạo ra, và HTTP 201 không phải là một trang trống.
Phân trang có thể ẩn đi một sự thay đổi trong thí nghiệm. Một bộ sưu tập có thể tiến bù đắp trong khi mất cài đặt quốc gia, hoặc kết hợp một phản hồi đang chờ với các trang đã hoàn thành và gọi kết quả là hoàn thành. Danh sách URL kết quả không tiết lộ cách mà những khác biệt đó đã vào bộ sưu tập.
API tìm kiếm Google không bị thu thập hỗ trợ các bù đắp tìm kiếm thông qua start. Hướng dẫn phân trang API tìm kiếm Google này xây dựng một bộ thu thập có giới hạn có bản ghi cho mỗi yêu cầu và lý do để dừng lại. Nó thể hiện luồng điều khiển ứng dụng mà không hứa hẹn quyền truy cập vào mọi kết quả hoặc một chuỗi xếp hạng lịch sử liên tục.
Điều kiện tiên quyết và Kế hoạch Bộ sưu tập Có giới hạn
Chương trình sử dụng thư viện tiêu chuẩn của Python và yêu cầu một mã khóa API tài khoản trong SCRAPELESS_API_KEY để thu thập thực sự. Tên biến môi trường là quy ước của ví dụ này. Giữ mã khóa ngoài các nhật ký thân yêu cầu và các tệp ghi lại được tạo ra.
Các thông số tìm kiếm Google mô tả start như bù đắp kết quả, với các ví dụ là 0, 10 và 20. Chương trình giới hạn kế hoạch của nó vào những bù đắp đó. Đây là một ràng buộc ứng dụng, không phải là một tuyên bố về độ sâu tối đa của dịch vụ hoặc một đảm bảo rằng mỗi trang chứa một số lượng mục hữu cơ cố định.
Câu truy vấn cơ bản là coffee với gl=us, hl=en và đầu vào dành cho máy tính để bàn. Những cài đặt này là một cấu hình yêu cầu minh họa. Chúng không được trình bày như một mẫu tìm kiếm đã quan sát. Thay đổi cấu hình chỉ sau khi quyết định phạm vi của bộ sưu tập riêng của bạn.
Các yêu cầu được xác thực đã không được thực hiện cho bài viết này vì không có mã khóa tài khoản nào được cung cấp. Các bài kiểm tra địa phương thực hiện logic tiếp tục và trạng thái với các phản hồi tổng hợp. Một lần chạy tài khoản trực tiếp vẫn là điều kiện tiên quyết để xác thực hành vi phản hồi hiện tại.
Giữ một Bù đắp Trang Bên Cạnh Mỗi Ghi Nhận Thô
Luồng công việc yêu cầu Tìm kiếm Google gửi scraper.google.search đến POST https://api.scrapeless.com/api/v1/scraper/request với xác thực trong x-api-token. Bộ thu thập ghi lại thân đã gửi riêng biệt với phản hồi và thêm dấu thời gian của khách hàng cùng với định danh chạy của riêng nó.
HTTP 200 chứa dữ liệu tác vụ; HTTP 201 đại diện cho một tác vụ vẫn đang được xử lý. Phản hồi sau phải được lưu trước khi dừng lại. Định danh tác vụ của nó vẫn nằm trong phản hồi thô cho một quy trình hoàn thành được xác minh riêng biệt; ví dụ này không tạo ra một điểm cuối thu hồi tác vụ.
Bìa ghi lại thuộc về ứng dụng. request, response, dấu thời gian và trường lỗi của nó không được coi là lớp bọc API gốc. Giữ chúng tách biệt tuân theo mô hình dữ liệu JSON và bảo tồn chứng cứ cơ bản cho một bộ đếm sau này.
Kiểm tra Chứng cứ Tiếp tục Trước Khi Tiến Lên
Ví dụ phản hồi nhanh chóng bao gồm pagination.next, nhưng URL mẫu được viết tắt. Đừng thực hiện giá trị viết tắt đó. Mã yêu cầu một URL tìm kiếm Google HTTPS thực tế, không bị cắt ngắn trước khi sử dụng bù đắp của nó như một gợi ý tiếp tục.
Ví dụ chỉ chấp nhận các máy chủ Google đã được xem xét và kiểm tra rằng truy vấn, quốc gia và ngôn ngữ trong liên kết trả về khớp với cấu hình cơ bản. Nó bảo tồn tất cả các cài đặt cơ bản đã gửi và chỉ thay đổi start. Một trường quốc gia thiếu trong liên kết tiếp theo sẽ gây ra một dừng xem xét thay vì một suy diễn về những gì dịch vụ đã định.
Những kiểm tra này sử dụng phân tích thành phần URL. Chúng xác định chính sách tiếp tục hẹp của ứng dụng, không phải mọi URL Google hợp lệ. Bộ thu thập không bao giờ theo dõi URL được trả về trực tiếp; nó gửi yêu cầu tiếp theo đến cùng một điểm cuối API với thân dạng tham số đã được xem xét.
Chạy Bộ thu thập Với Các Lý do Dừng Rõ Ràng
Lưu chương trình dưới dạng paginate_search.py. Với khóa của bạn được thiết lập trong môi trường, chạy python3 paginate_search.py trong một thư mục có thể ghi. Kịch bản tạo ra một thư mục đầu ra có tên duy nhất, gửi các yêu cầu theo thứ tự và giữ lại mỗi lần chụp trang cộng với collection-summary.json.
Lưu ý: Thu thập API trực tiếp là một điều kiện tiên quyết và không được thực hiện cho bài viết này. Luồng điều khiển cục bộ của mã đã được kiểm tra bằng cách sử dụng các phản hồi tổng hợp. Kiểm tra đầu ra tài khoản thực tế trước khi dựa vào chính sách liên kết tiếp theo, và xác minh việc thu thập nhiệm vụ một cách riêng biệt nếu một yêu cầu trả về HTTP 201.
python
import json
import os
import uuid
from datetime import datetime, timezone
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.parse import parse_qs, urlsplit
from urllib.request import Request, urlopen
ENDPOINT = 'https://api.scrapeless.com/api/v1/scraper/request'
OFFSETS = (0, 10, 20)
BASE = {'q': 'coffee', 'gl': 'us', 'hl': 'en', 'device': 'desktop'}
def now():
return datetime.now(timezone.utc).isoformat()
def fetch(body, key):
request = Request(ENDPOINT, data=json.dumps(body).encode(), method='POST',
headers={'Content-Type': 'application/json', 'x-api-token': key})
try:
with urlopen(request, timeout=60) as response:
status, raw = response.status, response.read().decode('utf-8')
except HTTPError as error:
status, raw = error.code, error.read().decode('utf-8', errors='replace')
except (URLError, TimeoutError) as error:
return None, None, type(error).__name__
try:
return status, json.loads(raw), None
except json.JSONDecodeError:
return status, {'unparsed_body': raw}, 'response_not_json'
def next_offset(payload, current):
pagination = payload.get('pagination')
link = pagination.get('next') if isinstance(pagination, dict) else None
if not isinstance(link, str) or not link:
return None, 'next_link_unavailable'
try:
parts = urlsplit(link)
if (parts.scheme != 'https' or parts.hostname not in ('google.com', 'www.google.com')
or parts.username or parts.password or parts.port or parts.path != '/search'
or parts.fragment or '...' in link or '…' in link):
return None, 'next_link_needs_review'
query = parse_qs(parts.query, keep_blank_values=True)
if any(query.get(k) != [BASE[k]] for k in ('q', 'gl', 'hl')):
return None, 'next_context_needs_review'
values = query.get('start', [])
if len(values) != 1 or not values[0].isascii() or not values[0].isdigit():
return None, 'next_offset_needs_review'
offset = int(values[0])
if offset <= current or offset not in OFFSETS:
return None, 'next_offset_outside_plan'
return offset, None
except ValueError:
return None, 'next_link_needs_review'
def collect(directory, key):
directory = Path(directory)
directory.mkdir(parents=True, exist_ok=False)
seen, visited, pages = set(), set(), []
offset, stop = 0, None
while offset in OFFSETS and offset not in visited:
visited.add(offset)
body = {'actor': 'scraper.google.search', 'input': dict(BASE, start=offset)}
started = now()
status, payload, error = fetch(body, key)
run_id = uuid.uuid4().hex
capture = {'run_id': run_id, 'requested_at': started, 'received_at': now(),
'request': body, 'http_status': status, 'response': payload, 'error': error}
filename = f'{offset}-{run_id}.json'
(directory / filename).write_text(json.dumps(capture, ensure_ascii=False, indent=2), encoding='utf-8')
page = {'start': offset, 'capture': filename, 'new_exact_urls': None}
pages.append(page)
if status == 201:
stop = 'pending'
break
if status != 200 or error:
stop = 'collection_error'
break
rows = payload.get('organic_results') if isinstance(payload, dict) else None
if not isinstance(rows, list) or any(not isinstance(row, dict) for row in rows):
stop = 'unmapped_organic_results'
break
links = {row['link'] for row in rows if isinstance(row.get('link'), str) and row['link']}
page['new_exact_urls'] = len(links - seen)
seen.update(links)
if not rows:
stop = 'empty_organic_slice'
break
if not links:
stop = 'no_usable_link_strings'
break
if page['new_exact_urls'] == 0:
stop = 'no_new_exact_urls'
break
if len(visited) == len(OFFSETS):
stop = 'planned_page_limit'
break
offset, stop = next_offset(payload, offset)
if stop:
break
summary = {'pages': pages, 'stop_reason': stop, 'unique_exact_url_strings': len(seen)}
(directory / 'collection-summary.json').write_text(json.dumps(summary, indent=2), encoding='utf-8')
return summary
if __name__ == '__main__':
key = os.environ['SCRAPELESS_API_KEY']
output = 'search-pages-' + uuid.uuid4().hex
print(json.dumps(collect(output, key), indent=2))
Thời gian chờ là một cài đặt khách hàng cục bộ, không phải là một tuyên bố hiệu suất dịch vụ. Một ngoại lệ vận chuyển hoặc phản hồi không phải JSON tạo ra một bản ghi lỗi và ngừng thu thập. Chương trình giữ lại các lần chụp trước đó thay vì trình bày một lần chạy phần nào như một lần quét hoàn chỉnh.
Bắt đầu thu thập dữ liệu với Scrapeless
Khởi động quy trình thu thập dữ liệu web và tự động hóa của bạn với Scrapeless!
Đăng ký hôm nay và nhận 5 đô la tín dụng miễn phí — không yêu cầu thẻ tín dụng.Nhận tín dụng miễn phí của bạn ngay bây giờ trong Bảng điều khiển Scrapeless.
Tách biệt loại bỏ trùng lặp khỏi bằng chứng tìm kiếm
Tóm tắt đếm các chuỗi URL chính xác mới trên mỗi trang trong khi giữ lại mọi phản hồi gốc. Các URL lặp lại vẫn nằm trong các lần chụp đó với ngữ cảnh trang của chúng. Điều này cho phép người xem kiểm tra sự chồng chéo thay vì mất nó trong quá trình xuất.
Việc loại bỏ chuỗi chính xác được thực hiện một cách có chủ ý hẹp. Nó không hợp nhất các biến theo dõi, mảnh, URL chuẩn hoặc các trang khác nhau từ một tên miền. Một chính sách nhóm rộng hơn thuộc về một chuyển đổi phiên bản riêng biệt và nên giữ lại các liên kết gốc.
Chương trình ngừng lại khi một trang có thể sử dụng không đóng góp chuỗi URL chính xác mới. Đó là một quyết định ngân sách thu thập, không phải bằng chứng rằng không còn trang liên quan nào khác tồn tại. Tương tự, một mảng hữu cơ trống hiện tại ngừng chế độ chạy này mà không thiết lập một ranh giới tìm kiếm toàn diện.
Diễn giải một tập hợp một phần một cách trung thực
Đọc lý do dừng lại của tóm tắt trước khi sử dụng số đếm URL của nó. planned_page_limit có nghĩa là giới hạn cục bộ đã được đạt được. next_link_unavailable có nghĩa là việc tiếp tục chưa được thiết lập. Các trạng thái hoãn, chưa được ánh xạ và lỗi thu thập mô tả bằng chứng chưa hoàn thành hoặc không thể sử dụng, không phát hiện thành công ở cuối kết quả.
Mỗi yêu cầu có dấu thời gian riêng. Các trang theo thứ tự không được thu thập đồng thời, và chương trình không tuyên bố có một phiên máy chủ chung giữa các cuộc gọi. Bảo tồn cửa sổ thu thập khi so sánh quy trình này với quy trình khác.
Mô hình nguồn gốc giúp tách biệt các quan sát trang thô, hoạt động thu thập và danh sách đã loại bỏ trùng lặp. Ngay cả một cách bố trí hệ thống tệp nhỏ cũng có thể duy trì các mối quan hệ đó nếu tóm tắt chỉ về mọi lần chụp.
Kết luận
Lập kế hoạch thu thập có giới hạn, bảo tồn mọi yêu cầu đã gửi và chỉ tiến bộ khi phản hồi hỗ trợ quy tắc tiếp tục của ứng dụng. Các lý do dừng rõ ràng và trùng lặp đã giữ lại khiến một tập dữ liệu một phần dễ hiểu mà không tuyên bố rằng nó chứa mọi kết quả của Google.
Một cách tiếp cận tập dữ liệu snapshot SERP có thể giúp tổ chức các quan sát đã lưu sau khi thu thập; giữ các sai lệch trang rõ ràng khi quyết định các bản ghi nào là có thể so sánh.
Xây dựng Quan sát Tìm kiếm Tiếp theo của Bạn
Sử dụng Scrapeless Google Search API cho dữ liệu tìm kiếm trong quy trình này. Xem xét giá của Scrapeless khi lập kế hoạch thu thập, và giữ các tham số Tìm kiếm của Google bên cạnh cấu hình của bạn.
Thảo luận về việc triển khai của bạn với cộng đồng trên Discord hoặc Telegram.
Câu hỏi thường gặp
H: Liệu start=10 có đảm bảo mười hàng hữu cơ không?
Không. start chỉ định một sự chênh lệch. Kiểm tra mảng hữu cơ thực tế thay vì suy ra chiều dài của nó từ sự chênh lệch yêu cầu.
H: Kịch bản này có thu thập mọi kết quả Google không?
Không. Nó bị giới hạn bởi một kế hoạch ứng dụng nhỏ và dừng lại khi không có dữ liệu có thể sử dụng hoặc tiếp tục.
H: Điều gì xảy ra với HTTP 201?
Phản hồi được lưu và quá trình thu thập dừng lại dưới dạng chờ xử lý. Một quy trình làm việc hoàn thành nhiệm vụ đã được xác minh là cần thiết trước khi giải thích dữ liệu tìm kiếm cuối cùng.
Q: Tại sao dừng lại khi một liên kết tiếp theo bỏ qua một trường ngữ cảnh?
Ví dụ này yêu cầu sự đồng ý rõ ràng về truy vấn, quốc gia và ngôn ngữ. Thiếu bằng chứng sẽ kích hoạt xem xét thay vì một giả định im lặng về việc tiếp tục.
Q: Có phải các URL lặp lại bị loại bỏ khỏi các bản ghi gốc không?
Không. Chỉ có số lượng chuỗi chính xác trong tóm tắt là bị loại trừ. Các quan sát gốc vẫn có sẵn để kiểm tra.
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.



