1 scrapy全站爬取
1.1 全站爬取简介
CrawlSpider :全站数据爬虫的方式,它是一个类,属于Spider的子类 如果不使用CrawlSpider ,那么就相当于基于spider ,手动发送请求,太不方便 基于CrawlSpider 可以很方便地进行全站数据爬取
1.2 CrawlSpider
1.2.1 基本讲解
基本步骤:
- 创建一个工程:
scrapy startproject ProjectName - 切换到爬虫工程中后,创建爬虫文件:
scrapy genspider -t crawl xxx www.xxx.com
使用CrawlSpider 和spider 产生的爬虫文件除了继承类不一样外还有一个rules 的规则解析器
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
在rules 规则解析器内有一个链接提取器LinkExtractor(allow=r'Items/') ,callback 是规则解析器指定的解析方法,follow 是指爬取页面内可见部分页面还是全部
页面内可见部分页面如下: 链接提取器作用:根据指定的规则allow=r'Items/' 进行指定的链接的提取 规则解析器作用:把链接提取器提取到的链接进行指定规则callback='parse_item' 的解析操作 follow 作用:True 可以把 链接提取器 继续作用到 链接提取器提取到的链接 所对应的 页面 中,False 爬取页面内可见部分页面
1.2.2 使用CrawlSpider
1.2.2.1 爬虫文件
使用CrawlSpider 生成爬虫文件时,在规则解析器rules 里面添加正则表达式进而发起请求,如果要一个请求内需要再次发起请求,就需要在rules 中添加链接请求并指定对应的解析方法
注意 :xpath 中最好不要出现tbody 标签
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from sunPro.items import SunproItem,DetailItem
class SunSpider(CrawlSpider):
name = 'sun'
start_urls = ['http://dk.test.com/mail/?ac=list&tid=1']
rules = (
Rule(LinkExtractor(allow=r'ac=list&tid=1&order=1&page=\d+'), callback='parse_item', follow=False),
Rule(LinkExtractor(allow=r'ct=index&ac=detail&id=\d+'), callback='parse_detail', follow=False),
)
def parse_item(self, response):
tr_list=response.xpath('/html/body/table[2]//tr/td/table//tr[3]/td/table//tr/td[1]/table//tr/td/table//tr[1]/td/div/table//tr[@bgcolor="#FFFFFF"]')
for tr in tr_list:
news_num = tr.xpath('./td[1]/text()').extract_first()
news_title = tr.xpath('./td[2]/a/text()').extract_first()
print(news_num,news_title)
""" item=SunproItem()
item['news_title']=news_title
item['news_num']=news_num
yield item """
def parse_detail(self,response):
news_id=response.xpath('/html/body/table[2]//tr/td/table//tr[3]/td/table//tr/td[1]/table//tr[1]/td/table//tr/td/table//tr[2]/td/table//tr[1]/td[1]/span/text()').extract_first()
news_content=response.xpath('/html/body/table[2]//tr/td/table//tr[3]/td/table//tr/td[1]/table//tr[1]/td/table//tr/td/table//tr[3]/td/table//tr[1]/td/table//tr[2]/td//text()').extract()
news_content=''.join(news_content)
item=DetailItem()
item['news_id']=news_id
item['news_content']=news_content
yield item
1.2.2.2 items.py文件
由于不能发送请求时传参因此,需要两个item类文件
import scrapy
class SunproItem(scrapy.Item):
news_title=scrapy.Field()
news_num=scrapy.Field()
class DetailItem(scrapy.Item):
news_id=scrapy.Field()
news_content=scrapy.Field()
2 分布式爬虫
2.1 分布式爬虫概念
分布式爬虫:需要搭建一个分布式的集群,让其对一组资源进行分布联合爬取,主要是为了提升爬取数据效率
2.2 环境安装
安装一个scrapy-redis 的组件:pip install scrapy-redis ,由于原生的scrapy 不可以失效分布式爬虫,必须让scrapy 结合scrapy-redis 组件一起实现分布式爬虫 那么为什么原生scrapy 不可以实现分布式?
- 调度器不可以被分布式集群共享
- 管道不可以被分布式集群共享
但是scrapy-redis 组件可以提供共享的管道和调度器
2.3 使用方法
2.3.1 CrawlSpider配置
基本使用步骤:
- 创建基于
CrawlSpider 的爬虫文件,修改爬虫文件
导包:from scrapy_redis.spiders import RedisCrawlSpider 把start_urls 和allowed_domains 注释掉 添加一个新属性:redis_key='sun' 作为可以被共享的调度器队列名称 编写数据解析相关操作 把当前父类修改为RedisCrawlSpider
- 修改配置文件
settings.py ,不要开启项目自带的pipelines 不然还是走的原来的管道,需要指定共享的管道RedisPipeline ,还要指定调度器
指定管道
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline':400
}
指定调度器
DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
SCHEDULER='scrapy_redis.scheduler.Scheduler'
SCHEDULER_PERSIST = True
指定redis地址和端口
REDIS_HOST='127.0.0.1'
REDIS_PORT='6379'
2.3.2 redis相关配置
把redis.windows-server.conf 文件修改把bind 127.0.0.1 给注释掉,由于要把爬到的数据库储存到不同地方,因此不要绑定本地 关闭保护模式protected-mode yes 修改为protected-mode no ,如果开启了保护模式,那么其他客户端只能读取redis而不能写入
2.3.3 启动工程
分布式爬虫启动和scrapy工程不同,需要定位到爬虫文件.py 目录内,执行scrapy runspider xxx.py 工程启动后在redis客户端中向redis添加调度队列:lpush sun www.xxx.com (由于之前写过redis_key='sun’的共享调度属性)
3 增量式爬虫
3.1 概念讲解
增量式爬虫:检测网站数据更新的情况,只会爬取网站最新出来的数据 还是基于CrawlSpider 获取其他页码链接处理的,每次爬取时,都会对已经爬取的数据进行比较,若爬取过了,就不再爬取
3.2 使用
3.2.1 爬虫文件
主要通过redis来判断是否已经存储过
from redis import Redis
from sunPro.items import SunproItem
class SunSpider(CrawlSpider):
name = 'sun'
start_urls = ['http://www.xxx.com/']
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
conn = Redis(host='127.0.0.1',port=6379)
def parse_item(self, response):
li_list=response.xpath('xxxxx');
for li in li_list:
detail_url=li.xpath('xxxxxxxxxx').extract_first()
ex=self.conn.sadd('urls',detail_url)
if ex==1:
print('该url没有爬取过,可以进行数据爬取')
yield scrapy.Request(url=detail_url,callback=self.parse_detail)
else:
print('数据没有更新,暂无新数据可爬取')
def parse_detail(self,response):
item = SunproItem()
item['name']=response.xpath('xxxxxxxxxxxxx').extract()
3.2.2 管道文件
在管道文件中获取redis
class SunproPipeline:
conn=None
def open_spider(self,spider):
self.conn=spider.conn
def process_item(self, item, spider):
dict={
'name':item['name']
}
self.conn.lpush('test',dict)
return item
def close_spider(self, spider):
pass
|