爬蟲之Scrapy

參考博客:點擊這裏html

 

1、scrapy安裝配置python

Linux
      pip3 install scrapy
 
 
Windows
      a. pip3 install wheel
      b. 下載twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
      c. 進入下載目錄,執行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl
      d. pip3 install scrapy
      e. 下載並安裝pywin32:https://sourceforge.net/projects/pywin32/files/

  

2、建立並運行scrapyredis

1. scrapy startproject 項目名稱
   - 在當前目錄中建立中建立一個項目文件(相似於Django)
 
2. scrapy genspider [-t template] <name> <domain>
   - 建立爬蟲應用
   如:
      scrapy gensipider -t basic oldboy oldboy.com
      scrapy gensipider -t xmlfeed autohome autohome.com.cn
   PS:
      查看全部命令:scrapy gensipider -l
      查看模板命令:scrapy gensipider -d 模板名稱
 
3. scrapy list
   - 展現爬蟲應用列表
 
4. scrapy crawl 爬蟲應用名稱
   - 運行單獨爬蟲應用  
注意:
 python import scrapy 報錯:from .. import etree ImportError: DLL load failed: 找不到指定的程序。
解決辦法:因爲本地缺乏lxml文件或是lxml文件不符
pip3 uninstall lxml  先卸載已經安裝的lxml
官網下載新的lxml: http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 

 下載而後 pip3 install 文件路徑 安裝 lxml 
 
5.目錄結構:

 6.運行流程數據庫

Scrapy是一個爲了爬取網站數據,提取結構性數據而編寫的應用框架。 其能夠應用在數據挖掘,信息處理或存儲歷史數據等一系列的程序中。
其最初是爲了頁面抓取 (更確切來講, 網絡抓取 )所設計的, 也能夠應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。Scrapy用途普遍,能夠用於數據挖掘、監測和自動化測試。cookie

Scrapy 使用了 Twisted異步網絡庫來處理網絡通信。總體架構大體以下網絡

Scrapy主要包括瞭如下組件:架構

  • 引擎(Scrapy)
    用來處理整個系統的數據流處理, 觸發事務(框架核心)
  • 調度器(Scheduler)
    用來接受引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回. 能夠想像成一個URL(抓取網頁的網址或者說是連接)的優先隊列, 由它來決定下一個要抓取的網址是什麼, 同時去除重複的網址
  • 下載器(Downloader)
    用於下載網頁內容, 並將網頁內容返回給蜘蛛(Scrapy下載器是創建在twisted這個高效的異步模型上的)
  • 爬蟲(Spiders)
    爬蟲是主要幹活的, 用於從特定的網頁中提取本身須要的信息, 即所謂的實體(Item)。用戶也能夠從中提取出連接,讓Scrapy繼續抓取下一個頁面
  • 項目管道(Pipeline)
    負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體、驗證明體的有效性、清除不須要的信息。當頁面被爬蟲解析後,將被髮送到項目管道,並通過幾個特定的次序處理數據。
  • 下載器中間件(Downloader Middlewares)
    位於Scrapy引擎和下載器之間的框架,主要是處理Scrapy引擎與下載器之間的請求及響應。
  • 爬蟲中間件(Spider Middlewares)
    介於Scrapy引擎和爬蟲之間的框架,主要工做是處理蜘蛛的響應輸入和請求輸出。
  • 調度中間件(Scheduler Middewares)
    介於Scrapy引擎和調度之間的中間件,從Scrapy引擎發送到調度的請求和響應。

Scrapy運行流程大概以下:app

    1. 引擎從調度器中取出一個連接(URL)用於接下來的抓取
    2. 引擎把URL封裝成一個請求(Request)傳給下載器
    3. 下載器把資源下載下來,並封裝成應答包(Response)
    4. 爬蟲解析Response
    5. 解析出實體(Item),則交給實體管道進行進一步的處理
    6. 解析出的是連接(URL),則把URL交給調度器等待抓取

 

