scrapy爬蟲架構介紹和初試

jopen 11年前發布 | 57K 次閱讀 網絡爬蟲 Scrapy

今天這篇文章主要是介紹一下scrapy的架構,以及如何創建一個scrapy的工程; 什么是scrapy?為什么要用 scrapy?下面主要是對這兩個問題的簡要回答。


Scrapy 是一套基于Twisted的異步處理框架,是純python實現的爬蟲框架,用戶只需要定制開發幾個模塊就可以輕松的實現一個爬蟲,用來抓取網頁內容或者各種圖片。下圖顯示了Scrapy的大體架構,其中包含了scheduler、item pipeline、downloader、spider以及engine這幾個組件模塊,而其中的綠色箭頭則說明了整套系統的數據處理流程。

scrapy爬蟲架構介紹和初試

下面就來一個個解釋每個組件的作用及數據的處理過程。

一、組件說明:

    1、Scrapy Engine(Scrapy引擎)

    Scrapy引擎是用來控制整個系統的數據處理流程,并進行事務處理的觸發。更多的詳細內容可以看下面的數據處理流程。

    2、Scheduler(調度)

    調度程序從Scrapy引擎接受請求并排序列入隊列,并在Scrapy引擎發出請求后返還給他們。

    3、Downloader(下載器)

    下載器的主要職責是抓取網頁并將網頁內容返還給蜘蛛( Spiders)。

    4、Spiders(蜘蛛)

    蜘蛛是有Scrapy用戶自己定義用來解析網頁并抓取制定URL返回的內容的類,每個蜘蛛都能處理一個域名或一組域名。換句話說就是用來定義特定網站的抓取和解析規則。

    蜘蛛的整個抓取流程(周期)是這樣的:

    (1)首先獲取第一個URL的初始請求,當請求返回后調取一個回調函數。第一個請求是通過調用start_requests()方法。該方法默認從start_urls中的Url中生成請求,并執行解析來調用回調函數。

    (2)在回調函數中,你可以解析網頁響應并返回項目對象和請求對象或兩者的迭代。這些請求也將包含一個回調,然后被Scrapy下載,然后有指定的回調處理。

    (3)在回調函數中,你解析網站的內容,同程使用的是Xpath選擇器(但是你也可以使用BeautifuSoup, lxml或其他任何你喜歡的程序),并生成解析的數據項。

    (4)最后,從蜘蛛返回的項目通常會進駐到項目管道。

5、Item Pipeline(項目管道)

    項目管道的主要責任是負責處理有蜘蛛從網頁中抽取的項目,他的主要任務是清晰、驗證和存儲數據。當頁面被蜘蛛解析后,將被發送到項目管道,并經過幾個特定的次序處理數據。每個項目管道的組件都是有一個簡單的方法組成的Python類。他們獲取了項目并執行他們的方法,同時他們還需要確定的是是否需要在項目管道中繼續執行下一步或是直接丟棄掉不處理。

