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'#爬蟲文件名
# allowed_domains = ['www.xxx.com']#允許的url
start_urls = ['http://dk.test.com/mail/?ac=list&tid=1']#url列表
#規則解析器
rules = (
#LinkExtractor(allow=r'Items/')連接提取器,就是用來提取連接,根據指定規則(allow=r'Items/')進行指定連接的提取
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"]')
# print(tr_list)
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):
# define the fields for your item here like:
# name = scrapy.Field()
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 = {
#'sunPro.pipelines.SunproPipeline': 300,
'scrapy_redis.pipelines.RedisPipeline':400
}
指定調度器
#增加一個去重容器類的配置,作用使用redis的set集合來存儲請求的指紋數據,從而實現請求去重的持久化
DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
#使用scrapy-redis組件自己的調度器
SCHEDULER='scrapy_redis.scheduler.Scheduler'
#配置調度器是否需要持久化,也就是當爬蟲結束了,要不要清空reids中請求隊列
#如果服務器宕機了,重啟后從爬取的位置繼續爬取
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'
# allowed_domains = ['www.xxx.com']
start_urls = ['http://www.xxx.com/']
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
#創建redis對象
conn = Redis(host='127.0.0.1',port=6379)
def parse_item(self, response):
li_list=response.xpath('xxxxx');
for li in li_list:
# 獲取詳情url
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
# 關閉爬蟲時執行,只執行一次。 (如果爬蟲中間發生異常導致崩潰,close_spider可能也不會執行)
def close_spider(self, spider):
# 可以關閉數據庫等
pass