一、分布式爬蟲介紹
分布式爬蟲概念:多台機器上執行同一個爬蟲程序,實現網站數據的分布爬取。
1、原生的Scrapy無法實現分布式爬蟲的原因?
- 調度器無法在多台機器間共享:因為多台機器上部署的scrapy會各自擁有各自的調度器,這樣就使得多台機器無法分配start_urls列表中的url。
- 管道無法給多台機器共享:多台機器爬取到的數據無法通過同一個管道對數據進行統一的數據持久出存儲。
2、scrapy-redis組件
scrapy-redis是專門為scrapy框架開發的一套組件。該組件可以解決上述兩個問題,讓Scrapy實現分布式。
組件安裝:
$ pip3 intall scrapy-redis
二、基於RedisCrawlSpider的分布式爬取
1、redis配置和啟動
(1)對redis配置文件redis.conf進行配置
- 注釋該行:bind 127.0.0.1,表示可以讓其他ip訪問redis
- 將yes該為no:protected-mode no,表示可以讓其他ip操作redis
# 不注釋時,只允許本機的客戶端連接
# bind 127.0.0.1
# yes改為no,關閉redis的保護模式,客戶端可以對服務器進行讀寫操作
protected-mode no
(2)基於配置文件開啟redis服務器
# MAC/Linux
$ pwd
/Users/hqs/redis-5.0.2
$ src/redis-server redis.conf
# windows
$ redis-server.exe redis-windows.conf
2、項目和爬蟲創建
$ scrapy startproject redisPro
$ cd redisPro/
$ scrapy genspider -t crawl qiubai www.qiushibaike.com/pic/
這里運用CrawlSpider創建爬蟲文件爬取全站圖片數據。
3、爬蟲文件編寫
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis.spiders import RedisCrawlSpider
from redisPro.items import RedisproItem # 引入items類
class QiubaiSpider(RedisCrawlSpider):
name = 'qiubai'
# allowed_domains = ['www.qiushibaike.com/pic/']
# start_urls = ['http://www.qiushibaike.com/pic/']
# redis_key表示調度器中的隊列(將要爬取的頁面數據對應的url都需要放置到調度器隊列中)
redis_key = 'qiubaispider' # 自定義調度器隊列的名稱,該行代碼表示的含義和start_urls一樣
# 鏈接提取器
# 從頁面提取鏈接頁碼的url:/pic/page/2?s=5147462
# s后對應的參數值會在瀏覽器刷新時動態變化, 正則忽略s參數的變化進行提取
link = LinkExtractor(allow=r'/pic/page/\d+')
# 規則解析器
rules = (
Rule(link, callback='parse_item', follow=True),
)
def parse_item(self, response):
"""解析操作:提取圖片鏈接"""
div_list = response.xpath('//div[@id="content-left"]/div') # 獲取divz列表
for div in div_list: # 遍歷div列表
# 解析提取div中圖片url,保存在img標簽的src屬性下
img_url = "https:" + div.xpath('.//div[@class="thumb"]/a/img/@src').extract_first()
# 實例化items對象
item = RedisproItem()
item['img_url'] = img_url # 封裝items
yield item
(1)引入scrapy-redis中的模塊
在爬蟲文件中要導入RedisCrawlSpider類,然后將爬蟲文件修改成基於該類的源文件。
from scrapy_redis.spiders import RedisCrawlSpider
# class QiubaiSpider(CrawlSpider):
class QiubaiSpider(RedisCrawlSpider):
(2)聲明redis_key屬性
redis_key表示調度器中的隊列(將要爬取的頁面數據對應的url都需要放置到調度器隊列中)。
redis_key自定義調度器隊列的名稱,表示的含義和start_urls一樣,因此需要將原來的start_url注釋掉。
class QiubaiSpider(RedisCrawlSpider):
name = 'qiubai'
# allowed_domains = ['www.qiushibaike.com/pic/']
# start_urls = ['http://www.qiushibaike.com/pic//']
# redis_key表示調度器中的隊列(將要爬取的頁面數據對應的url都需要放置到調度器隊列中)
redis_key = 'qiubaispider' # 自定義調度器隊列的名稱,該行代碼表示的含義和start_urls一樣
(3)鏈接提取器和規則解析器定義
class QiubaiSpider(RedisCrawlSpider)
"""代碼省略"""
# 鏈接提取器
# 從頁面提取鏈接頁碼的url:/pic/page/2?s=5147462
# s后對應的參數值會在瀏覽器刷新時動態變化, 正則忽略s參數的變化進行提取
link = LinkExtractor(allow=r'/pic/page/\d+')
# 規則解析器
rules = (
Rule(link, callback='parse_item', follow=True),
)
(4)items.py編寫和爬蟲引入調用
import scrapy
class RedisproItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
img_url =scrapy.Field()
4、settings.py配置修改
(1)使用scrapy-redis組件中封裝好的管道
使用scrapy-redis組件中封裝好的可以被共享的管道。
可以將每台機器爬取到的數據存儲通過該管道存儲到redis數據庫中,從而實現了多台機器的管道共享。
# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
# 'redisPro.pipelines.RedisproPipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400,
}
(2)使用scrapy-redis組件中封裝好的調度器
使用scrapy-redis組件中封裝好的調度器,將所有的url存儲到該指定的調度器中,從而實現了多台機器的調度器共享。
以下代碼可在settings.py中任意位置粘貼:
# 使用scrapy-redis組件的去重隊列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis組件自己的調度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 核心配置
# 是否允許暫停
SCHEDULER_PERSIST = True # 值為True表示:宕機恢復服務時,從宕機的那個地方開始爬取,不用從頭開始
(3)爬蟲程序不在redis本機時,指定redis地址
管道默認會連接且將數據存儲到本機的redis服務中,如果想要連接存儲到其他redis服務中需要在settings.py中進行如下配置
# 如果redis服務器不在自己本機,則需要做如下配置
REDIS_HOST = '192.168.31.31' # redis服務的ip地址
REDIS_PORT = 6379
# 可選配置
# REDIS_ENCODING = 'utf-8'
# REDIS_PARAMS = {'password':'123456'}
(4)修改ua和關閉robots協議
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' # 偽裝請求載體身份
# Obey robots.txt rules
ROBOTSTXT_OBEY = False # 不遵從門戶網站robots協議,避免某些信息爬取不到
5、執行爬蟲程序
執行爬蟲程序的方式與之前不同。
格式:lpush 隊列名稱(redis-key) 起始url
$ pwd
/Users/hqs/ScrapyProjects/redisPro/redisPro/spiders
$ scrapy runspider qiubai.py
.......
2018-12-08 12:04:34 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
然后需要在redis客戶端中,將起始url扔到調度器隊列中:
$ src/redis-cli
127.0.0.1:6379> lpush qiubaispider https://www.qiushibaike.com/pic/ # 將url扔入隊列
(integer) 1
查看reids中保存的爬取數據:
127.0.0.1:6379> keys *
1) "qiubai:items"
2) "data"
3) "name"
4) "qiubai:dupefilter"
127.0.0.1:6379> lrange qiubai:items 0 -1
1) "{\"img_url\": \"https://pic.qiushibaike.com/system/pictures/12132/121326116/medium/FO4Z7Q83NXSOPYTL.jpg\"}"
......