3、實例:框架

1.dom

 1 # -*- coding: utf-8 -*-
 2 import scrapy
 3 from scrapy.http import Request
 4 from scrapy.selector import Selector
 5 from scrapy.http.cookies import CookieJar
 6 
 7 class ChoutiSpider(scrapy.Spider):
 8     #爬蟲名稱,必須
 9     name = 'chouti'
10     #容許的url範圍
11     allowed_domains = ['chouti.com']
12     #起始url
13     start_urls = ['http://dig.chouti.com/']
14     #維護請求cookies
15     cookie_dict = {}
16 
17     def start_requests(self):
18         """
19         根據繼承的類源碼,自定製本身的起始函數,如沒有此函數則用
20         繼承類默認的函數,本質就是生成一個生成器,next生成器作操做
21         :return:Request()
22         """
23         for url in self.start_urls:
24             yield Request(url,dont_filter=True,callback=self.login)
25 
26     def login(self, response):
27         """
28         獲取響應頁面發來的cookies,分析登陸請求,獲取所需數據進行登陸
29         :param response: 首頁內容
30         :return:
31         """
32 
33         #獲取cookie
34         cookie_jar = CookieJar()
35         cookie_jar.extract_cookies(response,response.request)
36         for k,v in cookie_jar._cookies.items():
37             for i,j in v.items():
38                 for m,n in j.items():
39                     self.cookie_dict[m] = n.value
40 
41         #登陸須要的post數據
42         post_data = {
43             'phone':'861767712xxxx',
44             'password':'xxxx',
45             'oneMonth':1,
46         }
47         import urllib.parse
48 
49         yield Request(
50             url="http://dig.chouti.com/login",
51             method='POST',
52             cookies=self.cookie_dict,
53             #將字典轉化成k1=v1&k2=v2的格式,放在請求頭中
54             body=urllib.parse.urlencode(post_data),
55             headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
56             callback=self.show
57         )
58 
59     def show(self,response):
60         """
61         登陸成功,獲取新聞列表
62         :return:
63         """
64         yield Request(url='http://dig.chouti.com/',cookies=self.cookie_dict,callback=self.find_tag)
65 
66     def find_tag(self,response):
67         """
68         分析每個頁面取到點贊id,發送POST請求對每個頁面的文章進行點贊
69         :return:
70         """
71         hxs = Selector(response=response)
72         link_id_list = hxs.xpath('//div[@class="part2"]/@share-linkid').extract()
73         print(link_id_list)
74         for link_id in link_id_list:
75             #獲取首頁全部文章ID點贊
76             link_url = 'http://dig.chouti.com/link/vote?linksId=%s' % link_id
77             yield Request(url=link_url,method="POST",cookies=self.cookie_dict,callback=self.show_res)
78 
79         #獲取其它分頁的文章點贊
80         page_list = hxs.xpath('//div[@id="dig_lcpage"]//a/@href').extract()
81         print(1)
82         for page in page_list:
83             page_url = "http://dig.chouti.com%s" % (page,)
84             #將每一頁結果從新交給這個函數,遞歸的執行每一頁的點贊
85             yield Request(url=page_url,method='GET',callback=self.find_tag)
86 
87     def show_res(self,response):
88         """
89         顯示點贊結果
90         :param response:
91         :return:
92         """
93         print(response.text)
自動登陸點贊抽屜網

 

 

4、高級

1.持久化:

爬蟲數據爬取後的結果通過xpath取到須要的值,能夠交給爬蟲主目錄下的items.py的類進行規則化封裝,

yield這個item,交給pipelines進行持久化處理

爬蟲.py:

from ..items import Sp1Item
yield Sp1Item(url=url,text=text)

