Python爬蟲之scrapy高級(全站爬取,分布式,增量爬蟲)


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

使用CrawlSpiderspider產生的爬蟲文件除了繼承類不一樣外還有一個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_urlsallowed_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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM