爬蟲框架-scrapy的使用

eliwang發表於2021-04-28

Scrapy

  • Scrapy是純python實現的一個為了爬取網站資料、提取結構性資料而編寫的應用框架。
  • Scrapy使用了Twisted非同步網路框架來處理網路通訊,可以加快我們的下載速度,並且包含了各種中介軟體介面,可以靈活的完成各種需求

1、安裝

sudo pip3 install scrapy

2、認識scrapy框架

  • 2.1 scrapy架構圖

  • Scrapy Engine(引擎): 負責SpiderItemPipelineDownloaderScheduler中間的通訊,訊號、資料傳遞等。

  • Scheduler(排程器): 它負責接受引擎傳送過來的Request請求,並按照一定的方式進行整理排列,入佇列,當引擎需要時,交還給引擎

  • Downloader(下載器):負責下載Scrapy Engine(引擎)傳送的所有Requests請求,並將其獲取到的Responses交還給Scrapy Engine(引擎),由引擎交給Spider來處理

  • Spider(爬蟲):它負責處理所有Responses,從中分析提取資料,獲取Item欄位需要的資料,並將需要跟進的URL提交給引擎,再次進入Scheduler(排程器)

  • Item Pipeline(管道):它負責處理Spider中獲取到的Item,並進行進行後期處理(詳細分析、過濾、儲存等)的地方.

  • Downloader Middlewares(下載中介軟體):你可以當作是一個可以自定義擴充套件下載功能的元件

  • Spider Middlewares(Spider中介軟體):可以理解為是一個可以自定擴充套件和操作引擎Spider中間通訊的功能元件(比如進入Spider的Responses和從Spider出去的Requests)

  • 2.2 Scrapy執行的大體流程:

1.引擎從spider拿到第一個需要處理的URL,並將request請求交給排程器。

2.排程器拿到request請求後,按照一定的方式進行整理排列,入佇列,並將處理好的request請求返回給引擎。

3.引擎通知下載器,按照下載中介軟體的設定去下載這個request請求。

4.下載器下載request請求,並將獲取到的response按照下載中介軟體進行處理,然後後交還給引擎,由引擎交給spider來處理。對於下載失敗的request,引擎會通知排程器進行記錄,待會重新下載。

5.spider拿到response,並呼叫回撥函式(預設呼叫parse函式)去進行處理,並將提取到的Item資料和需要跟進的URL交給引擎。

6.引擎將item資料交給管道進行處理,將需要跟進的URL交給排程器,然後開始迴圈,直到排程器中不存在任何request,整個程式才會終止。

  • 2.3 製作scrapy爬蟲步驟:

1.建立專案:通過(scrapy startproject 專案名)來建立一個專案

2.明確目標:編寫items.py檔案,定義提取的Item

3.製作爬蟲:編寫spiders/xx.py檔案,爬取網站並提取Item

4.儲存內容:編寫pipelines.py檔案,設計管道來儲存提取到的Item(即資料)

 3、入門教程

  • 3.1 建立專案

    • 在開始爬蟲之前,第一步需要建立一個專案。先進入打算儲存程式碼的目錄,執行以下命令:
      scrapy startproject myProject
    • 其中myProject為專案名,執行上述命令後,在當前目錄下會建立一個myProject目錄,該目錄包含以下內容:
      .
      ├── myProject
      │   ├── __init__.py
      │   ├── items.py
      │   ├── middlewares.py
      │   ├── pipelines.py
      │   ├── settings.py
      │   └── spiders
      │       └── __init__.py
      └── scrapy.cfg

scrapy.cfg:專案的配置檔案

myProject/items.py:專案中的目標檔案

myProject/middlewares.py:專案中的中介軟體檔案

myProject/pipelines.py:專案中的管道檔案

myProject/settings.py:專案中的設定檔案

