如何在抓取时处理分页:可靠完成

如何在抓取时处理分页

Scrapeless Agent Browser 提供可在云端运行的浏览器会话,能够导航分页页面并与由 JavaScript 驱动的结果列表交互。

在抓取时你应该如何处理分页?

通过识别数据源的续页机制、提取具有稳定标识的记录、仅在当前结果被接受后再前进,并在记录的或观测到的结束状态处停止来处理分页。对运行设置一个独立的上限,以避免损坏的续页机制导致无限抓取。

困难之处在于证明进展。页面标签可能发生变化,而记录保持不变。无限滚动列表可能会替换旧节点而不是增长。一次成功响应可能只包含一条访问消息。正确的采集器应测试真正重要的内容和状态,而不是将一次点击或一次请求等同于新的已接受页面。

摘要

  • 遵循观测到的续页行为。 在构造页面 URL 之前先发现下一页链接或请求状态。
  • 使用记录标识来衡量进展。 元素数量和滚动高度是辅助信号。
  • 区分数据源结束与运行限制。 页面上限产生的是有界集合,而不是完整性的证明。
  • 保持数据源权限和工作负载边界的明确性。 浏览器访问不会扩展采集授权范围。

数据源使用哪种分页模式?

数据源的实际状态转换决定分页模式。检查一个获准访问的页面,并观察用户前进时会发生什么。下一页链接可能导航到新文档。编号控件可能改变查询参数。“加载更多”按钮可能追加记录。滚动哨兵可能在没有可见按钮的情况下触发另一个请求。

在允许访问且含义已被理解的前提下检查结构化响应。浏览器界面可能显示页码,而底层请求却使用不透明的游标。使用受支持的界面和权限边界;一个可发现的端点并不会自动成为公共集合 API。

模式前进动作进展证据可能的结束证据
下一页链接跟随当前的下一目标预期的新记录或续页状态确认不存在有效的下一目标
编号页面使用观测到的受支持页面导航窗口变化且记录键稳定数据源定义的最后一页
加载更多激活已启用的控件新的已接受标识显式的耗尽状态
无限滚动移动相关容器或哨兵新的标识或请求续页确认的数据源结束信号

如何安全地跟随下一页链接?

下一页链接采集器读取当前页面的续页目标,将相对引用解析为相对于页面 URL 的绝对地址,并在跟随之前验证目标。该 HTML 链接关系模型 包含 next 关系,但站点也可以通过自定义标记来表达分页。

在相关信号能够标识预期控件的前提下,优先使用语义属性和稳定的页面结构,而不是生成的类名。确认目标是在推进同一个集合,而不是打开相关类别或广告。保持所选过滤条件和排序上下文不变。

跟踪已访问的页面状态以检测循环。重复的 URL 可以识别下一页链接循环,但某些应用会用一个 URL 表示多个状态。在这些情况下,记录相关的游标、数据源页面状态,或已接受记录键的指纹。仅用 URL 保护会错误地终止有效的有状态列表。

在发送凭据之前验证目标来源。被抓取的链接是不受信任的输入。采集器不应仅因为页面把某个链接称作“下一页”,就向不相关的主机转发授权头或会话材料。

何时可以使用编号页面 URL?

当数据源公开并支持观测到的 URL 模式时使用编号页面 URL。在构建范围之前确认首次跳转和任何最后一页信号。不要假设每个集合都使用页面查询参数,或可见页码都对应简单的算术偏移。

变化中的集合会使编号遍历变得复杂。插入和删除会移动结果窗口,产生重叠或空洞。稳定标识能够去重输出,但无法恢复在窗口之间被跳过的记录。在界面允许的情况下保持数据源的排序顺序固定,并报告集合的观测范围。

The 在受限查询中对唯一排序的需求 说明了潜在的问题。按受欢迎程度进行的可见排序在收集过程中可能会发生变化。即使是按时间顺序排序,如果记录具有相同的时间戳,也需要一个打破平局的策略。

加载更多列表与无限滚动列表有何不同?

“加载更多”列表暴露出一个显式操作,而无限滚动列表则在满足相关视口条件时触发加载。两者在该操作之后都需要等待特定数据源的进度完成。一次点击操作的完成或滚动事件的触发并不能证明已获取到更多记录。

The 交叉观察者机制 解释了可见性变化如何触发异步工作。一些站点会在嵌套滚动容器内观察一个哨兵元素,而不是文档视口。滚动文档可能不会触及那个哨兵元素。

虚拟列表可以在可见窗口移动时重用 DOM 元素。即使有新记录变为可见,元素数量或页面高度也可能保持稳定。应在每次观察窗口时收集稳定的键,并根据具体实现使用适合的结束信号或延续状态。

在加载过程中,一个被禁用的按钮同样是含糊不清的。应将临时的加载状态与由来源定义的耗尽状态区分开来。如果在本次运行允许的观察窗口内无法确定进度,则应以未解决的结果停止,而不是声称来源已经结束。

有界的下一链接示例是什么样子的?

一个有界示例可以在不声称完整收集的情况下演示遍历。下面的 Python 程序使用公共 用于抓取演示的图书目录,跟随其观察到的下一个链接,验证预期的主机,并且最多只接受两个页面。它使用 Python 标准库且不会调用 Scrapeless。

该代码已针对本文示例执行。它从此特定演示标记中带有标题的锚点中发现图书详情链接;该选择器规则并不是通用的目录解析器。下面的输出是捕获的结果,而页面抓取状态有意标识为部分遍历。

from html.parser import HTMLParser
from urllib.parse import urljoin, urlparse
from urllib.request import Request, urlopen

