scrapy 概述

用了一段時間的scrapy了,比直接Requests、Urllib確實是好用不少,框架仍是不錯的,偶然看到這篇帖子,確實是深有體會,copy下做爲記錄php

在編程語言的世界裏,python彷佛被貼上了作爬蟲的一個標籤,強而有力。而scrapy作爲另外一個老牌的開源項目,更是大規模抓取不可或缺的一個重要力量。縱使scrapy依舊有一些長期沒法解決的詬病,可是他在抓取過程幫程序員解決的一系列的細節問題,仍是有無以倫比的優點。html

缺點

1. 重量級

scrapy依賴於twisted,而twisted是python世界的出名的重量級框架。要讀懂scrapy的源碼,必須先了解twisted(deffered,reactor)的工做原理,讀懂twisted的源碼,這樣一切看起都很是困難。python

2. 內存

爲了防止重複對同一個url抓取,避免無限遞歸的遍歷url,scrapy把全部處理過的request(包括method,url,headers)放到內存當中,若是spider處理的request夠多的話,spider佔用的內存是驚人的。react

解決方法程序員

  1. 必要的時,將一個大型spider(可預計抓取的url過多)拆分紅多個spider。ajax

  2. bloom,以空間換取準確性的一種去重算法。settings.py裏配置DUPEFILTER_CLASS = ‘scrapy.dupefilter.RFPDupeFilter’,用本身實現的bloom算法覆蓋便可。要注意的是,bloom是犧牲了準確性開換取空間開支小的算法,在內存可以勝任,就顯得畫蛇添足。redis

3. xpath的容錯(並非scrapy獨有)

常常會遇到解析一些非標準的html,在瀏覽器會對他們進行容錯處理,而不會影響到頁面展示。可是用xpath就要了命了,並且一般還不容易察覺。解決辦法算法

  1. 將整個html轉化成標準的xml,在用xpath解析。好比數據庫

    xx
    ,這個時候就要想辦法把第二個去掉。

  2. 用re解析抽取結構化信息。不推薦用re,不是由於在這種場景下效果很差,而是由於用正則會增長spider的維護成本,具體的下面會提到。編程


優勢

理論只是實踐基礎和指導原則,要知道理論到實踐仍是有不少路要走的。本人比較反對將重複造輪子的東西輕易的拿到生產上去實踐。spider不只僅是把內容結構化,而是在各個細節處理上都很全面。

1. 智能編碼

scrapy會從meta中自動提取裏涉及到的編碼,若是沒有則嘗試gb2312,utf-8編碼,最後還不行的話,就用自定義的編碼DEFAULT_RESPONSE_ENCODING。雖然這種方式不能完成正確,也能保證90%(我實踐中獲得的結果)以上的正確性。

2. 靈活的pipeline

pipeline以管道的方式處理item,好比說item再加工,過濾,持久化均可以再這裏處理。能夠定義多個pipeline,對item作不一樣的處理。若是在item中有個image字段,能夠先用MediaPipeline處理過,將圖片下載到本地,再講item插入到數據庫。

3. 強大的xpath

scrapy是xpath做爲解析工具,以前提到的也能夠用正則可是不推薦,一個很重要的緣由是,xpath維護,可讀性要比正則強太多。spider的維護一項持久而耗時的工做,特別是一些靜態網站,都是經過cms系統發佈,這樣會致使網站的結構,樣式調整的比較頻繁,用re會陷入無盡的煩惱當中。而xpath當然也會有種問題,可是良好的可讀性,會讓維護成本成倍的下降。

4. 處理http請求的各類細節

  1. 設置抓取時間間隔,在spider中設置download_delay=x(單位是秒)

  2. 配置代理,settings中增長HttpProxyMiddleware(默認),設置系統代理

  3. 配置自定義代理

1 import base64 
2 
3 class ProxyMiddleware(object):
4     def process_request(self, request, spider):
5         request.meta['proxy'] = "http://YOUR_PROXY_IP:PORT"
6         proxy_user_pass = "USERNAME:PASSWORD"
7         encoded_user_pass = base64.encodestring(proxy_user_pass)
8         request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass

也能夠將代理配置成隨機的,只須要在上面代碼中稍做處理。

  1. http code的處理,正常狀況下,scrapy的response只處理20x,設置handle_httpstatus_list = [301,302,204,206,404,500]

  2. retry機制,因爲網絡或者對方服務器的緣由,對url重複處理是很是有必有,spider中設置RETRY_TIMES,RETRY_HTTP_CODES

  3. 模擬瀏覽器行爲,設置user-agent

  4. 設置默認headers, DEFAULT_REQUEST_HEADERS

  5. cookies處理,開啓COOKIES_DEBUG=True後,能夠再Request中帶上cookies