myProject/spiders:放置spider程式碼的目錄

  • 3.2 明確目標(定義Item)

    • 我們打算抓取網站http://www.itcast.cn/channel/teacher.shtml#ajavaee裡所有老師的姓名、職稱、入職時間和個人簡介:
      • 首先開啟myProject/items.py檔案
      • Item是儲存爬取到的資料的容器,其使用方法和python字典類似
      • 建立一個scrapy.Item 類, 並且定義型別為 scrapy.Field的類屬性來定義一個Item(類似於ORM的對映關係)
      • 建立一個MyprojectItem 類,和構建item模型(model)
        import scrapy
        
        class MyprojectItem(scrapy.Item):
            name = scrapy.Field()
            title = scrapy.Field()
            hiredate = scrapy.Field()
            profile = scrapy.Field()
        
  • 3.3 製作爬蟲

    • 在專案根目錄下輸入以下命令,可以在myProject/spiders目錄下建立一個名為itcast的爬蟲(itcast.py),並且指定爬蟲作用域的範圍itcast.cn:
      scrapy genspider itcast itcast.cn 
    • 開啟itcast.py,預設添上了以下內容:
      import scrapy
      
      class ItcastSpider(scrapy.Spider):
          name = 'itcast'
          allowed_domains = ['itcast.cn']
          start_urls = ['http://itcast.cn/']
      
          def parse(self, response):
              pass
    • 要建立一個Spider, 你必須用scrapy.Spider類建立一個子類,並確定了3個強制的屬性和1個方法。

      • name這個爬蟲的識別名稱,必須是唯一的

      • allow_domains爬蟲的約束區域,規定爬蟲只爬取這個域名下的網頁,不存在的URL會被忽略。

      • start_urls爬取的URL列表。因此,第一個被獲取到的頁面將是其中之一。 後續的URL則從初始URL返回的資料中提取。

      • parse(self, response)Request物件預設的回撥解析方法。每個初始URL完成下載後將被呼叫,呼叫的時候傳入從每一個URL傳回的Response物件來作為唯一引數,該方法負責解析返回的資料(response.body),提取資料(生成item)以及生成需要進一步處理的URL的Request物件

    • 修改start_urls為第一個需要爬取的URL:
      start_urls = ['http://www.itcast.cn/channel/teacher.shtml#ajavaee']
    • 修改parse方法提取Item:
      def parse(self, response):
          for teacher in response.xpath("//ul[@class='clears']/li/div[@class='main_mask']"):
              #將提取到到的資料封裝到一個MyprojectItem物件中
              item = MyprojectItem()
              #利用xpath返回該表示式所對應的所有節點的selector list列表
              #呼叫extract方法序列化每個節點為Unicode字串並返回list
              name = teacher.xpath('h2/text()').extract()[0]
              title = teacher.xpath('h2/span/text()').extract()[0]
              hiredate = teacher.xpath('h3/text()').extract()[0].split('')[-1]
              profile = teacher.xpath('p/text()').extract()[0]
              item['name'] = name
              item['title'] = title
              item['hiredate'] = hiredate
              item['profile'] = profile
              # 使用yield將獲取的資料交給pipelines,如果使用return,則資料不會經過pipelines
              yield item
  • 3.4 儲存內容

    • Feed輸出

      • 如果僅僅想要儲存item,可以不需要實現任何的pipeline,而是使用自帶的Feed輸出(Feed export)。主要有以下4種方式,通過-o指定輸出檔案格式:
        # json格式,預設為Unicode編碼
        scrapy crawl itcast -o itcast.json
        # json lines格式,預設為Unicode編碼
        scrapy crawl itcast -o itcast.jsonl
        #csv 逗號表示式,可用Excel開啟
        scrapy crawl itcast -o itcast.csv
        # xml格式
        scrapy crawl itcast -o itcast.xml

        執行這些命令後,將會對爬取的資料進行序列化,並生成檔案。

    • 編寫Item Pipeline(通用):

      • 每個Item Pipeline都是實現了簡單方法的Python類,他們接收到Item並通過它執行一些行為,同時也決定此Item是丟棄還是被後續pipeline繼續處理。
      • 每個item pipeline元件必須實現process_item(self,item,spider)方法:
        • 這個方法必須返回一個Item (或任何繼承類)物件, 或是丟擲 DropItem異常。
        • 引數是被爬取的item和爬取該item的spider
        • spider程式每yield一個item,該方法就會被呼叫一次
      • 同時還可以實現以下方法:
        • open_spider(self,spider):開啟spider的時候呼叫,只執行1次
        • close_spider(self,spider):關閉spider的時候呼叫,只執行1次
    • item寫入json檔案:

      import json
      from itemadapter import ItemAdapter
      
      class MyprojectPipeline:
          def open_spider(self,spider):
              '''可選實現,開啟spider時呼叫該方法'''
              self.f = open('itcast.json','w')
      
          def process_item(self, item, spider):
              '''必須實現,被拋棄的item將不會被後續的pipeline元件所處理'''
              self.f.write(json.dumps(dict(item),ensure_ascii=False)+'\n')
              return item
      
          def close_spider(self,spider):
              '''可選實現,關閉spider時呼叫該方法'''
              self.f.close()
    • 啟用Item Pipeline元件

      ITEM_PIPELINES = {
         'myProject.pipelines.MyprojectPipeline': 300,
      }

      在settings.py檔案裡新增以上配置(可以取消原有的註釋),後面的數字確定了item通過pipeline的順序,通常定義在0-1000範圍內,數值越低,元件的優先順序越高

    • 啟動爬蟲

      scrapy crawl itcast

      檢視當前目錄下是否生成了itcast.json檔案

 4、Scrapy Shell

