ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Scrapling 通用爬虫模板实战:CrawlSpider、SitemapSpider 与 LinkExtractor 深度解析

Scrapling 通用爬虫模板实战:CrawlSpider、SitemapSpider 与 LinkExtractor 深度解析 Scrapling 通用爬虫模板实战CrawlSpider、SitemapSpider 与 LinkExtractor 深度解析【免费下载链接】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绝大多数站点抓取都逃不出两种模式「跟随符合某种模式的链接」和「遍历站点 sitemap 中列出的每个 URL」。Scrapling 的通用爬虫模板Generic Spider Templates正是为此而生——它把这两类最常见的parse()样板代码封装成了CrawlSpider与SitemapSpider并提供XMLFeedSpider、CSVFeedSpider处理数据源场景。读完本篇你将掌握如何用最少的代码声明式地驱动一场完整爬取并理解每个模板底层的调度逻辑与LinkExtractor的完整参数语义。模板总览它们省掉了什么所有模板都构建在LinkExtractor之上——这个原语负责从Response中抽取 URL或者通过matches()对单个 URL 做过滤判断。SitemapSpider还会在内部解析 sitemap.xml / sitemap_index.xml 的响应体无论是否 gzip 压缩。模板只是替你省去了接线工作wiring你完全可以在任何普通Spider.parse()里直接使用LinkExtractor。模板的源码位于 scrapling/spiders/templates/导出CrawlSpider、CrawlRule、SitemapSpider、ShopifySpider、XMLFeedSpider、CSVFeedSpider六个类。CrawlSpider基于声明式规则自动跟随链接CrawlSpider根据声明式的规则自动跟随链接。官方文档给出的最小可用示例from scrapling.spiders import CrawlSpider, CrawlRule, LinkExtractor class QuotesSpider(CrawlSpider): name blog start_urls [https://quotes.toscrape.com/] def rules(self): return [ CrawlRule(LinkExtractor(allowr/author/), callbackself.parse_author), CrawlRule(LinkExtractor(allowr/page/\d/)), # follow pagination, no callback ] async def parse_author(self, response): yield { .author-title: response.css(.author-title::text).get(), birthday: response.css(.author-born-date::text).get(), url: response.url, } result QuotesSpider().start()规则机制从源码看默认 parse() 的行为一条CrawlRule将LinkExtractor与三个可选字段配对定义见 crawler.pycallback蜘蛛上的一个绑定方法处理每个匹配 URL 的请求priority覆盖所派发Request的优先级process_request一个绑定方法在Request被 yield 之前对其进行修改。CrawlSpider的默认parse()实现非常直白crawler.py遍历rules()返回的每条规则用规则的link_extractor.extract(response)从当前响应中抽取 URL逐个调用response.follow(url, callbackrule.callback)生成请求若设置了priority则覆写请求优先级若设置了process_request则在其返回的 request 上继续 yield。两条值得注意的行为细节无 callback 的规则会落到默认parse()。规则没有 callback 时匹配到的 URL 会经由response.follow()继承原请求的callback即None引擎随后回落到蜘蛛的默认parse()。这对翻页非常方便抽取下一页链接让爬取继续无需单独写处理器。tests/spiders/test_templates.py 中的test_rule_with_no_callback_leaves_request_callback_none明确验证了这一点。规则是叠加执行的。默认parse()会把每条规则都作用于每个响应每个匹配 URL 都会产出一个Request——多条规则之间不做互斥。这与SitemapSpider的「首个匹配即胜出」语义不同写规则时要心中有数。结合规则与自定义逻辑重写parse()并调用super().parse(response)就能同时获得规则行为和自己的产出class MySpider(CrawlSpider): def rules(self): return [CrawlRule(LinkExtractor(allowr/posts/), callbackself.parse_post)] async def parse(self, response): yield {page_url: response.url} async for req in super().parse(response): yield reqtests/spiders/test_templates.py 的test_user_can_compose_super_parse验证了这种组合先产出自定义 item再转发规则产生的请求。用 process_request 修改请求process_request的签名是(request, response) - request可以在 yield 前加请求头、改优先级甚至整个替换请求def add_priority(self, request, response): request.priority 10 return request def rules(self): return [CrawlRule( LinkExtractor(allowr/posts/), callbackself.parse_post, process_requestself.add_priority, )]测试用例 test_process_request_invoked 与 test_process_request_can_replace_request 分别验证了「修改后返回」与「返回一个全新的 Request」两种用法都成立。另外response.follow()会附带 referer 头规则路径派发出的请求同样保留该头见 test_referer_set_on_followed_requests而带绑定方法 callback 的Request也通过__getstate__机制把回调转换为方法名字符串可安全参与 pickle 序列化这对 checkpoint 断点续爬至关重要见 test_pickle_request_with_bound_method_callback。SitemapSpider从 sitemap.xml 播种的爬虫SitemapSpider以 sitemap.xml 中的 URL 作为爬取种子。它使用与CrawlSpider相同的rules()API心智模型完全共享from scrapling.spiders import SitemapSpider, CrawlRule, LinkExtractor class MySitemap(SitemapSpider): name sm sitemap_urls [https://example.com/sitemap.xml] def rules(self): return [ CrawlRule(LinkExtractor(allowr/posts/), callbackself.parse_post), CrawlRule(LinkExtractor(allowr/products/), callbackself.parse_product), ] async def parse_post(self, response): yield {title: response.css(h1::text).get()} async def parse_product(self, response): yield {sku: response.css(.sku::text).get()} result MySitemap().start()URL 是如何被派发的对 sitemap 中的每个 URLSitemapSpider按顺序检查每条规则的LinkExtractor.matches(url)首个匹配的规则胜出并 yield 一个携带该规则 callback 的Request。若没有任何规则匹配且rules()非空该 URL 被丢弃若rules()返回空列表则所有 URL 都路由到蜘蛛的parse()方法——而基类默认实现直接抛NotImplementedError除非你重写了它。这段调度逻辑对应 _dispatch 与 _parse_sitemaprules为空时直接response.follow(url)callback 为None落回parse()否则逐条matches()命中即返回请求。tests/spiders/test_sitemap.py 的test_urlset_dispatched_through_rules和test_no_rules_means_all_urls_fall_through分别锁定了这两种行为/about这类未匹配 URL 会被丢弃而空规则时所有 URL 都以callbackNone派发。Sitemap 索引sitemap of sitemaps遇到sitemapindex时蜘蛛会自动深入每个子 sitemap。若要筛选深入哪些子 sitemap把sitemap_follow设为一个LinkExtractorclass MySitemap(SitemapSpider): name sm sitemap_urls [https://example.com/sitemap.xml] sitemap_follow LinkExtractor(allowr/posts-sitemap-\d\.xml) # only post sitemaps实现上_sm_body 区分根元素类型sitemapindex收集子 sitemap 的locurlset则提取 URL 列表见数据结构 SitemapResult随后在_parse_sitemap中每个子 sitemap URL 都先过一遍sitemap_follow.matches()sitemap_follow为None时全部深入。test_sitemap_follow_filters_child_sitemaps 验证了过滤器只放行posts-sitemap.xml的效果。robots.txt 支持直接把robots.txt的 URL 放进sitemap_urls蜘蛛会识别它、提取其中声明的所有 Sitemap 并逐一跟随class MySitemap(SitemapSpider): name sm sitemap_urls [https://example.com/robots.txt]判断依据是响应 URL 的路径是否以/robots.txt结尾sitemap.py解析则由 _robots_body 借助protego库完成解析失败时仅记录警告并返回空列表不会中断爬取。多语言AlternateURL设置sitemap_alternate_links True后xhtml:link relalternate hreflang...声明的 URL 也会一并通过你的rules()派发。实现位于 _extract_urls遍历url节点时除loc外还收集开启了开关后的link子元素的href。test_alternate_links_dispatched_when_enabled 验证了英文主 URL 与法语、德语 alternate URL 共三个 URL 全部进入调度。gzip 与容错Sitemap 体无论是以 gzip 魔数\x1f\x8b开头还是content-type声明 gzip都会被 _decompress 自动解压解压输出设置了64 MiB 上限以防御 gzip 炸弹超限抛OSError后被_sm_body捕获为警告。XML 解析失败XMLSyntaxError同样只记录警告并返回空的SitemapResult爬虫继续运行。XMLFeedSpider逐节点解析 XML 数据源XMLFeedSpider遍历 XML 数据源RSS、Atom、商品 feed 等的节点。把itertag设为你想迭代的节点名默认item并重写parse_node()——它会对每个匹配节点调用一次from scrapling.spiders import XMLFeedSpider class RSSSpider(XMLFeedSpider): name rss start_urls [https://example.com/feed.xml] itertag item async def parse_node(self, response, node): yield { title: node.findtext(title), link: node.findtext(link), date: node.findtext(pubDate), } result RSSSpider().start()和其他回调一样parse_node()也可以 yieldRequest对象例如response.follow(node.findtext(link), callbackself.parse_post)从而深入数据源指向的页面。节点如何匹配与解析传给parse_node()的每个节点都是一个已剥离全部命名空间的lxml元素因此node.findtext(title)、node.find(thumbnail).get(url)以及大小写敏感的node.xpath(...)在任何 feed 上都不需要命名空间映射即可工作。匹配机制分两种见 _wanted_tag 与 _iter_nodes普通itertag如entry按局部名匹配无视命名空间——这正是 Atom 和大多数带命名空间 feed 需要的行为带前缀的itertag前缀必须在namespaces中定义成(prefix, uri)元组否则抛ValueError。此时只匹配该命名空间下的节点class ThumbnailSpider(XMLFeedSpider): name thumbs start_urls [https://example.com/feed.xml] itertag media:thumbnail namespaces ((media, http://search.yahoo.com/mrss/),) async def parse_node(self, response, node): yield {thumbnail: node.get(url)}命名空间剥离由 _strip_namespaces 完成深拷贝节点后把每个 tag 与属性名替换为localname再调用etree.cleanup_namespaces。Gzipped feed.xml.gz或以 gzip content-type 传输沿用 sitemap 同一套解压保护自动解压畸形 XML 记录警告而非让爬取崩溃——feed.py 的parse()中对OSError和XMLSyntaxError都只warning后return。测试文件 tests/spiders/test_feed.py 覆盖了默认itertag迭代、命名空间剥离、Atomentry匹配等场景。CSVFeedSpider逐行解析 CSV 数据源CSVFeedSpider遍历 CSV feed 的每一行。重写parse_row()它接收的每行都是以列名为键的字典from scrapling.spiders import CSVFeedSpider class PriceSpider(CSVFeedSpider): name prices start_urls [https://example.com/products.csv] async def parse_row(self, response, row): yield {product: row[title], price: float(row[price])} result PriceSpider().start()相关类属性定义见 feed.pyheaders列名列表。默认不设置时feed 的第一行用作表头若 feed 没有表头行需自行指定delimiter字段分隔符默认,quotechar包裹特殊字符字段的引号字符默认。class PriceSpider(CSVFeedSpider): name prices start_urls [https://example.com/products.csv] headers [title, price, url] delimiter ;实现上parse响应体经_decompress解压后按response.encoding缺省utf-8errorsreplace解码交给csv.DictReader逐行产出并转发给parse_row()。Gzipped feed 同样自动解压保护机制与 XMLFeedSpider 相同。tests/spiders/test_feed.py 中专门准备了无表头 CSV 与分号分隔 CSV 的测试数据。直接使用 LinkExtractor你不必使用模板。LinkExtractor在任何普通Spider里都能工作from scrapling.spiders import Spider, LinkExtractor class CustomSpider(Spider): name custom start_urls [https://example.com] def __init__(self): super().__init__() self._links LinkExtractor(allowr/posts/, deny_domainsads.example.com) async def parse(self, response): for url in self._links.extract(response): yield response.follow(url, callbackself.parse_post) async def parse_post(self, response): yield {title: response.css(h1::text).get()}LinkExtractor 参数参考完整参数语义源码位于 links.py参数默认值说明allow()要保留的 URL 模式。空表示「全匹配」。可为字符串、编译好的Pattern或二者的可迭代对象。deny()要丢弃的 URL 模式。永远覆盖allow。allow_domains()要保留的主机名。子域自动匹配example.com匹配api.example.com。deny_domains()要丢弃的主机名。restrict_css()CSS 选择器把 DOM 抽取限定到某个区域。restrict_xpath()XPath 选择器把 DOM 抽取限定到某个区域。tags(a, area)查找链接的元素标签。attrs(href,)从这些标签读取 URL 的属性。canonicalizeTrue排序查询参数并规范化路径。stripTrue去除抽取 URL 中的空白字符。keep_fragmentFalse规范化时是否保留#fragment。deny_extensionsIGNORED_EXTENSIONS要丢弃的文件扩展名pdf、zip、图片、视频等。processNone可选的回调在过滤前作用于每个抽取到的 URL。返回假值即丢弃该 URL。LinkExtractor.extract(response)返回一个list[str]绝对的、经过过滤的、去重后的URL 列表LinkExtractor.matches(url)返回bool是纯 URL 过滤器allow/deny/domain/extension被SitemapSpider用于在没有Response的情况下按规则派发 sitemap URL。从 _extract 与 _url_passes 的实现还可以确认几个细节抽取流程若设置了restrict_css/restrict_xpath先圈定作用域都没设则作用于整页再用拼接的 XPath如.//a/href | .//area/href取原始 href随后依次经过strip空白 →response.urljoin相对转绝对 →process回调 →canonicalize_url规范化 → 合法性校验 → 过滤去重使用dict.fromkeys以保持链接出现顺序Schema 白名单只放行http、https、file三种协议javascript:、mailto:等链接天然被排除links.py扩展名检查最优先在 allow/deny 正则之前先做扩展名过滤IGNORED_EXTENSIONS内置了约 100 个扩展名涵盖压缩包zip、tar.gz等、图片、音视频、Office 文档、css/pdf/exe/js等links.py匹配时按后缀逐级判断tar.gz这类多段后缀也能命中域名匹配规则host d or host.endswith(. d)即精确主机加任意子域且两侧都转小写比较避免Example.com与example.com的漏配。小结模板的选择路径场景选择核心 API跟随符合正则模式的链接CrawlSpiderrules()CrawlRule以 sitemap.xml 为种子SitemapSpidersitemap_urls/sitemap_follow/rules()消费 robots.txt 声明的 sitemapSitemapSpidersitemap_urls直接放 robots.txt遍历 RSS/Atom/商品 XML 源XMLFeedSpideritertagparse_node()遍历 CSV 数据源CSVFeedSpiderheaders/delimiterparse_row()完全自定义链接策略普通Spider直接使用LinkExtractor三者共同的底座是LinkExtractor的正则化 URL 过滤scrapling/spiders/links.py模板则在其上叠加了各自的调度语义CrawlSpider多规则叠加、SitemapSpider首规则胜出。配合CrawlRule的priority与process_request钩子、response.follow()的 referer 传递、以及Request的 pickle 友好设计这套模板可以平滑接入 Scrapling 爬虫引擎的并发、限速与 checkpoint 能力从单条请求到全量爬取都适用。【免费下载链接】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),仅供参考
返回列表