Scrapy多层内部链接爬取优化:避免重复与数据不完整


scrapy多层内部链接爬取优化:避免重复与数据不完整

本文深入探讨了使用Scrapy框架进行多层内部链接爬取时常见的挑战,特别是如何有效避免数据重复、不完整以及跳过关键内容的问题。通过分析错误的爬取策略,文章提供了优化分页处理、正确使用请求过滤器以及合理组织数据提取和项(Item)提交的专业解决方案,旨在帮助开发者构建更高效、更健壮的Scrapy爬虫。

Scrapy多层内部链接爬取策略与优化

在使用Scrapy进行网页数据抓取时,经常会遇到需要从主页面提取链接,然后进入这些链接的页面继续提取数据的场景,即多层内部链接爬取。这种需求如果处理不当,极易导致数据重复、数据不完整或部分数据被遗漏的问题。本教程将详细解析这些常见问题,并提供一套优化的解决方案,以构建高效且准确的Scrapy爬虫。

Scrapy爬取基础与链接跟随

Scrapy的核心优势之一是其异步请求处理和内置的链接跟随机制。response.follow()方法是处理内部链接的强大工具,它能自动处理相对URL,并生成新的请求。

import scrapy

class ExampleSpider(scrapy.Spider):
    name = "example"
    start_urls = ['http://example.com']

    def parse(self, response):
        # 提取所有文章链接
        for link in response.css('div.article-list a::attr(href)').getall():
            yield response.follow(link, self.parse_article) # 跟随链接到文章详情页

    def parse_article(self, response):
        # 从文章详情页提取数据
        title = response.css('h1::text').get()
        content = response.css('div.content::text').get()
        yield {
            'title': title,
            'content': content,
            'url': response.url
        }

常见问题与优化方案

在处理复杂的多层链接结构时,以下是几个常见的陷阱及其对应的优化策略。

1. 分页处理不当导致重复请求

问题描述: 许多爬虫在处理分页时,会在每个页面都尝试获取所有分页链接,并为它们都发送请求。这会导致大量重复的请求,甚至可能陷入无限循环,因为每个页面都会重新发现并请求所有已知的分页。

错误示例(简化):

# ... (部分代码省略) ...
    def parse(self, response, **kwargs):
        # ... 提取当前页面的数据或链接 ...

        # 错误:收集所有分页链接并重复请求
        all_pages = response.xpath('//a[@class="pagination-link"]/@href').getall()
        for page_url in all_pages:
            yield response.follow(page_url, self.parse) # 可能导致重复和低效

优化方案: 采用顺序分页策略,即在当前页面只寻找并请求“下一页”的链接。Scrapy的response.urljoin()方法对于构建完整的下一页URL非常有用。

正确示例:

import scrapy

class IcsstriveSpider(scrapy.Spider):
    name = "icsstrive"
    start_urls = ['https://icsstrive.com/']
    baseUrl = "https://icsstrive.com" # 基础URL,用于拼接相对路径

    def parse(self, response):
        # 1. 提取当前页面上的主要内容链接
        for link in response.css('div.search-r-title a::attr(href)').getall():
            yield response.follow(link, self.parse_icsstrive)

        # 2. 寻找并请求下一页
        # 假设当前页的链接有一个特定的CSS类或XPath路径
        # 这里的例子是根据原问题提供的XPath进行修改
        current_page = response.css('li.wpv_page_current')
        # 查找当前页的下一个兄弟节点中的a标签的href属性
        if next_page_relative_url := current_page.xpath("./following-sibling::li/a/@href").get():
            # 使用response.urljoin来处理相对路径,确保生成完整的URL
            yield scrapy.Request(response.urljoin(next_page_relative_url), callback=self.parse)

通过这种方式,爬虫只会按顺序遍历分页,大大提高了效率并避免了重复。

2. dont_filter=True的滥用

问题描述: Scrapy默认会启用去重过滤器,避免对同一个URL发送多次请求。然而,在某些情况下,开发者可能会为了解决看似的“跳过”问题而滥用dont_filter=True参数。这会禁用Scrapy的去重机制,导致对同一URL进行多次请求和解析,从而产生大量重复数据。

无限画 无限画

千库网旗下AI绘画创作平台

无限画 574 查看详情 无限画

错误示例:

# ... (部分代码省略) ...
        # 错误:在不必要的情况下使用dont_filter=True
        request= scrapy.Request(url + "?dummy=" + str(random.random()),callback=self.parse_victims,dont_filter=True,meta={'item': item, 'malwares_urls': malwares_urls, 'threat_source_urls':threat_source_urls})
        # ...

即使添加了随机参数,如果页面的核心内容不变,重复抓取也是低效的。dont_filter=True应该仅在确实需要多次处理同一URL(例如,因为页面内容会随时间动态变化,或者需要用不同的参数组合请求同一资源)时才使用。

优化方案: 除非有明确的理由,否则应避免使用dont_filter=True。让Scrapy的去重过滤器发挥作用,可以有效减少不必要的网络请求和重复数据。如果担心Scrapy跳过某些页面,更应该检查链接提取逻辑或回调函数是否存在问题,而不是简单地禁用去重。