Scrapy終端是一個互動終端,我們可以在未啟動spider的情況下嘗試及除錯程式碼,也可以用來測試XPath或CSS表示式,檢視他們的工作方式,方便我們爬取的網頁中提取的資料。

  • 啟動scrapy shell

    scrapy shell <url>
    命令列啟動,url是要爬取的網頁的地址
  • 常見可用物件response

    • response.status:狀態碼
    • response.url:當前頁面url
    • response.body:響應體(bytes型別)
    • response.text:響應文字(str型別)
    • response.json():如果響應體的是json,則直接轉換成python的dict型別
    • response.headers:響應頭
    • response.selector:返回Selector物件,之後就可以呼叫xpath和css等方法,也可以簡寫成response.xpath()和response.css()
  • selector選擇器

    • Selector有四個基本的方法,最常用的還是xpath:

      • xpath(): 傳入xpath表示式,返回該表示式所對應的所有節點的selector list列表
      • extract(): 序列化該節點為Unicode字串並返回list
      • css(): 傳入CSS表示式,返回該表示式所對應的所有節點的selector list列表,語法同 BeautifulSoup4
      • re(): 根據傳入的正規表示式對資料進行提取,返回Unicode字串list列表

5、Spider

Spider類定義瞭如何爬取某個(或某些)網站。包括了爬取的動作(例如:是否跟進連結)以及如何從網頁的內容中提取結構化資料(爬取item)。

  • scrapy.Spider是最基本的類,所有編寫的爬蟲必須繼承這個類。
    import scrapy
    
    class XxSpider(scrapy.Spider):
        pass
  • 主要用到的函式及呼叫順序為:
    • __init__():初始化爬蟲名字和start_urls列表
    • start__requests(self):呼叫make_requests_from_url()生成Requests物件交給Scrapy下載並返回response
    • parse(self,response):解析response,並返回Item或Requests(需指定回撥函式)。Item傳給Item pipline持久化 , 而Requests交由Scrapy下載,並由指定的回撥函式處理(預設parse()),一直進行迴圈,直到處理完所有的資料為止。
  • 其他方法
    • log(self, message, level=log.DEBUG)
      • message:字串型別,寫入的log資訊
      • level:log等級,有CRITICAL、 ERROR、WARNING、INFO、DEBUG這5種,預設等級為DEBUG

