Scrapling 蜘蛛会话管理实战:多会话配置、sid 请求路由与 SessionManager 源码解析

📅 发布时间:2026/9/7 2:17:45
Scrapling 蜘蛛会话管理实战:多会话配置、sid 请求路由与 SessionManager 源码解析
Scrapling 蜘蛛会话管理实战多会话配置、sid 请求路由与 SessionManager 源码解析【免费下载链接】Scrapling️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!项目地址: https://gitcode.com/GitHub_Trending/sc/Scrapling在 Scrapling 的 Spider 框架中会话Session决定了每个请求由谁来发起是走高速 HTTP 客户端还是走带 JS 渲染的浏览器亦或是能处理反爬验证的隐身浏览器。本文以官方 Spider 会话文档为主线完整覆盖configure_sessions()的用法、SessionManager.add()的参数语义、sid请求路由与 kwargs 继承机制并结合 SessionManager 源码、Request 实现 与 SessionManager 测试 深入剖析其生命周期管理原理帮助你在一个爬虫中同时驾驭多种抓取引擎并按需切换。什么是 SessionsSession 是一个预先配置好的 fetcher 实例它在整个爬取期间保持存活。Spider 不会为每个请求新建连接或浏览器而是复用这些会话因此速度更快、资源占用更少。默认情况下每个 Spider 都会创建一个FetcherSession高速 HTTP 会话。通过覆写configure_sessions()方法你可以添加更多会话或替换默认会话。需要注意的是浏览器类会话只能使用各自的异步async版本这一约束在源码层面有明确体现——SessionManager 的类型定义 中只允许三种会话类型Session FetcherSession | AsyncDynamicSession | AsyncStealthySession可用会话类型及典型用途如下会话类型适用场景FetcherSession高速 HTTP 请求无 JavaScriptAsyncDynamicSession浏览器自动化、JavaScript 渲染AsyncStealthySession绕过反爬anti-bot、Cloudflare 等这三个类分别实现在 FetcherSession、AsyncStealthySession 与 AsyncDynamicSession 中并统一由 fetchers 包的惰性导入机制 导出因此可以直接from scrapling.fetchers import FetcherSession, AsyncStealthySession。配置会话configure_sessions()覆写 Spider 的configure_sessions()方法来设置会话。参数manager是一个SessionManager实例调用manager.add()注册会话from scrapling.spiders import Spider, Response from scrapling.fetchers import FetcherSession class MySpider(Spider): name my_spider start_urls [https://example.com] def configure_sessions(self, manager): manager.add(default, FetcherSession()) async def parse(self, response: Response): yield {title: response.css(title::text).get()}manager.add()方法的完整参数如下参数类型默认值说明session_idstr必填在请求中引用该会话时使用的名称sessionSession必填会话实例defaultboolFalse将该会话设为默认会话lazyboolFalse仅在该会话首次被使用时才启动对照 add() 的源码签名 可以看到几个关键行为重复注册直接报错若session_id已存在抛出ValueError(Session {session_id} already registered)避免静默覆盖导致的调试困难默认会话的确定规则if default or self._default_session_id is None——要么显式传入defaultTrue要么是第一个被添加的会话lazy 标记维护lazyTrue的会话 ID 会被记录到内部_lazy_sessions集合中供启动与关闭逻辑识别方法链式调用add()返回self测试用例 test_method_chaining 验证了manager.add(s1, ...).add(s2, ...)的流式写法。文档中的三条注意事项均可在源码中得到印证未指定会话时走默认会话。默认会话按两种方式之一确定第一个添加到 manager 的会话自动成为默认或者显式带defaultTrue添加的会话。SessionManager 测试 中test_first_session_becomes_default与test_explicit_default_session分别验证了这两种规则。此外Spider.start_requests() 生成的起始请求本身就携带sidself._session_manager.default_session_id即起始 URL 默认由默认会话抓取。你传入的会话实例无需提前启动。Spider 会检查所有会话是否已启动未启动的统一启动。对应 start() 的实现遍历所有非 lazy 且_is_alive为假的会话逐一调用await session.__aenter__()。lazy参数让会话用时才启动。例如希望浏览器只在真正需要时才启动而不是随 Spider 一起启动。start()会跳过 lazy 会话测试用例 test_start_skips_lazy_sessions 验证了这一点而当某个请求首次路由到该 lazy 会话时fetch() 会在锁保护下补启动它双重检查if sid in self._lazy_sessions and not session._is_aliveasync with self._lazy_lock保证并发安全。另外Spider 的初始化逻辑 对会话配置做了两层校验configure_sessions()中抛出的任何异常都会被包装为SessionConfigurationError并附带 Spider 类名若最终没有任何会话被添加同样抛出SessionConfigurationError。也就是说会话配置错误会在爬虫启动前就快速失败。多会话 SpiderHTTP 列表页 隐身浏览器详情页下面是一个典型实战场景列表页用高速 HTTP 会话抓取受保护的详情页交给隐身浏览器from scrapling.spiders import Spider, Response from scrapling.fetchers import FetcherSession, AsyncStealthySession class ProductSpider(Spider): name products start_urls [https://shop.example.com/products] def configure_sessions(self, manager): # Fast HTTP for listing pages (default) manager.add(http, FetcherSession()) # Stealth browser for protected product pages manager.add(stealth, AsyncStealthySession( headlessTrue, network_idleTrue, )) async def parse(self, response: Response): for link in response.css(a.product::attr(href)).getall(): # Route product pages through the stealth session yield response.follow(link, sidstealth, callbackself.parse_product) next_page response.css(a.next::attr(href)).get() if next_page: yield response.follow(next_page) async def parse_product(self, response: Response): yield { name: response.css(h1::text).get(), price: response.css(.price::text).get(), }核心是sid参数——它告诉 Spider 每个请求应该使用哪个会话。当response.follow()不传sid时会继承原请求的会话 ID。这一行为在 Response.follow() 的源码 中一目了然sidsid or self.request.sid。sid如何转化为实际的抓取动作看 SessionManager.fetch() 的路由逻辑sid request.sid if request.sid else self.default_session_id session self.get(sid)空sid回落到默认会话get()在会话不存在时抛出带有可用会话列表的KeyError如Session stealth not found. Available: http便于快速定位拼写错误会话类型不同调用路径也不同FetcherSession走底层 HTTP 客户端的_make_request(method..., url..., **kwargs)浏览器会话则调用session.fetch(url..., **request._session_kwargs)最后将request挂载回response.request并做 meta 合并response 的 meta 优先这也是response.follow()能继承会话参数的前提。爬虫引擎 中的实际取数入口就是response await self.session_manager.fetch(request)且统计模块会按request.sid or default_session_id为每个会话分别累计请求数increment_requests_count方便你观察各会话的流量分布。同一类会话的不同实例会话也可以是同一类的不同实例只是配置不同——例如用 Chrome 与 Firefox 两种指纹模拟并行抓取from scrapling.spiders import Spider, Response from scrapling.fetchers import FetcherSession class ProductSpider(Spider): name products start_urls [https://shop.example.com/products] def configure_sessions(self, manager): chrome_requests FetcherSession(impersonatechrome) firefox_requests FetcherSession(impersonatefirefox) manager.add(chrome, chrome_requests) manager.add(firefox, firefox_requests) async def parse(self, response: Response): for link in response.css(a.product::attr(href)).getall(): yield response.follow(link, callbackself.parse_product) next_page response.css(a.next::attr(href)).get() if next_page: yield response.follow(next_page, sidfirefox) async def parse_product(self, response: Response): yield { name: response.css(h1::text).get(), price: response.css(.price::text).get(), }这种同构多实例模式也常用于按域名/流量比例分配不同代理配置或不同impersonate指纹的会话。Session Arguments按请求定制而不改动会话配置传给Request或经response.follow(**kwargs)传入的额外关键字参数会被原样转发给对应会话的 fetch 方法。这使得你可以在不改动会话配置的前提下对单个请求做定制async def parse(self, response: Response): # Pass extra headers for this specific request yield Request( https://api.example.com/data, headers{Authorization: Bearer token123}, callbackself.parse_api, ) # Use a different HTTP method yield Request( https://example.com/submit, methodPOST, data{field: value}, sidfirefox, callbackself.parse_result, )从 Request 的构造函数 可以看到sid、callback、priority、dont_filter、meta是被具名消费的框架参数其余所有 kwargs 全部落入_session_kwargs字典最终由SessionManager.fetch()展开转发给底层会话。同时请求指纹 会把sid、请求体、method、规范化后的 URL 一并纳入去重哈希——这意味着同一个 URL 用不同会话不同sid抓取会被视为两个不同的请求。注意Warning在 Spider 中使用FetcherSession时不能直接调用.get()和.post()方法。请求默认是 HTTP GET若需其他 HTTP 方法请像上例那样通过method参数传入。这是为了在所有会话类型间统一Request接口。对于浏览器会话AsyncDynamicSession、AsyncStealthySession还可以传入浏览器专用参数如wait_selector、page_action、extra_headersasync def parse(self, response: Response): # Use Cloudflare solver with the AsyncStealthySession we configured above yield Request( https://nopecha.com/demo/cloudflare, sidstealth, callbackself.parse_result, solve_cloudflareTrue, block_webrtcTrue, hide_canvasTrue, google_searchTrue, ) yield response.follow( /dynamic-page, sidbrowser, callbackself.parse_dynamic, wait_selectordiv.loaded, network_idleTrue, )这些浏览器参数在 AsyncStealthySession 的参数文档 中有明确定义network_idle等待页面网络空闲至少 500 ms 后再返回wait_selector/wait_selector_state等待指定 CSS 选择器到达特定状态默认状态为attachedsolve_cloudflare自动处理 Cloudflare 的 Turnstile/Interstitial 挑战后再返回响应hide_canvas向 canvas 操作注入随机噪声防止指纹识别block_webrtc强制 WebRTC 遵守代理设置防止本机 IP 泄露google_search默认启用Scrapling 会设置 Google referer 头page_action导航完成后执行的自动化函数接收page对象。继承规则Warning从原请求传入的会话参数**kwargs会被response.follow()继承新传入的 kwargs 优先于继承值。follow() 的源码 展示了这条合并链# Merge original session kwargs with new kwargs (new takes precedence) session_kwargs {**self.request._session_kwargs, **kwargs}除 kwargs 外follow()还会自动继承sid、callback、priority并在referer_flowTrue默认时把当前响应 URL 写入 refererHTTP 会话写headers浏览器会话写extra_headers同时把google_search置为False以免 follow 请求被误加 Google referer。一个实际例子默认会话配置了桌面 Chrome 指纹但某个 follow 请求希望改用移动端指纹——from scrapling.spiders import Spider, Response from scrapling.fetchers import FetcherSession class ProductSpider(Spider): name products start_urls [https://shop.example.com/products] def configure_sessions(self, manager): manager.add(http, FetcherSession(impersonatechrome)) async def parse(self, response: Response): # I dont want the follow request to impersonate a desktop Chrome like the previous request, but a mobile one # so I override it like this for link in response.css(a.product::attr(href)).getall(): yield response.follow(link, impersonatechrome131_android, callbackself.parse_product) next_page response.css(a.next::attr(href)).get() if next_page: yield Request(next_page) async def parse_product(self, response: Response): yield { name: response.css(h1::text).get(), price: response.css(.price::text).get(), }这里新传入的impersonatechrome131_android覆盖了继承来的桌面 Chrome 指纹正是新 kwargs 优先规则的直接应用。值得说明的一个工程细节SessionManager.fetch() 使用request._session_kwargs.copy()后再取method而非原地pop。对应测试 特意验证了 fetch 不会篡改原请求的 kwargs——因为重试请求通过request.copy()复制若method在首次 fetch 时被弹掉重试就会退化为 GET。这类细节保证了会话参数在多轮重试中的一致性。会话生命周期启动、延迟启动与自动关闭会话的启动与关闭由SessionManager统一管理Spider 无需手工干预启动爬虫引擎在取数前通过async with self.session_manager见 engine 中的用法触发 start()。start()是幂等的_started标志位保证重复调用安全测试 test_start_idempotent 有验证且只启动非 lazy 的会话延迟启动lazy 会话在首次被请求命中时才启动且 fetch() 内部用_lazy_lock防止并发重复启动关闭Spider 结束时manager 会自动检查是否还有会话在运行并将其关闭之后才关闭 Spider。close() 的实现 中有一个精妙之处从未启动过的 lazy 会话会被跳过if sid in self._lazy_sessions and not session._is_alive: continue即你从未用到的浏览器既不会启动也不会被关闭避免无谓的资源开销。完整的生命周期语义在 测试用例 test_lifecycle_management 中得到验证start 前所有会话不存活 → start 后全部存活 → close 后全部关闭。小结Scrapling Spider 的会话机制可以归纳为四条主线会话即长连接/长浏览器通过覆写configure_sessions()用manager.add(session_id, session, default..., lazy...)注册FetcherSession/AsyncDynamicSession/AsyncStealthySession首个添加或显式defaultTrue的成为默认会话sid是请求级路由开关Request(sid...)或response.follow(..., sid...)决定请求走哪个会话不传则继承或回落默认kwargs 即请求级配置所有未具名参数经_session_kwargs原样转发给会话的 fetch 方法follow()按新值覆盖继承值合并从而在不改会话配置的前提下定制单个请求的 headers、method、指纹、等待策略乃至 Cloudflare 求解生命周期全自动非 lazy 会话随爬虫启动lazy 会话首次使用时在锁保护下补启动Spider 关闭时自动清理仍在运行的会话。配合 Spider 基础文档 与 会话抓取文档你可以快速把本文的HTTP 隐身浏览器双会话模式套用到自己的爬虫中。【免费下载链接】Scrapling️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!项目地址: https://gitcode.com/GitHub_Trending/sc/Scrapling创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考