3. 不完整或重复的Item提交

问题描述: 在多层爬取中,如果数据项(Item)需要在多个回调函数中逐步构建,并且在每个回调中都yield该项,就可能导致以下问题:

  • 不完整项: 在数据完全收集之前就yield了项。
  • 重复项: 同一个逻辑数据项在不同回调中被yield多次。
  • 数据覆盖: meta中传递的item被修改,但由于异步执行顺序,可能导致数据覆盖或混乱。

错误示例: 原始代码中,item在parse_icsstrive中初始化,然后通过meta传递给parse_victims,parse_victims又修改item后传递给parse_malware,以此类推。在每个回调函数中,都可能在某些条件下yield item,这会导致同一个逻辑项被多次yield,且可能在未完全填充所有字段时就被提交。

优化方案:

  • 集中数据提取: 尽可能在一个回调函数中提取所有相关数据。如果嵌套链接仅提供主Item的属性(例如,嵌套页面的标题或URL),则尝试在主页面上直接提取这些属性,而不是发起新的请求去访问嵌套页面。
  • 延迟Item提交: 如果确实需要访问嵌套页面来获取核心数据,那么应将Item的yield操作延迟到所有必要数据都已收集完毕的最后一个回调函数中。在中间回调函数中,只负责更新meta中的Item数据,而不进行yield。

示例:集中数据提取 根据原问题中的场景,如果“受害者”、“恶意软件”和“威胁来源”的链接和名称可以直接从主页面提取,而不需要深入其页面获取更多独立内容,那么最佳实践是在parse_icsstrive中一次性提取所有信息并提交Item。

import scrapy

class IcsstriveSpider(scrapy.Spider):
    name = "icsstrive"
    start_urls = ['https://icsstrive.com/']
    baseUrl = "https://icsstrive.com"

    def parse(self, response):
        for link in response.css('div.search-r-title a::attr(href)').getall():
            yield response.follow(link, self.parse_icsstrive)

        current_page = response.css('li.wpv_page_current')
        if next_page := current_page.xpath("./following-sibling::li/a/@href").get():
            yield scrapy.Request(response.urljoin(next_page), callback=self.parse)

    def parse_icsstrive(self, response):
        # 从主页面直接提取所有相关信息,包括嵌套链接的标题和URL
        title = response.xpath('//h1[@class="entry-title"]/text()').get()
        published = response.xpath('//p[@class="et_pb_title_meta_container"]/span/text()').get()
        summary = response.xpath('//div[@class="et_pb_text_inner"]/p/text()').get()
        incident_date = response.xpath('//h3[text()="Incident Date"]/following-sibling::*//text()').get()
        location = response.xpath('//h3[text()="Location"]/following-sibling::p/a/text()').get()
        estimated_cost = response.xpath('//h3[text()="Estimated Cost"]/following-sibling::p/text()').get()
        industries = response.xpath('//h3[text()="Industries"]/following-sibling::p/a/text()').get()
        impacts = response.xpath('//h3[text()="Impacts"]/following-sibling::*//text()').get()

        # 提取受害者、恶意软件、威胁来源的链接和文本
        victims_links = response.xpath("//div[h3[text()='Victims']]//li/a/@href").getall()
        victims_names = response.xpath("//div[h3[text()='Victims']]//li//text()").getall() # 提取文本,可能需要进一步清洗

        malware_links = response.xpath("//div[h3[text()='Type of Malware']]//li/a/@href").getall()
        malware_names = response.xpath("//div[h3[text()='Type of Malware']]//li//text()").getall()

        threat_source_links = response.xpath("//div[h3[text()='Threat Source']]//li/a/@href").getall()
        threat_source_names = response.xpath("//div[h3[text()='Threat Source']]//li/a/text()").getall()

        # 提取引用链接和名称
        references_name = response.xpath('//div[@class="et_pb_text_inner"]/h3[text()="References"]/following-sibling::div/ul/li/a/text()').getall()
        references_url = response.xpath('//div[@class="et_pb_text_inner"]/h3[text()="References"]/following-sibling::div/ul/li/a/@href').getall()

        # 构建并提交完整的Item
        item = {
            "title": title,
            "published": published,
            "summary": summary,
            "incident_date": incident_date,
            "location": location,
            "estimated_cost": estimated_cost,
            "industries": industries,
            "impacts": impacts,
            "victims_names": victims_names,
            "victims_links": victims_links,
            "malware_names": malware_names,
            "malware_links": malware_links,
            "threat_source_names": threat_source_names,
            "threat_source_links": threat_source_links,
            "references_name": references_name,
            "references_url": references_url,
            "url": response.url
        }
        yield item

这个优化后的parse_icsstrive函数直接从主页面提取了所有需要的数据,包括受害者、恶意软件和威胁来源的名称和链接,从而避免了多层回调的复杂性、重复请求和不完整Item的问题。如果确实需要深入这些链接的页面提取更复杂的数据,那么需要精心设计meta参数的传递和Item的组装逻辑,确保Item在所有数据收集完成后只被yield一次。