6、CrwalSpider

  • 快速建立CrawlSpider模板:

    scrapy genspider -t crawl 爬蟲名 爬蟲域
  • scrapy.spiders.CrwalSpider是編寫的爬蟲所必須繼承的類
    from scrapy.spiders import CrawlSpider
    
    class XxSpider(CrawlSpider):
         pass

    CrawlSpider類繼承於Spider類,它定義了一些規則(rule)來提供跟進link的方便的機制,從爬取的網頁中獲取link並繼續爬取的工作更適合。

  • LinkExtractor

    • class scrapy.spiders.LinkExtractor
    • 每個LinkExtractor物件有唯一的公共方法是 extract_links(),它接收一個Response物件,並返回一個 scrapy.link.Link 物件。根據不同的response呼叫多次來提取連結
    • 主要引數:

      • allow:滿足括號中“正規表示式”的值會被提取,如果為空,則全部匹配。

      • deny:與這個正規表示式(或正規表示式列表)匹配的URL一定不提取。

      • allow_domains:會被提取的連結的domains。

      • deny_domains:一定不會被提取連結的domains。

      • restrict_xpaths:使用xpath表示式,和allow共同作用過濾連結。

  • rules

    • class scrapy.spiders.Rule
    • 在rules中包含一個或多個Rule物件,每個Rule對爬取網站的動作定義了特定操作。如果多個rule匹配了相同的連結,第一個會被使用。
    • Rule物件主要引數:
      • link_extractor:是一個Link Extractor物件,用於定義需要提取的連結
      • callback:從link_extractor中每獲取到連結時,該回撥函式接受一個response作為其第一個引數。注意:字串型別,避免使用'parse'
      • follow:布林型別,指定了根據該規則從response提取的連結是否需要跟進。 如果callback為None,follow 預設設定為True ,否則預設為False。
      • process_links:指定函式,從link_extractor中獲取到連結列表時將會呼叫該函式,主要用來過濾。
      • process_requests:指定函式, 該規則提取到每個request時都會呼叫該函式,用來過濾request。
  • CrawSpider爬蟲示例

    • 以陽光熱線問政平臺http://wz.sun0769.com/political/index/politicsNewest?id=1為例,爬取投訴帖子的編號、帖子的標題,帖子的處理狀態和帖子裡的內容。
      import scrapy
      from scrapy.linkextractors import LinkExtractor
      from scrapy.spiders import CrawlSpider, Rule
      from myProject.items import MyprojectItem
      
      class SunSpider(CrawlSpider):
          name = 'sun'
          allowed_domains = ['wz.sun0769.com']
          start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']
      
          rules = (
              Rule(LinkExtractor(allow=r'id=\d+&page=\d+')),#每一頁的匹配規則,callback為None,預設跟進
              Rule(LinkExtractor(allow=r'politics/index\?id=\d+'), callback='parse_item'),#每個帖子的匹配規則,設定了callback,預設不跟進
          )
      
          def parse_item(self, response):
              item = MyprojectItem()
              title = response.xpath('//div[@class="mr-three"]/p[@class="focus-details"]/text()').extract()[0] #帖子標題
              status = response.xpath('//div[@class="focus-date clear focus-date-list"]/span[3]/text()').extract()[0].split()[1] #處理狀態
              number = response.xpath('//div[@class="focus-date clear focus-date-list"]/span[4]/text()').extract()[0].split('')[-1] #帖子編號
              content = response.xpath('//div[@class="details-box"]/pre/text()').extract()[0] #帖子內容
              item['title'] = title
              item['status'] = status
              item['number'] = number
              item['content'] = content
      yield
      item

7、logging功能

Scrapy提供了log功能,通過在setting.py中進行設定,可以被用來配置logging

  • 設定

    • LOG_ENABLED:預設: True,啟用logging
    • LOG_ENCODING:預設: 'utf-8',logging使用的編碼
    • LOG_FILE:預設::None,在當前目錄裡建立logging輸出檔案的檔名
    • LOG_LEVEL:預設:'DEBUG',有'CRITICAL'(嚴重錯誤)、'ERROR'(一般錯誤)、'WARNING'(警告資訊)、'INFO'(一般資訊)、'DEBUG'(除錯資訊)這5種等級
    • LOG_STDOUT:預設: False 如果為 True,程式所有的標準輸出(及錯誤)將會被重定向到log中。
  • 示例:

    #在settings.py中任意位置添上以下兩句,終端上會清爽很多
    LOG_FILE = "xxx.log"
    LOG_LEVEL = "INFO"