項目管道通常執行的過程有:

  1. 清洗HTML數據
  2. 驗證解析到的數據(檢查項目是否包含必要的字段)
  3. 檢查是否是重復數據(如果重復就刪除)
  4. 將解析到的數據存儲到數據庫中
  5. </ol>

    6、Downloader middlewares(下載器中間件)

        下載中間件是位于Scrapy引擎和下載器之間的鉤子框架,主要是處理Scrapy引擎與下載器之間的請求及響應。它提供了一個自定義的代碼的方式來拓展 Scrapy的功能。下載中間器是一個處理請求和響應的鉤子框架。他是輕量級的,對Scrapy盡享全局控制的底層的系統。

    7、Spider middlewares(蜘蛛中間件)

        蜘蛛中間件是介于Scrapy引擎和蜘蛛之間的鉤子框架,主要工作是處理蜘蛛的響應輸入和請求輸出。它提供一個自定義代碼的方式來拓展Scrapy的功能。蛛中間件是一個掛接到Scrapy的蜘蛛處理機制的框架,你可以插入自定義的代碼來處理發送給蜘蛛的請求和返回蜘蛛獲取的響應內容和項目。

    8、Scheduler middlewares(調度中間件)

        調度中間件是介于Scrapy引擎和調度之間的中間件,主要工作是處從Scrapy引擎發送到調度的請求和響應。他提供了一個自定義的代碼來拓展Scrapy的功能。

    二、數據處理流程

    Scrapy的整個數據處理流程由Scrapy引擎進行控制,其主要的運行方式為:

    1. 引擎打開一個域名,時蜘蛛處理這個域名,并讓蜘蛛獲取第一個爬取的URL。
    2. 引擎從蜘蛛那獲取第一個需要爬取的URL,然后作為請求在調度中進行調度。
    3. 引擎從調度那獲取接下來進行爬取的頁面。
    4. 調度將下一個爬取的URL返回給引擎,引擎將他們通過下載中間件發送到下載器。
    5. 當網頁被下載器下載完成以后,響應內容通過下載中間件被發送到引擎。
    6. 引擎收到下載器的響應并將它通過蜘蛛中間件發送到蜘蛛進行處理。
    7. 蜘蛛處理響應并返回爬取到的項目,然后給引擎發送新的請求。
    8. 引擎將抓取到的項目項目管道,并向調度發送請求。
    9. 系統重復第二部后面的操作,直到調度中沒有請求,然后斷開引擎與域之間的聯系。
    10. </ol>

      ========================華麗的分割線========================

      以上部分是屬于網上抄過來的,誰是第一手就無可考究了,對于大家是否能看明白就沒有保證了,我本還還算能夠明白七成吧。

      上面兩個分割線中的內容也算得是回答了第一個問題了,那么為什么要用scrapy呢?我有一個習慣,在提出問題的時候都會先再找一個問題,對于這個問題而提出的問題是:我自己寫一個不行嗎?

      剛開始的時候我也是這樣想的,于是就自己開始找python怎么抓網頁數據之類的了,后來還弄了一個不堪入目的一段代碼,用于從一個根網頁中找到所有的鏈接,然后將這些鏈接都放到一個列表中,然后弄個循環從這個列表中一個個去抓。下面就是我之前第一次接觸python時寫的代碼(不堪入目,不喜勿噴。。。)

      #encoding=utf-8

      author = 'dragon'

      import urllib2 import os import pymongo import time import hashlib

      def myspider(startweb, keyword): list = [startweb] curindex = 0 Keyword = keyword

      #網絡上MongoHQ
      #con = pymongo.Connection("paulo.mongohq.com", 10042)
      #db = con.mytest
      #db.authenticate("dragon", "dragon")
      #db.urllist.drop()
      
      #本地數據庫
      con = pymongo.Connection("localhost", 27017)
      db = con.mytest
      
      while curindex < len(list):
          url = list[curindex]
          print "list count =", len(list), "  curcheck ", curindex
          print "try to visit ", url
      
          headers = ('User-Agent', 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36')
      
          try:
              opener = urllib2.build_opener()
              opener.addheaders = [headers]
              openness = opener.open(url, None, 30)
              data = openness.read()
              opener.close()
          except:
              print "some error ..."
              curindex += 1
              continue
      
          print "finish get data..."
      
          os.remove("d:/test.txt")
          file = open("d:/test.txt", "a")
          print >> file, data
          file.close()
      
          myfile      = open("d:/test.txt", "r")
          mystring    = myfile.read()
          myfile.close()
      
          #找到標題
          title       = ""
          headstart   = mystring.find("<head>")
          headend     = mystring.find("</head>")
          if headstart < 0:
              headstart   = mystring.find("<HEAD>")
              headend     = mystring.find("</HEAD>")
      
          if headstart > 0:
              titlestart  = mystring.find("<title>")
              titleend    = mystring.find("</title>")
              if titlestart < 0:
                  titlestart  = mystring.find("<TITLE>")
                  titleend    = mystring.find("</TITLE>")
      
              if titleend > titlestart and titlestart < headend and titleend < headend:
                  title = mystring[titlestart+len("<title>"):titleend]
      
          dbdata = {"title":"", "url":"", "time":""}
      
          try:
              title = title.decode("utf-8").encode("utf-8")
          except:
              try:
                  title = title.decode("gbk").encode("utf-8")
              except:
                  pass
      
      
          dbdata["title"] = title
          dbdata["url"] = url
          dbdata["time"] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
          try:
              db.urllist.insert(dbdata)
          except:
              print "insert error"
      
          if len(mystring) > 0:
              while len(mystring) > 0:
                  start = mystring.find("href=\"")
                  if start <= 0:
                      break
      
                  substring = mystring[start+6:]
                  end = substring.find("\"")
                  weblink = substring[:end]
                  if Keyword != "":
                      if weblink.find(Keyword) >= 0 and list.count(weblink) <= 0:
                          list.append(weblink)
                  elif 0 > weblink.find("video.sina.com.cn") \
                      and 0 > weblink.find("video.baidu.com") \
                      and 0 <= weblink.find("http:") \
                      and 0 >= list.count(weblink):
      
                      list.append(weblink)
      
                  mystring = mystring[start+6:]
      
          curindex += 1
      
      

      if name == 'main': myspider("處理的流程是:

          1. 將一個開始的網頁url存放到list中

          2. 不斷從list中取出url進行數據獲取

          3. 在獲取到的網頁數據中的鏈接都存放到list里面

          4. 不斷重復2、3步驟

      上面這段代碼寫得怎么樣就不值得評論了,但是值得我們思考的是,上面的代碼出發點是爬蟲,并且還是漫無目的的爬蟲,沒有了終點,或者大家會想到很多退出循環的方法,但是我們還有很多問題需要考慮:如何提高爬蟲的效率?如何最大限度利用網絡帶寬?如果提高抓取回來數據的處理?最重要一點是:我們都不會想著去做google或百度,而是針對一些特定的需求來實現一個爬蟲,那么我們如何簡單而又快速的去實現我們的定制功能呢?

      上面的幾個問題就是我們最終都會遇到的問題,也是scrapy能夠很好的處理的問題,它通過幾個組件完成不同的部分,將類似下載網頁數據的這些通用操作封裝起來,減少了我們編寫爬蟲時的難度,并且各個部件之間通過異步來處理,能夠最大限度利用了網絡帶寬。我們只需要按照它的要求來實現幾個模塊就可以了。

      最后,講講使用如何生成一個scrapy工程。

      打開cmd,cd到你要創建工程的目錄,然后使用以下命令創建工程test:

          scrapy startproject test

      如下圖所示,我們看到創建了一個test文件夾,里面包含了其他一些文件

      scrapy爬蟲架構介紹和初試

      根據上圖可以對照第一幅圖,找到一些組件的對應文件,我們可以在spider下面創建一個py文件(例如:spider.py),然后寫上以下代碼:

      from scrapy.spider import BaseSpider

      class test(BaseSpider): name = "test" allowed_domains = ["hao123.com"] start_urls = ["

      def parse(self, response):
          print response.url</pre>在cmd中,cd進入剛才創建的test目錄,使用以下命令運行這個爬蟲,我們可以最后看到一些debug輸出 <p><img alt="scrapy爬蟲架構介紹和初試" src="https://simg.open-open.com/show/3f9b806806fcfc780601c8167ccaa006.png" width="294" height="48" /> </p>
      

      scrapy爬蟲架構介紹和初試

      這篇文章到此為止,寫得比較粗糙。可能大家對于spider.py中的一些變量和函數名稱,以及該模塊什么時候被調用存在疑問,大家可以上網找找資料學習,我將在下一篇文件中說明,并且以獲取百度文庫中的圖書信息作為例子,完成相關的代碼并提供源碼。

      來自:
      http://my.oschina.net/dragonblog/blog/173545

      </div>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!