新闻中心
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进行多次请求和解析,从而产生大量重复数据。
Zyro AI Background Remover
Zyro推出的AI图片背景移除工具
145
查看详情
错误示例:
# ... (部分代码省略) ...
# 错误:在不必要的情况下使用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爬虫,特别是在处理多层内部链接时,需要注意以下几点:
- 采用顺序分页: 避免在每个页面都重新发现并请求所有分页链接,只跟随“下一页”链接。
- 谨慎使用dont_filter=True: 除非有充分理由,否则应依赖Scrapy的去重机制,避免不必要的重复请求。
- 优化Item提交策略: 尽可能在一个回调函数中收集所有相关数据并提交Item。如果必须分多步收集,确保Item只在数据完全收集后yield一次,并妥善管理meta中传递的数据状态。
遵循这些最佳实践,可以显著提高Scrapy爬虫的性能、准确性和健壮性,从而更有效地完成数据抓取任务。
以上就是Scrapy多层内部链接爬取优化:避免重复与数据不完整的详细内容,更多请关注其它相关文章!
# 是在
# 武穴企业网站推广怎么做
# 云南seo营销推荐
# 高明seo优化
# 广州seo排名制作公司
# 承德淘宝网站推广
# 抚顺网站优化企业招聘信息
# 小店区关键词排名意义
# 黑龙江seo推广网站
# 如何开礼品网站店铺推广
# seo招商哪里好
# 如何使用
# 面上
# 而不
# 跳过
# css
# 这会
# 下一页
# 不完整
# 分页
# 回调
# a标签
# cos
# 常见问题
# 爬虫
# win
# ai
# 工具
# 回调函数
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
Golang如何使用bytes.Split分割字节切片_Golang bytes切片分割方法
Log4j Console Appender性能瓶颈与高并发优化策略
中兴Axon42Ultra怎样在文件App筛图_iPhone中兴Axon42Ultra文件App筛图【图片筛选】
c++如何使用std::memory_order控制原子操作顺序_c++ C++11内存模型详解
双系统安装时,如何设置默认启动系统? msconfig命令了解一下!
苹果手机如何防止被恶意App追踪
Golang如何通过reflect获取匿名字段方法_Golang reflect匿名字段方法访问技巧
如何使用Node.js csv 包按条件移除含空字段的CSV记录
Golang如何优雅处理error_Golang error处理最佳实践总结
word中如何让数字纵向排列_Word数字纵向排列方法
CSS Flexbox如何实现多行排列_flex-wrap wrap自动换行显示
CSS Flexbox与媒体查询:实现响应式布局中元素的并排与堆叠
Kafka Streams中基于消息头条件过滤消息的实现指南
顺丰快递查询系统 官方正版查询入口
QQ邮箱官方网站登录入口_QQ邮箱网页版在线使用
css滚动区域卡顿如何改善_css滚动问题用will-change优化渲染
Win10磁盘清理工具在哪 Win10打开并使用磁盘清理【教程】
Steam官网入口直达 Steam注册及登录步骤
Fabric Mod开发:在1.19.3+版本中正确添加自定义物品并管理物品组
Python中如何避免重复条件判断:利用数据结构实现动态逻辑
妖精动漫免费平台 妖精动漫官网资源观看网址
一加Ace 6T实拍样张首次公布!李杰:主摄实力完全看齐4K档性能旗舰
Django表单提交验证失败后保持字段值不刷新
html怎么运行外部js文件中的函数_运html外js文件函数法【技巧】
Win11截图该按哪些键 Win11截屏完整流程解析【教程】
可靠CSGO开箱平台解析 CSGO开箱网合集
在React函数组件中利用原生HTML5进行邮箱地址验证
html5 app怎么运行环境_配html5 app运行环境【教程】
从J*aScript对象中精确提取指定属性的教程
抖音隐秘迷城小游戏入口_ 抖音冒险解谜小游戏秒玩
聚水潭ERP登录页面入口 聚水潭ERP官网登录界面
Angular中单选按钮的正确使用与常见陷阱解析
如何在Python中使用Optional类型处理可变对象并避免Pylint警告
qq游戏网页版直接玩_qq游戏免下载快速入口
Win10系统怎么查看已安装更新_Win10卸载有问题的更新补丁
Mac终端命令大全_Mac常用Terminal指令速查
React/Next.js中实现列表项的动态选择与移动
反效果?《战地6》免费试玩开启后玩家数不升反降
动漫花园资源网使用步骤_动漫花园资源网下载流程
QQ邮箱登录平台入口 QQ邮箱网页版邮箱官方入口
CSS图片焦点样式实现教程:理解与应用tabindex属性
cad如何更改注释性对象的比例_cad注释性比例调整方法
Descript怎样用AI剪辑自动去噪_Descript用AI剪辑自动去噪【自动降噪】
LINQ to XML为何解析失败? 深入理解C# XDocument的异常处理
印象笔记如何设提醒任务防漏执行_印象笔记设提醒任务防漏执行【任务提醒】
漫蛙官网正版漫画入口 漫蛙2官方网页登录地址
机构:以往存储涨价周期小米利润率实际上有所改善 能转嫁给消费者等
深入理解J*a编译器的兼容性选项:从-source到--release
天猫2025双十一0点秒杀攻略 天猫爆款抢购时间
wps文字怎么插入目录并自动更新_wps文字如何插入目录并自动更新方法


2025-11-17
浏览次数:次
返回列表
ider):
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