一:基本概念
1:什么是分布式爬虫
--分布式爬虫,就是用多台电脑,同时运行一个爬虫文件。进行数据的爬去
2:原生的scrapy不能实现分布式爬取原因
--1:管道不能共享
--2:调度器不能共享
3:使用scrapy-redis组件进行分布式爬虫,scrapy-redis专门为scrapy开发的一套组件。该组件可以使scrapy进行分布式爬虫
--:准备工作
--下载scrapy-redis pip install scrapy-redis
--修改redis.conf配置文件
--注释掉 bind 127.0.0.1 (不注释,会默认redis只能被本机链接)
--修改 protected-mode no 关闭保护模式
二:基于RedisCrawlSpider类的分布式爬虫
1:创建工程
--scrapy startproject 工程名
--scrapy genspider -t crawl 爬虫文件名 起始url
2:导入RedisCrawlSpider
--修改爬虫文件基于该类的源文件
# -*- coding: utf-8 -*- import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from scrapy_redis import RedisCrawlSpider from redisPRO.items import RedisproItem #爬去糗事百科糗图的地址 class QiubaiSpider(CrawlSpider): name = 'qiubai' # allowed_domains = ['www.baidu'] # start_urls = ['http://www.qiushibaike.com/'] #调度器名称,和start_urls 作用一样 redis_key = 'qiubaispider' link = LinkExtractor(allow=r'/pic/paged+') rules = ( Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True), ) def parse_item(self, response): div_list=response.xpath('//div[@id="content-left"]') for div in div_list: img_url = "https"+div.xpath('.//div[@class="thumb"/a/img/@src]').extract_fist() item = RedisproItem() item['img_url']=img_url yield item
3:修改setting.py配置文件,将管道和调度器设置成scrapy-redis组件中
--修改管道
#修改管道 ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400, }
--修改调度器
#使用 scrapy_redis去重队列。 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" #使用 scrapy_redis调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 是否保持调度器队列,断点续爬 SCHEDULER_PERSIST = True
--设置redis ip和端口
#当redis服务器不在本机,需要进行redis配置 REDIS_HOST='192.168.10.11' REDIS_PORT=6379
4:进入多台电脑文件目录下,执行爬虫文件
--scrapy runspider qiubai.py #此时所有电脑均处于监听状态
5:开启redis客户端,将起始的url放入队列中。(爬虫电脑监听成功后,开始运行爬虫文件,并且把结构存储到redis数据库中)
--lpush redis-key(对列名称) 起始url
三:基于RedisSpider类的分布式爬虫
案例:基于网易新闻文字模块的爬虫
--ua池 #对请求头进行伪装
--代理池 #对请求ip进行批量伪装
--selenium #scrapy 使用 selenium模块
1:创建工程 (略)
2:编写爬虫文件
a):导入RedisSpider类 from scrapy_redis.spiders import RedisSpider
b):修改爬虫父类,继承RedisSpider类
class WangyiSpider(RedisSpider):
c):添加 redis_key属性
redis_key='wangyi'
d)编写爬虫文件逻辑
# -*- coding: utf-8 -*- import scrapy from wangyiPro.items import WangyiproItem from selenium import webdriver from scrapy_redis.spiders import RedisSpider class WangyiSpider(RedisSpider): name = 'wangyi' # allowed_domains = ['www.wangyi.com'] # start_urls = ['https://news.163.com/'] redis_key='wangyi' def __init__(self): self.bro = webdriver.Chrome(executable='/Users/yingjianping/Desktop/chromedriver') def parse(self, response): lis=response.xpath('//div[@class="ns_area list"]/ul/li') #只爬取部分模块 indexs=[3,4,6,7] li_list= [lis[index] for index in indexs] for li in li_list: url = li.xpath('./a/@href').extract_fist() title =li.xpath('./a/text()').extract_fist() #对获取的url 进行页面解析,并获取数据信息 yield scrapy.Request(url=url,callback=self.secondParse,meta={'titel':title}) def get_content(self,response): item = response.meta['item'] content_list = response.xpath('//div[@class="post_text"]/p/text()').extract() content = ''.join(content_list) item['content']=content yield item def secondParse(self,response): #动态获取数据的,无法 div_list=response.xpath('//div[@class="ns_area top_news clearfix"]/') for div in div_list: head = div.xpath('div[@class="news_title"]/h3/a/text()').extract_fist() url = div.xpath('div[@class="news_title"]/h3/a/@href').extract_fist() img_url =div.xpath('./a/img/src').extract_fist() tag ="".join(div.xpath('.//div[@class="new_tag"]//text()')) item=WangyiproItem() item['img_url']=img_url item['url'] = url item['tag'] = tag item['head'] = head item['title']=response.meta['title'] yield scrapy.Request(url=url,callback=self.get_content,mate={'item':item}) def close(self): self.bro.quit()
e):修改settings文件
--配置redis,ip,端口,密码(有密码设置密码)
#当redis服务器不在本机,需要进行redis配置 REDIS_HOST='192.168.10.11' REDIS_PORT=6379 #REDIS_PARAMS={'password':123456} #有密码配置密码
--配置管道文件
ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400, }
--配置队列
#使用 scrapy_redis去重队列。 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" #使用 scrapy_redis调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 是否保持调度器队列,断点续爬 SCHEDULER_PERSIST = True
--文件执行 scrapy runspider wangyi.py
--向redis客户端放入一个起始url
lpush wangyi www.news.163.com
3:selenium的使用
a):使用场景
--许多网站页面,部分数据是通过动态获取的,在原本的scrapy中获取的请求,不能获取页面内获取动态页面的请求
b):使用原理
--在下载器将下载的页面提交给spider文件中,因为获取不到动态页面数据。如果在下载器提交给spider文件过程中,通过selenium修改下载器提交结果,便可以获得完整数据。
c):使用步骤
--在爬虫文件中导入webdriver类
from selenium import webdriver
--在爬虫实列中,实列化浏览器对象
def __init__(self): self.bro = webdriver.Chrome(executable='/Users/yingjianping/Desktop/chromedriver')
--close方法中关闭浏览器驱动器
def close(self): self.bro.quit()
--在middlewares.py文件中 导入from scrapy.http import HtmlResponse 用于响应伪装。
--在middlewares.py文件中重写 RedisproDownloaderMiddleware(object)方法中的proess_response()方法
class RedisproDownloaderMiddleware(object): #可以拦截下载器传递给spider的相应对象 #request:当前相应对象对应的请求对象 #response:拦截到响应对象 #spider 当前爬虫文件对应的实例化对象, 比如获取浏览器对象 spider.bro #没发一次请求,调用一次该方法 def process_response(self, request, response, spider): #修改响应页面数据 #1:通过selenium发送请求,去除首页 if request.url not in ['https://news.163.com/']: spider.bro.get(url =request.url) #滑动页面使页面加载完全 js_code ='windon.scrollTo(0,document.body.scrollHeight)' spider.pro.execute_script(js_code) time.sleep(3) page_text=spider.bro.page_sourse return HtmlResponse(url=spider.bro.current_url,body=page_text,enconding='utf-8') else: return response
--在setting.py文件开启中间件
DOWNLOADER_MIDDLEWARES = { 'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543, }
4:ua池的使用
--伪装请求头,防止被识别禁止访问
--在middleware.py文件中导入 from scrapy.contrib.downloadermiddleware.useragent import UserAgentMiddleware
--自定义中间件类,继承UserAgentMiddleware类,重写proess_request()方法
import random user_agents = [ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)", "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)", "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0", "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20", "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52", ] class RandomUserAgent(UserAgentMiddleware): def process_request(self, request, spider): ua =random.choice(user_agents) request.headers.setdefault('User-Agent',ua)
--setting.py设置开启管道
DOWNLOADER_MIDDLEWARES = { 'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543, 'wangyiPro.middlewares.RandomUserAgent': 542, 'wangyiPro.middlewares.RandomProxy': 541, }
5:代理池(和ua池使用方式类型)
--自定义中间件类,继承object类,重写proess_request()方法
#注意请求头 我这里写死了 ip_list=[ 'http:39.134.66.72:8080', 'http:80.68.121.217:34392' ] class RandomProxy(object): def process_request(self, request, spider): request.meta['proxy']=random.choice(ip_list)
--settings.py文件中开启中间件。