class CatalogPage(HTMLParser):
    def __init__(self):
        super().__init__()
        self.in_next = False
        self.next_url = None
        self.records = []

    def handle_starttag(self, tag, attrs):
        attrs = dict(attrs)
        if tag == 'li' and 'next' in attrs.get('class', '').split():
            self.in_next = True
        if tag == 'a' and self.in_next:
            self.next_url = attrs.get('href')
        if tag == 'a' and 'title' in attrs:
            self.records.append((attrs.get('href'), attrs['title']))

    def handle_endtag(self, tag):
        if tag == 'li':
            self.in_next = False

url = 'https://books.toscrape.com/catalogue/page-1.html'
seen_pages, seen_items = set(), set()
reason = 'page cap'
for _ in range(2):
    if urlparse(url).hostname != 'books.toscrape.com':
        raise ValueError('Unexpected continuation host')
    if url in seen_pages:
        raise ValueError('Repeated page URL')
    seen_pages.add(url)
    with urlopen(Request(url, headers={'User-Agent': 'Mozilla/5.0'}),
                 timeout=20) as response:
        if response.geturl() != url:
            raise ValueError('Unexpected redirect')
        document = response.read().decode('utf-8')
    parser = CatalogPage()
    parser.feed(document)
    if not parser.records:
        raise ValueError('Expected book records are absent')
    for href, title in parser.records:
        seen_items.add(urljoin(url, href))
    print('Accepted page:', url, 'records:', len(parser.records))
    if not parser.next_url:
        reason = 'source end'
        break
    url = urljoin(url, parser.next_url)
print('Unique books:', len(seen_items), '| Stop:', reason)

捕获的输出

Accepted page: https://books.toscrape.com/catalogue/page-1.html records: 20
Accepted page: https://books.toscrape.com/catalogue/page-2.html records: 20
Unique books: 40 | Stop: page cap

该程序在继续到下一页之前会接受当前页面,检测重复的页面 URL,并存储唯一的条目 URL。生产环境中的工作流应将内存中的集合替换为一个其键和值的进度边界与其输出契约相匹配的目标。它还应使用特定于源的证据,将源内容为空与抽取失败区分开来。

何时需要使用浏览器?

当分页需要执行 JavaScript、交互式控件或已授权的会话状态时,浏览器就很有用。 无痕代理浏览器 提供了受控的浏览器界面。 代理浏览器连接配置 描述其设置;收集器仍然定义选择器、进度检查和存储。

The 无抓取分页操作指南 涵盖常见的页面控制形态。将这些形态作为发现类别使用,然后验证当前目标。在普通的可增长文档中,滚动高度启发式可以作为有用的证据,但不应被视为通用的结束条件。

浏览器不会移除访问限制,也不会使馆藏变得完整。当页面显示访问挑战或权限错误时,应停止将其内容视为记录,并记录访问结果。在来源要求的情况下,在整个页面链中保持授权会话状态的一致性。

你应该如何记录完成情况并负责任地运营?

记录运行结束的原因、集合上下文以及已接受记录的边界。源耗尽、页面上限、记录上限、已用时间上限和未解决的进度是不同的状态。在推进其持久检查点之前,先持久化已接受的输出,以便在中断时不会默默跳过一个窗口。

在运行抓取之前,请查看源术语和适用的采集规则。 机器人排除协议 描述了爬虫指令,并明确将其与访问授权区分开来。遵守 robots 规则是源数据处理的一部分;它本身并不足以决定是否拥有收集或重复使用数据的权限。

保持每个来源的工作量适度并受源策略约束。仅当各个窗口彼此独立且工作负载被允许时,才适用并行处理。Cursor 链通常需要按顺序探索。不要将一个受限的工作负载分发到多个身份上,以规避其预期的限制。

使用 无爬取定价 用于规划浏览器采集成本,然后监控已接受的唯一记录和未解决的分区。更大的页面数量并不能证明数据集更好,如果新增页面重复了记录或包含不相关的内容。

结论

从观测到的源行为中选择分页分支,使用稳定身份验证进度,并保留一个明确的结束原因。收集器应能够解释其接受了哪些页面或状态,以及为什么认为所得范围是完整的或部分的。

将集合层付诸实践

选择与您的来源匹配的 Scrapeless 集合功能,然后应用上述验证和操作决策。

今天注册即可获得 5 美元免费额度 — 无需信用卡.

领取您的 $5 额度 →

常见问题

收集公开的分页数据是否总是被允许?

不。仅仅是公开可见并不能决定权限、条款、隐私义务或再利用权利。在收集之前,请明确定义允许的来源和用途。

分页流程是否总是需要代理?

不。需求取决于数据来源和已批准的部署方式。代理不会扩展访问权限,也不会免除遵守来源工作负载策略的要求。

如果出现的是访问页面而不是记录怎么办?

将受影响的状态标记为访问结果,并停止将其视为数据集内容。在继续收集工作前,确认已授权会话和来源策略。

如果网站的标记结构发生变化怎么办?

重新检查当前记录与续页结构,然后更新来源特定的选择器和接收校验。选择器未返回任何条目并不能证明来源为空。

分页是否可以在没有 AI 代理的情况下运行?

可以。受限的 Python 示例可以在没有 AI 代理的情况下直接运行。依赖浏览器的工作流也可以使用普通的自动化代码,并配合显式的状态和进度检查。

页面链是否可以并发运行?

只有彼此独立可寻址的窗口才应被视为在允许的来源工作负载范围内并行收集。下一令牌链通常依赖于前一个响应。

参考资料