总结

构建一个高效且准确的Scrapy爬虫,特别是在处理多层内部链接时,需要注意以下几点:

  1. 采用顺序分页: 避免在每个页面都重新发现并请求所有分页链接,只跟随“下一页”链接。
  2. 谨慎使用dont_filter=True: 除非有充分理由,否则应依赖Scrapy的去重机制,避免不必要的重复请求。
  3. 优化Item提交策略: 尽可能在一个回调函数中收集所有相关数据并提交Item。如果必须分多步收集,确保Item只在数据完全收集后yield一次,并妥善管理meta中传递的数据状态。

遵循这些最佳实践,可以显著提高Scrapy爬虫的性能、准确性和健壮性,从而更有效地完成数据抓取任务。

以上就是Scrapy多层内部链接爬取优化:避免重复与数据不完整的详细内容,更多请关注其它相关文章!


# 是在  # 移动网站建设网站运营  # 荔湾网站优化品牌推广  # 河北淘宝网关键词排名  # 滨州网站seo  # 黄圃seo优化  # 手机营销软件推广方式有  # 荆门谷歌seo  # 东莞芦苞网站建设  # 海外网站建站推广怎么做  # SEO优化 内部  # 如何使用  # 面上  # 而不  # 跳过  # css  # 这会  # 下一页  # 不完整  # 分页  # 回调  # a标签  # cos  # 常见问题  # 爬虫  # win  # ai  # 工具  # 回调函数 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894 】


相关推荐: 4399造梦西游3无敌版_4399游戏入口  Excel怎么用XLOOKUP函数实现双向查找_ExcelXLOOKUP替代VLOOKUP+HLOOKUP的高级用法  Leaflet地图弹出窗口图片动态显示:避免缺失图标的专业指南  《地下城堡4:骑士与破碎编年史》墓穴挑战125攻略  Mac如何开启画中画模式_Mac Safari浏览器视频画中画功能  yy漫画官方网站登录入口_yy漫画在线阅读页面地址  mysql离线安装后如何启动_mysql离线安装完成后启动服务的方法  顺丰快递怎么查物流_顺丰快递物流信息实时查询操作指南  Dash应用中自定义HTML页面标题与网站图标(F*icon)的实用指南  CSS动画如何实现图标旋转并放大_transform rotate scale @keyframes实现  天天漫画2025最新入口 天天漫画永久有效登录入口  银信通自动开通原因揭秘  怎样设置开机后自动运行某个程序_Windows启动文件夹与任务计划【自动化】  百度浏览器无法安装扩展程序_百度浏览器插件安装失败原因解析  word页码灰色不能用如何解决  《海豚家》注销账号方法  苹果手机缓存怎么清除_苹果手机缓存如何清除iphone各版本操作步骤  sf漫画官网登录入口直达_sf漫画官方正版网址  mysql归档数据怎么导出为csv_mysql归档数据导出为csv文件的方法  Chart.js 教程:自定义插件实现图表与图例间距调整  Lar*el 中高效执行多列更新:单次查询实现  Win10关闭UAC用户账户控制的方法 Win10降低安全提示等级【技巧】  百度小说看书时如何翻页_百度小说手动翻页与自动翻页设置  TikTok私信无法发送表情怎么办 TikTok消息表情发送修复方法  J*a中的值传递到底指什么_值传递模型在参数传递中的真正含义说明  使用逻辑应用(Logic Apps)自动处理邮件附件中的XML到Excel  铁路12306官网登录入口 铁路12306在线购票官方平台  优化Google Charts Gauge:在数据库无数据时显示默认值  《书耽》更换手机号方法  我的世界游戏平台入口 我的世界官方官网直达链接  Win11怎么设置分辨率 Win11显示设置调整分辨率及刷新率修改  J*aScript:从子元素中批量移除特定CSS类  研招网官方网站正版登录网址_中国研究生招生信息网官网首页  vivo云服务一直提示空间不足怎么办 怎么办vivo云服务老是提示空间不足  《土豆雅思》修改密码方法  优化Flask模板中SQLAlchemy查询迭代标签:处理字符串空格问题  海棠阅读网页版_进入海棠网页版在线阅读中心  《花瓣》创建专辑方法  iPhone 15 Pro如何查看存储空间占用_iPhone 15 Pro存储空间查看教程  《美篇》取消会员自动续费方法  手机耗电快是什么原因 延长手机电池续航时间的设置方法【详解】  抖音作品被限流怎么办 抖音内容优化与流量恢复方法  Win10锁屏时间怎么设置 Win10调整自动锁屏时间方法  163邮箱网页版官方登录入口 163邮箱网页版访问页面  Excel如何快速合并单元格内容_Excel文本合并与函数操作技巧  Symfony路由参数转换器:实体存在性验证与错误处理策略  Mac hosts文件在哪里_Mac修改hosts文件详细教程  Python实时数据流中高效查找最大最小值  外媒评《燕云十六声》DIY载具新玩法:很像《塞尔达传说王国之泪》!  《领英》查看屏蔽名单方法 

 2025-11-17

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.