items.py:
class Sp1Item(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    url = scrapy.Field()
    text = scrapy.Field()

pipeline:

	
1持久化: item,pipeline
		pipeline執行的前提:
			- spider中yield Item對象
			- settings中註冊
				ITEM_PIPELINES = {
#權重,越小優先級越高 'sp2.pipelines.Sp2Pipeline': 300, 'sp2.pipelines.Sp2Pipeline': 100, } 編寫pipeline class Sp2Pipeline(object): def __init__(self): self.f = None def process_item(self, item, spider): """ :param item: 爬蟲中yield回來的對象 :param spider: 爬蟲對象 obj = JanDanSpider() :return: """ print(item) self.f.write('....') return item # from scrapy.exceptions import DropItem # raise DropItem() 下一個pipeline的process_item方法不在執行 @classmethod def from_crawler(cls, crawler): """ 初始化時候,用於建立pipeline對象 :param crawler: :return: """
                        #crawler包含爬蟲相關的全部東西,.settings可讀取配置文件 # val = crawler.settings.get('MMMM') print('執行pipeline的from_crawler,進行實例化對象') #return cls(val)
                        return cls() def open_spider(self,spider): """ 爬蟲開始執行時,調用 :param spider: :return: """ print('打開爬蟲') self.f = open('a.log','a+') def close_spider(self,spider): """ 爬蟲關閉時,被調用 :param spider: :return: """ self.f.close() PipeLine是全局生效,全部爬蟲都會執行,個別作特殊操做: spider.name

 運行順序:

1.首先檢測 CustomPipeline類中是否有 from_crawler方法。

若是有, obj = 類.from_crawler()  此方法的本質也是實例化對象,但能夠預留一些鉤子,好比讀取配置文件,將參數寫入

若是沒有,obj = 類()

2.執行obj.open_spider()方法  (1次)

3.while True:

  爬蟲運行,而且執行parse各類方法,若是在爬蟲代碼中yield了Item,則會執行obj.process_item()

4.執行完爬蟲代碼,執行obj.close_spider()   (1次)

在二、4步驟時,整個代碼運行只會進行一次,所以打開爬蟲時能夠打開文件或者鏈接數據庫,close_spider時,能夠關閉文件或數據庫,而在爬蟲運行過程當中的process_item中能夠進行寫入操做,

如此能夠避免重複打開文件或數據庫,減少資源的消耗。

多個pipeline

當有多個pipeline時,默認的執行數序爲p1.__init__--p2.__init__,
p1.open_spider()--p2.o...--p1.proce...--p2.pro..--p1.close_spider--p2.clo..
這樣的好處是同一爬蟲能夠定製2個pipeline處理持久化,如一個鏈接數據庫,
另外一個寫入文件中
即,在p1.process_item()中 return item,就是將item傳入p2來進行處理,
若是p1的process方法執行完後,不想執行p2的process方法,則須要導入
DropItem模塊,再也不return item ,經過raise DropItem()便可終止p2.process方法

pipeline是全局生效,若是想對特殊爬蟲特殊操做,則經過process_item中的spider
參數,經過spider.name進行單個爬蟲的判斷。
"""
from scrapy.exceptions import DropItem
"""

2.去重規則

 

scrapy默認使用的去重:settings配置文件(內部,settings文件沒有)
DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter'
DUPEFILTER_DEBUG = False
JOBDIR = "保存範文記錄的日誌路徑,如:/root/" # 最終路徑爲 /root/requests.seen
實例化時from_settings讀取配置文件,如配置文件沒有設置,則使用默認的
去重規則,主要函數爲request_seen(),用來查看url是否訪問過。
須要自定義時,只須要在settings配置py文件的路徑便可生效 DUPEFILTER_CLASS = 'scrapyXXX.XXX.XXX'
class RepeatUrl:
    def __init__(self):
        self.visited_url = set()  # 放在當前服務的內存,此處能夠用redis等代替

    @classmethod
    def from_settings(cls, settings):
        """
        初始化時,調用
        :param settings:
        :return:
        """
        return cls()

    def request_seen(self, request):
        """
        檢測當前請求是否已經被訪問過
        :param request:
        :return: True表示已經訪問過;False表示未訪問過
        """
        if request.url in self.visited_url:
            return True
        self.visited_url.add(request.url)
        return False

    def open(self):
        """
        開始爬去請求時,調用
        :return:
        """
        print('open replication')

    def close(self, reason):
        """
        結束爬蟲爬取時,調用
        :param reason:
        :return:
        """
        print('close replication')

    def log(self, request, spider):
        """每次請求都會運行,記錄日誌
        :param request: 
        :param spider: 
        :return: 
        """
        print('repeat', request.url)

  

3.基於信號的自定義規則:

 

如Django中同樣,scrapy也預留了不少能夠擴展的鉤子,只須要在settings裏面註冊一下並編寫類便可
EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,(文件路徑,權重)
}

 

from scrapy import signals

class MyExtension(object):
    def __init__(self, value):
        self.value = value

    @classmethod
    def from_crawler(cls, crawler):
        val = crawler.settings.getint('MMMM')
        ext = cls(val)

        # 在scrapy中註冊信號: spider_opened,signal=signals.spider_opened指的是,
        #當spider開始運行時執行,第一個參數則是指定執行哪一個函數。
        crawler.signals.connect(ext.opened, signal=signals.spider_opened)
        # 在scrapy中註冊信號: spider_closed,同理這裏爲spider結束時執行
        crawler.signals.connect(ext.closed, signal=signals.spider_closed)

        return ext

    def opened(self, spider):
        print('open')

    def closed(self, spider):
        print('close')

  

 4.中間件

1.爬蟲中間件:

class SpiderMiddleware(object):

    def process_spider_input(self,response, spider):
        """
        下載完成,執行,而後交給parse處理
        :param response: 
        :param spider: 
        :return: 
        """
        pass

    def process_spider_output(self,response, result, spider):
        """
        spider處理完成,返回時調用
        :param response:
        :param result:
        :param spider:
        :return: 必須返回包含 Request 或 Item 對象的可迭代對象(iterable)
        """
        return result

    def process_spider_exception(self,response, exception, spider):
        """
        異常調用
        :param response:
        :param exception:
        :param spider:
        :return: None,繼續交給後續中間件處理異常;含 Response 或 Item 的可迭代對象(iterable),交給調度器或pipeline
        """
        return None


    def process_start_requests(self,start_requests, spider):
        """
        爬蟲啓動時調用
        :param start_requests:
        :param spider:
        :return: 包含 Request 對象的可迭代對象
        """
        return start_requests

  

 2.下載中間件

每次下載以前會執行,能夠用來設置代理等工做,下載以後執行,可用來對下載的Response內容進一步封裝

class DownMiddleware1(object):
    def process_request(self, request, spider):
        """
        請求須要被下載時,通過全部下載器中間件的process_request調用
        :param request: 
        :param spider: 
        :return:  
            None,繼續後續中間件去下載;
            Response對象,中止process_request的執行,開始執行process_response
            Request對象,中止中間件的執行,將Request從新調度器
            raise IgnoreRequest異常,中止process_request的執行,開始執行process_exception
        """
        pass



    def process_response(self, request, response, spider):
        """
        spider處理完成,返回時調用
        :param response:
        :param result:
        :param spider:
        :return: 
            Response 對象:轉交給其餘中間件process_response
            Request 對象:中止中間件,request會被從新調度下載
            raise IgnoreRequest 異常:調用Request.errback
        """
        print('response1')
        return response

    def process_exception(self, request, exception, spider):
        """
        當下載處理器(download handler)或 process_request() (下載中間件)拋出異常
        :param response:
        :param exception:
        :param spider:
        :return: 
            None:繼續交給後續中間件處理異常;
            Response對象:中止後續process_exception方法
            Request對象:中止中間件,request將會被從新調用下載
        """
        return None

下載器中間件
相關文章
相關標籤/搜索