yield Request(url=’http://www.douban.com‘,cookies={‘session’:’xxxx’})
  等等吧,太多了。

5. 對https,ftp等協議支持,使用過程和http同樣。

6. 強大的監控,日誌系統

默認開啓TELNETCONSOLE_ENABLED = 1,WEBCONSOLE_ENABLED = 1具體的能夠看文檔。通常的狀況下,會查看spider close的日誌,整個spider的運行狀態都查看到

**重點內容**7. 支持json,jsonlines,csv,xml,marshal,pickle導出

下面接着寫:

體會

斷斷續續的使用scrapy已經很長時間,在各類問題也算是有所領悟(所需工具firefox,firebug,firefox的xpath插件)。

1. 非標準的html解析問題(上一篇提到過)

這個問題蛋疼不是問題有多難解決,而是很難定位到錯誤的緣由。在firefox中確保xpath沒有寫錯的狀況下,找不到相應的數據,這個時候就要考慮是不是這個緣由,直接查看頁面源碼,若是有不標準的html,在firefox中會有紅色標識。

selector = XPathSelector(text=response.body.replace('<div class="left-clear"/>','<div class="left-clear">'))

2. 反扒

一般的狀況下,一些流量比較大的網站都有反爬蟲的機制,避免惡意的訪問,減輕服務器的壓力。通常的狀況的下調整抓取的間隔,更換代理。2種方式都有缺點。

抓取間隔設置download_delay=1,一般設置這個屬性,整個spider的性能變得很是低。

更換代理,能夠到http://pachong.org/去找免費的代理,通常速度也不快,並且不穩定。

3. http code欺騙(我本身取得名字)

通常狀況數據的正常返回都是20x,scrapy會自動忽略掉50x的http code。這個(http://www.travelplus.cn/plus/list.php?tid=8

)用瀏覽器打開的時候,你看不出任何問題。用firebug就能清晰的看到,其實它是將500頁面,當作正常頁面顯示。

  在spider裏添加handle_httpsatstus_code = [500],一切照舊便可。

4. 模擬瀏覽器請模擬完全。

若是你有服務器點開發的經驗,就不難理解,服務器能夠經過任何一個headers或者參數來屏蔽你的request。參數天然不用說,能帶上都帶上,至於一些參數加密,之後再提。主要是針對headers中的幾個經常使用的。

  Cookie,這個最多見用的,平心而論scrapy對cookie的支持只是基礎的支持,用起來不太好用。

  User-Agent, 這個主要仍是服務器爲了區分request是來自pc端仍是手機端,會致使response不同。

  Referer, 防止僞造的跨網站請求。

  X-Requested-With=XMLHttpRequest, 這個很好理解,對於同一個url,ajax request和普通request的response結果不同很正常

  Content-Length, 一些網站沒有這個header會返回411

  以上5個header基本上能解決問題,萬一遇到頑固份子,那隻好完全的模仿吧。

5. 不要忽略firebug裏監控到任何http request

部分網站在提交數據的時候,會先跳轉到一個頁面,這個頁面是空白的頁,只是包含一些隱藏的表單,最後用js帶上表單跳轉到其餘的頁面。所以在firebug的監控的request中,會莫名其妙的多出來一些參數。

6. xpath表達式

能簡單儘可能簡單,儘可能class和id來表達。千萬別將xpath的表達式依賴不少屬性,這樣難以維護不說,並且極不穩定。

7. xpath表達式

xpath表達式能在瀏覽器中找到元素,在spider裏確不能,有可能性js動態加載,還有table元素,有的會自動添加th等。這個時候請直接對着html源碼寫你的表達式。

8. ajax request和json,這應該是如今網站的主流。

要關注的真正獲得數據的request,一般咱們要抓取的數據也都在ajax request中,拿到數據後用json.loads(response.body)

9. 要抓取的內容分佈在不一樣的request中,這個時候request meta就能很好的勝任。

def parse_city_item(self, response):
        x = HtmlXPathSelector(response)
        item = Item()
        //給item賦值
        item['title'] = ''.join(x.select('//div[@class="title"]/text()').extract())
        data = {}
        req = FormRequest(url=url,formdata=data,callback=self.parse_comment)
        req.meta['item'] = item
        //帶上返回req
        return req
def parse_comment(self, response):
        item = response.request.meta['item']
        x = HtmlXPathSelector(response)
        item['content'] = ''.join(x.select('//div[@class="content"]/text()').extract())
        return item

10. 增量抓取

不少狀況下須要抓取網站更新的內容。咱們知道在一次抓取的過程是能避免重複抓取,scrapy默認提供文件存儲的方式,只須要再settings裏設置JOBDIR=」path」。我在使用scrapy仍是0.9,沒有這個特性,使用redis做爲url存儲。我的感受對於大規模抓取用redis仍是比文件的方式要好不少。redis裏能夠設置key的過時時間,確定會有人說,這樣能保證不重複的抓取嗎,固然不能絕對,可是能夠調整抓取深度,對於抓取較爲頻繁網站,抓取到相同的機率就很低。好比說抓取sina的體育新聞,將url作md5加密存儲到redis裏,過時時間設置爲1天,抓取體育新聞滾動頁面前3頁 點擊連接,15分鐘抓取一次。而單純用文件方式存儲的話,文件只大不小,多了天然影響性能。

11. 編碼

上一篇也提到過。scrapy爲咱們作了編碼,可是若是這樣好錯了,就須要特殊處理了。

參考自:http://www.cnblogs.com/twelfthing/articles/4620533.html
http://www.cnblogs.com/twelfthing/articles/4620761.html

相關文章
相關標籤/搜索