使用Scrapy实现增量式爬取方式的攻略如下:
一、为什么要使用增量式爬取方式
在许多情况下,我们需要经常更新我们的爬虫程序,以便及时获取网站上的新数据。但是,一些网站每天只能采集一定数量的数据,可能由于网站资源受到限制或自身能力问题。在这种情况下,为了提高爬取的效率,我们可以使用增量式爬取方式。
相比于全量爬取,增量式爬取能够只抓取最新的数据,只需爬取网站上新增的或更新的内容,大大减少了爬取的时间和资源占用,提高了爬虫程序的效率和可靠性。
二、如何实现增量式爬取
Scrapy提供了一些内置的机制和扩展,可以轻松实现增量式爬取。
以下是实现增量式爬取的基本步骤:
1. 使用唯一标识符进行去重
在Scrapy中,去重的机制是通过使用唯一标识符来识别和排除重复的URL。通过在item中定义一组需要去重的字段(如ID),Scrapy可以根据这些字段的值自动判断是否需要对该URL进行处理。
from scrapy.item import Item, Field
class MyItem(Item):
id = Field()
title = Field()
content = Field()
在pipeline中的实现:
from scrapy.exceptions import DropItem
class DuplicatePipeline(object):
def __init__(self):
self.ids_seen = set()
def process_item(self, item, spider):
if item['id'] in self.ids_seen:
raise DropItem("Duplicate item found: %s" % item)
else:
self.ids_seen.add(item['id'])
return item
2. 更新爬虫程序
当网站上出现新内容时,我们需要更新爬虫程序,以确保它可以检测到并采集新数据。更新爬虫程序需要对以下几个部分进行改进:
- 修改start_urls或者start_requests方法以爬取新的页面
class MySpider(scrapy.Spider):
name = 'myspider'
def start_requests(self):
urls = [
'http://www.example.com/page1.html',
'http://www.example.com/page2.html',
...
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
...
- 确保Scrapy爬虫程序在下一次运行时能够继续爬取上次停止的位置
class MySpider(scrapy.Spider):
name = 'myspider'
custom_settings = {
'JOBDIR': '/path/to/save/job',
}
def parse(self, response):
pass
- 中间件或pipeline中添加增量式爬取相关的处理逻辑
class MyPipeline(object):
def process_item(self, item, spider):
if is_new(item):
# 处理新数据
pass
else:
# 忽略旧数据
raise DropItem()
return item
3. 使用增量式爬虫框架
为了更加便捷地实现增量式爬取,可以使用一些Scrapy的增量式爬虫框架,例如:scrapy_redis、scrapy-splash、scrapy-redis-bloomfilter、scrapy-deltafetch等。这些框架都提供了一些针对增量式爬取的优化和扩展,可以帮助我们更加轻松地实现增量式爬取。
例如,使用scrapy-redis-bloomfilter可以避免过多的重复URL,保证在多个爬虫同时爬取同一网站时不会重复抓取同一个URL:
import redis
from scrapy_redis_bloomfilter import bloomfilter
class MySpider(scrapy.Spider):
name = 'myspider'
redis_key = 'myspider:start_urls'
redis_bf_key = 'myspider:dupefilter'
def __init__(self, *args, **kwargs):
super(MySpider, self).__init__(*args, **kwargs)
self.server = redis.from_url(self.settings.get('REDIS_URL'))
self.bf = bloomfilter.BloomFilter(server=self.server, key=self.redis_bf_key)
def start_requests(self):
if not self.bf:
self.bf.init()
for url in self.start_urls:
if not self.bf.is_exist(url):
yield scrapy.Request(url, callback=self.parse)
def parse(self, response):
...
if not self.bf.is_exist(url):
self.bf.add(url)
三、示例说明
以下是两个实现增量式爬取的示例:
示例1:使用scrapy-redis实现增量式爬取
scrapy-redis是一个Scrapy的Redis分布式组件,支持多个Scrapy进程并行爬取相同的网站。使用该组件,可以方便地实现增量式爬取。
- 安装scrapy-redis:
pip install scrapy-redis
- 在settings.py中添加配置:
# 启用Redis调度
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 启用去重组件
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 如果你的redis服务器不在本地,请设置REDIS_URL
REDIS_URL = 'redis://localhost:6379'
- 在spiders中修改爬虫程序:
from scrapy_redis.spiders import RedisSpider
class MySpider(RedisSpider):
name = 'myspider'
redis_key = 'myspider:start_urls'
def parse(self, response):
...
示例2:使用scrapy-splash实现增量式爬取
scrapy-splash是一个Scrapy的JavaScript渲染插件,可以在爬虫中使用Splash来爬取JavaScript生成的内容。
- 安装scrapy-splash:
pip install scrapy-splash
- 在settings.py中添加配置:
# 启用Splash中间件
DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashMiddleware': 725,
}
# 将Splash请求拒截器的优先级设置为高于其他请求拒截器
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
# 设置Splash服务地址
SPLASH_URL = 'http://localhost:8050'
# 启用增量式爬取中间件
SPIDER_MIDDLEWARES = {
'myspider.middlewares.IncrementMiddleware': 543,
}
# 开启缓存
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
- 在middlewares中添加增量式爬取中间件:
import scrapy_splash
from scrapy.exceptions import IgnoreRequest
class IncrementMiddleware(object):
def __init__(self):
self.urls_seen = set()
def process_request(self, request, spider):
# 检查URL是否已经被处理过
if request.url in self.urls_seen:
raise IgnoreRequest()
else:
self.urls_seen.add(request.url)
return None
def process_response(self, request, response, spider):
# 处理新数据
if response.status == 200:
# 过滤已处理过的URL
self.urls_seen.add(request.url)
# 确定是否存在下一页
next = response.xpath("//a[@class='next-page']/@href")
if next:
next_url = next.get()
# 构造新的请求
request = scrapy_splash.SplashRequest(
next_url, callback=self.parse, args={'wait': 0.5}
)
request.meta['handle_httpstatus_all'] = True
return request
else:
return response
else:
return response
def process_exception(self, request, exception, spider):
return None
结论
增量式爬取是一个非常有用的技术,在实际的爬虫开发中应该尽可能地采用它,提高爬虫程序的效率和可靠性。Scrapy提供了丰富的内置机制和扩展,可以方便地实现增量式爬取,同时还有一些优秀的增量式爬虫框架可以使用。通过这些工具和方法的结合,可以轻松实现高效、稳定、持续的爬虫程序运行方式。