Scrapy爬虫:如何保存爬取的链接列表到文件并处理后续页面
解决Scrapy爬虫的两个核心问题
一、正确调用自定义页面处理函数
你之前尝试的parse_landing_page没生效,主要是代码里self.current未定义导致过滤逻辑失效,同时回调函数的调用逻辑也需要调整。下面是修正后的完整代码:
import scrapy from scrapy.linkextractors import LinkExtractor class MySpider(scrapy.Spider): name = "myspider" start_urls = ["https://books.toscrape.com/"] allowed_domains = ["books.toscrape.com"] # 限制爬取范围,避免意外爬取外部链接 def parse(self, response): to_avoid = ['tel','facebook','twitter','instagram','privacy','terms','contact','java','cookies','policies','google','mail'] le = LinkExtractor(deny=to_avoid, allow_domains=self.allowed_domains) ex_links = le.extract_links(response) for href in ex_links: url = response.urljoin(href.url) # 先调用自定义函数处理当前页面数据 yield response.follow(url, callback=self.parse_landing_page) # 继续跟进该页面下的子链接,维持爬取深度 yield response.follow(url, callback=self.parse) def parse_landing_page(self, response): # 这里编写你处理每个页面的业务逻辑,比如提取页面信息 page_info = { 'url': response.url, 'page_title': response.css('h1::text').get(default='无标题'), # 可添加更多需要提取的字段,比如价格、分类等 } # 输出数据,供后续导出使用 yield page_info
关键修正点:
- 新增
allowed_domains替代未定义的self.current,精准控制爬取范围 - 每个链接同时触发两个回调:
parse_landing_page处理页面数据,parse继续递归爬取子链接 - 去掉类变量
self.links,改用yield输出数据,符合Scrapy异步爬虫的设计规范,避免多线程环境下的数据异常
二、将爬取结果保存为CSV或JSON
Scrapy自带成熟的数据导出功能,无需手动编写文件操作代码,直接在命令行运行爬虫时指定输出文件即可:
保存为JSON格式
scrapy crawl myspider -o page_data.json
保存为CSV格式
scrapy crawl myspider -o page_data.csv
原理说明:
当你在parse_landing_page中yield字典(或Scrapy Item对象)时,Scrapy会自动收集所有输出数据,运行命令时通过-o参数指定文件路径和格式,就能直接导出结构化数据,比手动维护列表再写入文件更安全高效。
额外方案:手动保存链接列表(不推荐)
如果一定要用self.links存储链接,可以借助Scrapy的spider_closed信号,在爬虫结束时触发保存操作:
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy import signals from scrapy.signalmanager import dispatcher import json import csv class MySpider(scrapy.Spider): name = "myspider" start_urls = ["https://books.toscrape.com/"] allowed_domains = ["books.toscrape.com"] links = [] def __init__(self): # 绑定爬虫结束信号 dispatcher.connect(self.on_spider_closed, signal=signals.spider_closed) def parse(self, response): to_avoid = ['tel','facebook','twitter','instagram','privacy','terms','contact','java','cookies','policies','google','mail'] le = LinkExtractor(deny=to_avoid, allow_domains=self.allowed_domains) ex_links = le.extract_links(response) for href in ex_links: url = response.urljoin(href.url) if url not in self.links: # 去重 self.links.append(url) yield response.follow(url, callback=self.parse) yield response.follow(url, callback=self.parse_landing_page) def parse_landing_page(self, response): # 页面处理逻辑 pass def on_spider_closed(self, spider): # 保存为JSON with open('all_links.json', 'w', encoding='utf-8') as f: json.dump(self.links, f, indent=2, ensure_ascii=False) # 保存为CSV with open('all_links.csv', 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerow(['爬取链接']) for link in self.links: writer.writerow([link])
注意:这种方式仅适合小规模爬取,类变量self.links在多线程环境下可能出现数据丢失或重复,优先推荐使用Scrapy自带的导出功能。
内容的提问来源于stack exchange,提问作者mugndhn
相关产品推荐
相关产品推荐

