无法访问派生类新方法及网页爬取存入Solr索引技术咨询
问题2:爬取网页存入Solr的代码检查与优化建议
先看你给出的代码片段,我挑几个关键问题和优化点说:
现有代码的潜在问题
crawledLinks = [] solr = pysolr.Solr('some url', timeout=10) class MySpider(Spider): name = "tutsplus" start_urls = ["some url"] allowed_domains = ["some domain"] custom_settings = { 'CONCURRENT_REQUESTS': 100, 'CONCURRENT_REQUESTS_PER_DOMAIN': 100, 'DEPTH_LIMIT': 100, 'LOG_ENABLED': True, } def parse(self, response): links = response.xpath('//a/@href').extract() current_url = response.url ...
- 全局变量的线程安全问题:
crawledLinks是全局列表,Scrapy是多线程异步框架,多个爬虫线程同时操作这个列表会导致数据混乱(比如重复添加、丢失数据)。Solr实例全局化也有问题,pysolr的连接不是线程安全的,多线程共享可能引发连接异常。 - 并发设置过高:
CONCURRENT_REQUESTS和CONCURRENT_REQUESTS_PER_DOMAIN都设为100,这会瞬间给目标网站发大量请求,大概率会被封禁IP,甚至直接把对方服务器打崩,非常不友好。 - 链接处理不完整:
links = response.xpath('//a/@href').extract()拿到的是相对URL,需要转换成绝对URL,而且没有去重逻辑,会导致重复爬取。 - 缺少异常处理:Solr的操作(比如添加文档)很容易因为网络问题、格式错误失败,但代码里没有任何异常捕获,会直接导致爬虫崩溃。
- 日志设置太简单:
LOG_ENABLED: True只是开启日志,但没有设置日志级别和输出格式,出问题的时候很难排查。
优化后的代码示例
from scrapy.spiders import Spider from scrapy.utils.url import urljoin import pysolr from scrapy.exceptions import CloseSpider import scrapy class MySpider(Spider): name = "tutsplus" start_urls = ["some url"] allowed_domains = ["some domain"] custom_settings = { 'CONCURRENT_REQUESTS': 10, # 降低并发,友好爬取 'CONCURRENT_REQUESTS_PER_DOMAIN': 5, 'DEPTH_LIMIT': 5, # 深度不用设100,除非你真的需要爬这么深 'LOG_LEVEL': 'INFO', # 明确日志级别 'LOG_FORMAT': '%(asctime)s - %(name)s - %(levelname)s - %(message)s', 'ROBOTSTXT_OBEY': True, # 遵守robots协议,避免法律风险 'DOWNLOAD_DELAY': 1, # 增加请求间隔,降低反爬风险 } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 把Solr实例放到类属性里,每个爬虫实例单独初始化 try: self.solr = pysolr.Solr('some url', timeout=10) except Exception as e: self.logger.error(f"Failed to connect to Solr: {str(e)}") raise CloseSpider("Solr connection failed") # 用集合维护已爬URL,比列表查询更快 self.crawled_urls = set() # 批量提交的缓存,攒够20条再提交Solr self.solr_batch = [] self.batch_size = 20 def parse(self, response): # 跳过已爬取的URL if response.url in self.crawled_urls: return self.crawled_urls.add(response.url) # 提取页面数据(示例:标题、内容、URL) item = { 'url': response.url, 'title': response.xpath('//title/text()').get(default=''), 'content': ' '.join(response.xpath('//p/text()').getall()).strip() } # 加入批量缓存,达到阈值就提交 self.solr_batch.append(item) if len(self.solr_batch) >= self.batch_size: self._submit_to_solr() # 处理链接:转绝对URL + 过滤允许域名 + 去重 links = response.xpath('//a/@href').getall() domain_prefixes = (f'http://{self.allowed_domains[0]}', f'https://{self.allowed_domains[0]}') for link in links: absolute_url = urljoin(response.url, link) if absolute_url.startswith(domain_prefixes) and absolute_url not in self.crawled_urls: yield scrapy.Request(absolute_url, callback=self.parse) def _submit_to_solr(self): """批量提交数据到Solr的私有方法""" try: self.solr.add(self.solr_batch) self.logger.info(f"Successfully indexed {len(self.solr_batch)} documents") self.solr_batch.clear() except Exception as e: self.logger.error(f"Failed to submit batch to Solr: {str(e)}") def closed(self, reason): """爬虫关闭时提交剩余的缓存数据""" if self.solr_batch: self._submit_to_solr()
额外优化建议
- 增量爬取策略:给每个Solr文档添加
last_modified字段,爬取时对比页面的Last-Modified响应头,只更新有变化的内容,避免重复索引。 - 数据格式验证:存入Solr前验证必填字段是否存在、格式是否符合要求(比如URL合法性、内容长度),可以用Scrapy的Item Pipeline做统一校验。
- Solr连接池配置:如果爬虫规模较大,可以给pysolr配置连接池(比如
pysolr.Solr(..., pool_kwargs={'maxconnections': 10})),提升连接复用效率。 - 代理IP配置:如果目标网站反爬严格,可以添加Scrapy代理中间件,切换不同IP进行爬取,降低封禁风险。
内容的提问来源于stack exchange,提问作者Nilesh Guria
相关产品推荐
相关产品推荐