8、Request物件

  • GET請求

    • 可以使用yield scrapy.Request(url,callback)方法來傳送請求
    • Request物件初始化方法傳入引數如下:
      class Request(object_ref):
      
          def __init__(self, url, callback=None, method='GET', headers=None, body=None,
                       cookies=None, meta=None, encoding='utf-8', priority=0,
                       dont_filter=False, errback=None, flags=None, cb_kwargs=None):
              pass
    • 主要引數:
      • url:需要請求並進行下一步處理的url
      • callback:指定該請求返回的Response,由哪個函式來處理
      • method:預設'GET',一般不需要指定,可以是‘POST’,'PUT'等
      • headrs:請求時包含的標頭檔案,一般不需要
      • meta:比較常用,在不同的request之間傳遞資料用的,dict型別
      • encoding:使用預設的‘utf-8’就行
      • dont_filter:表明該請求不由排程器過濾,可以傳送重複請求,預設為False
      • errback:指定錯誤處理函式
  • POST請求

    • 可以使用scrapy.FormRequest(url, formdata, callback)方法進行傳送
    • 如果希望程式執行一開始就傳送POST請求,可以重寫Spider類的start_requests(self)方法,並且不再呼叫start_urls裡的url。
    • 如果想要預填充或重寫像使用者名稱、使用者密碼這些表單欄位, 可以使用 scrapy.FormRequest.from_response(response, formdata, callback) 方法實現。

9、Downloader Middlewares(下載中介軟體)

  • 下載中介軟體是處於引擎(crawler.engine)和下載器(crawler.engine.download())之間的一層元件,可以有多個下載中介軟體被載入執行。
    • 當引擎傳遞請求給下載器的過程中,下載中介軟體可以對請求進行處理 (例如增加http header資訊,增加proxy資訊等);
    • 在下載器完成http請求,傳遞響應給引擎的過程中, 下載中介軟體可以對響應進行處理(例如進行gzip的解壓等)
  • 要啟用下載器中介軟體元件,將其加入到settings.py中的DOWNLOADER_MIDDLEWARES 設定中。 該設定是一個字典(dict),鍵為中介軟體類的路徑,值為其中介軟體的順序(order)。例如:
    DOWNLOADER_MIDDLEWARES = {
       'myProject.middlewares.MyprojectDownloaderMiddleware': 543,
    }
  • 中介軟體元件是一個定義了以下一個或多個方法的Python類:
    • process_request(self, request, spider):當每個request通過下載中介軟體時,該方法被呼叫。
    • process_response(self, request, response, spider):當下載器完成http請求,傳遞響應給引擎的時候呼叫
  • 示例:(使用隨機User-Agent和代理IP)
    • middlewares.py檔案
      import random
      import json
      import redis
      
      from scrapy import signals
      from itemadapter import is_item, ItemAdapter
      from myProject.settings import USER_AGENTS
      
      class MyprojectDownloaderMiddleware:
          def __init__(self):
              self.r = redis.StrictRedis(host='localhost') #建立redis連線客戶端,用於取裡面儲存的動態獲取的代理ip
      
          def process_request(self, request, spider):
              user_agent = random.choice(USER_AGENTS) #取隨機user-Agent
              proxy_list = json.loads(self.r.get('proxy_list').decode())
              proxy = random.choice(proxy_list) #取隨機ip
              request.headers.setdefault("User-Agent",user_agent) #設定user-agent
              request.meta['proxy'] ='http://'+proxy['ip']+':'+str(proxy['port']) #使用代理ip
    • 修改settings.py檔案配置
      #禁用cookies
      COOKIES_ENABLED = False
      
      #設定下載延遲
      DOWNLOAD_DELAY = 3
      
      #新增自己寫的下載中介軟體類
      DOWNLOADER_MIDDLEWARES = {
         'myProject.middlewares.MyprojectDownloaderMiddleware': 543,
      }
      
      #新增USER-AGENTS
      USER_AGENTS = [
          "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"
          ]

       

 

相關文章