你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Scrapy+Flask+Mongodb+Swift開發全攻略(2)(3)

Scrapy+Flask+Mongodb+Swift開發全攻略(2)(3)

編輯:IOS開發基礎

Scrapy+Flask+Mongodb+Swift 開發全攻略(1)

好的,這期開始之前,我們先要干兩件事。第一件事是找到spiders文件夾裡的dbmeizi_scrapy.py。打開他,上一篇教程裡,這個爬蟲文件是這麼寫的。

    def parse(self, response):
        liResults = Selector(response).xpath('//li[@class="span3"]')
        for li in liResults:
            for img in li.xpath('.//img'):
                item = MeiziItem()
                item['title'] = img.xpath('@data-title').extract()
                item['dataid'] = img.xpath('@data-id').extract()
                item['datasrc'] = img.xpath('@data-src').extract()
                item['startcount'] = 0
                yield item

現在我們需要改成這樣.

    def parse(self, response):
        liResults = Selector(response).xpath('//li[@class="span3"]')
        for li in liResults:
            for img in li.xpath('.//img'):
                item = MeiziItem()
                item['title'] = img.xpath('@data-title').extract()[0]
                item['dataid'] = img.xpath('@data-id').extract()[0]
                item['datasrc'] = img.xpath('@data-src').extract()[0]
                item['startcount'] = 0
                yield item

why?

很簡單,因為用extract這個方法得到的是一個數組,而我們的每一個字段實際上是一個string而非一個array,如果不取第一個值,那麼存入mongodb之後,title這個key對應的value是一個數組,這會導致我們將mongodb裡的數據轉換成json之後需要在客戶端再進行分解。很麻煩。

第二件事,是刪除我們上一個爬蟲爬取的數據。

如圖:

1431998793461434.gif

ok,重新運行我們的爬蟲,scrapy crawl dbmeiziSpider,現在,check一下數據庫裡的內容,是不是以前的每個字段對應的內容已經從數組變成了string了。

2.jpg

開始編寫服務器

激動人心的時刻要開始了,我們要從iOS程序員變成一個菜鳥級別的server端選手。不過能用自己編寫的iOS客戶端從自己寫的server下載數據,也挺爽的,不是麼?

在編寫服務器端的時候確保你用pip安裝了下面幾個庫。

1.pymongo

2.Flask

我們的服務端代碼如下。

from flask import Flask, request
import json
from bson import json_util
from bson.objectid import ObjectId
import pymongo
app = Flask(__name__)
mongoClient = pymongo.MongoClient('localhost', 27017)
db = mongoClient['dbmeizi']
def toJson(data):
    return json.dumps(data, default=json_util.default)
@app.route('/meizi/', methods=['GET'])
def findmeizi():
    if request.method == 'GET':
        lim = int(request.args.get('limit', 10))
        off = int(request.args.get('offset'),0)
        results = db['meizi'].find().skip(off).limit(lim)
        json_results= []
        for result in results:
            json_results.append(result)
        return toJson(json_results)
if __name__ == '__main__':
    app.run(debug=True)

以上代碼就是我們的服務端代碼,只有短短28行,python的強大之處也在於此。

好的,我來一行一行的解釋一下。

前面5行就是import各種我們需要的庫,如果後面你使用python server.py運行的時候提示錯誤,很可能是你的機子上缺少上述的庫。

app = Flask(__name__)這句話就是利用Flask的構造方法生成一個Flask實例,name是什麼?簡單來說,你創建的任何python文件(.py),都會有一個內置屬性,叫做__name__,他有兩個用途,如果你在命令行狀態下直接運行`python .py`的時候,這個時候這個python文件裡的__name__就是__main__,如果你是在別的python文件裡import *.py,那麼這個name的東西就是這個Python文件的文件名。so,這個東西常常用來判斷,你是在import還是直接在命令行裡運行這個文件。

所以,上一行,我們生成了一個Flask實例並且把這個實例賦給了app這個變量。

 mongoClient = pymongo.MongoClient('localhost', 27017)
db = mongoClient['dbmeizi']

這兩句很簡單,就是用pymongo這個第三方庫,打開我們的mongodb數據庫,並且拿到我們的dbmeizi這個database。

def toJson(data):
    return json.dumps(data, default=json_util.default)

這句話,我們定義了一個函數,用來把mongodb裡的數據轉換為json格式。用來返回給我們的ios客戶端。

@app.route('/meizi/', methods=['GET'])

這句話的意思就是Flask的一種寫法,意思就是當我們發起了一個request,並且這個request的方法是get,url是"localhost:5000/meizi/"這種的的時候,我們就執行findmeizi()這個方法。

def findmeizi():
    if request.method == 'GET':
        lim = int(request.args.get('limit', 10))
        off = int(request.args.get('offset'),0)
        results = db['meizi'].find().skip(off).limit(lim)
        json_results= []
        for result in results:
            json_results.append(result)
        return toJson(json_results)

這個方法就是我們的http server監測到用戶發起get請求,並且URL是形如'http://127.0.0.1:5000/sightings/?offset=0&limit=3'的時候,我們取出limit這個值,賦給lim這個變量,然後取出offset這個值,賦給off。

然後呢?利用我們的db(就是剛才利用pymongo獲取的mongodb實例),取出‘meizi’這個collection,skip(off)的意思就是跳過前面多少行,limit(lim)表示從數據庫取出多少個值。

整句話的意思就是,從meizi這個collection裡跳過前off個值,取後面的lim個值。

現在取到的數據都在results變量裡,我們遍歷results,放入json_results這個數組裡,然後把數組轉換成json格式返回給客戶端。

我們運行一下試試。

python server.py

1431998870294249.gif

perfect!

數據已經返回給浏覽器了。

這時候我們編寫一個簡單的iOS客戶端,驗證一下。

我們建立一個swift的iOS程序,用cocoapods安裝下列庫。

platform :ios, '8.0'
use_frameworks!
target 'HotGirls' do
pod 'Alamofire'
pod 'Kingfisher'
end
target 'HotGirlsTests' do
end

注意,cocoapods務必升級到最新版,要不然安裝swift的第三方庫會出現問題。  

我寫了一個超簡單的iOS客戶單,純驗證下服務端是否有效。  

class ViewController: UIViewController {
    @IBOutlet weak var mTableView: UITableView!
    var imageURLStringArray:NSMutableArray = NSMutableArray()
    override func viewDidLoad() {
        super.viewDidLoad()
        Alamofire.request(.GET, "http://localhost:5000/meizi/?offset=0&limit=10")
            .responseJSON { (_, _, JSON, _) in
            let resultArray:NSArray = JSON as! NSArray
                for dict in resultArray{
                    self.imageURLStringArray.addObject(dict["datasrc"] as! String)
                }
            print(self.imageURLStringArray)
            self.mTableView.reloadData()
        }
        // Do any additional setup after loading the view, typically from a nib.
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
extension ViewController:UITableViewDelegate, UITableViewDataSource{
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell:ImageTableViewCell = tableView.dequeueReusableCellWithIdentifier("ImageViewCellID") as! ImageTableViewCell
        var imageURL:NSString = imageURLStringArray[indexPath.row] as! NSString
        cell.meiziImageView.kf_setImageWithURL(NSURL(string: imageURL as String)!)
        return cell
    }
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return imageURLStringArray.count
    }
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 250.0
    }

運行一下。

哈哈,大功告成。數據返回正確。

github地址:https://github.com/zangqilong198812/MeiziServer

但是,這樣就結束了麼?

遠遠沒有,爬蟲完全是個半成品,服務端簡直就是個玩笑,客戶端什麼炫酷特效交互都沒有,做這樣的東西簡直是打自己的臉。

現在,萬裡長征只開始了一小步。

1.爬蟲如何自己定時運行?

2.Mongodb如何避免插入重復數據?

3.Server如何提供多個接口。

4.如何Put,delete,Post,Get

5.POP,AsyncDisplaykit,collectionviewlayout,Custom Transition,Bézier curve,Private Cocoapods,Continuous integration,Unit Test.

現在,到了我展現真正實力的時候了。

5.jpeg

請允許我小小的裝一下13

我們經過之前的學習,掌握了哪些內容呢?

1.學會了如何用python的scrapy框架來趴一些數據。

2.學會了把爬蟲爬來的數據存入mongodb。

3.學會了怎麼用Flask寫一個簡單的接口。

那麼,從現在開始,我們會把注意力集中在如何寫一個iOS app上。(主要原因是我這個server端菜??還在看服務端的資料,所以只能先做app了)。

先看一張圖。

1-1.gif

我們的app大概長這個樣子。

關注我微博的人應該知道我前段時間做了一個card效果,主要也是為了寫這個界面,因為card那個效果應該是整個頁面最復雜的了。飯要一口一口吃吃,我們今天先講一個簡單地,就是圖中顯示PASSES的那個圓形view。

這個動畫加載的順序大概是。

1.首先是一個圓形的環的形成動畫。

2.然後是數字的滾動效果。

3.細看的話,數字滾動的速度是不同的,十位數滾動的稍慢。個位數略快一些。那麼首先腦子裡的想法就是這肯定不能用一個label來顯示,用兩個label來分別顯示個位和十位比較好做動畫。

ok,我們先來做最簡單的圓環形成的動畫。

那很簡單,用CAShapelayer來做strokeEnd動畫就行了。

我們新建一個類,繼承自UIView,然後改名叫StartView.

1431999697390554.jpg

然後呢,給這個類添加一個屬性。

var progressLayer: CAShapeLayer = CAShapeLayer()

然後重寫我們的init方法。

加入下列代碼

progressLayer.lineWidth = 2
        progressLayer.fillColor = UIColor.clearColor().CGColor
        progressLayer.strokeColor = UIColor.whiteColor().CGColor
        progressLayer.path = UIBezierPath(ovalInRect: self.bounds).CGPath
        self.layer.addSublayer(progressLayer)

現在我們的StarView類是這樣的。

blob.png

然後呢,我們先看看效果,在Viewcontroller裡添加一個我們剛剛寫的StarView,運行模擬器。看看效果。

1431999752889021.jpg

很好,已經有一個圓環了。

剛才我說過,要做出畫出圓環的那種動畫要使用一個叫做strokeEnd的動畫。為什麼呢?

我早期在一篇blog中講過,CABasicAnimation的keypath來源於何處。

其實keypath就是來源於CALayer的所有屬性,也就是說,layer的所有屬性都能放到keypath裡做動畫,而CAShapeLayer有一個叫做strokeEnd的屬性,專門是用來檢測CAShapeLayer的path屬性是否被賦值,如果賦值的話就畫出path的路徑。so,我們就可以用這個strokeEnd屬性做一些繪畫的動畫。

直接看代碼。

 func startDrawCircleAnimation(){
        var pathAnimation:CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd")
        pathAnimation.fromValue = 0
        pathAnimation.toValue = 1
        pathAnimation.duration = 0.5
        progressLayer.addAnimation(pathAnimation, forKey: "pathAnimation")
    }

這裡我定義了一個函數,專門用來執行這個畫圓環的動畫,然後把動畫時長設置為0.5.

然後我們來運行一下。

1431999777130819.gif

確實是執行了畫圓環的操作,但是問題來了。繪畫的起點並不正確,起點是在三點鐘方向,而我們的原型是需要在12點鐘方向開始畫。

問題在哪呢?

問題就在progressLayer.path = UIBezierPath(ovalInRect: self.bounds).CGPath 這句話上。

因為這個函數就是默認創建一個起點在三點鐘方向的oval(圓形),所以我們通過這個函數創建的圓環是沒有辦法指定起點的。

現在我們有兩個解決方案,第一個是讓我們的layer逆時針方向做一個90度的transform,那麼我們的子layer也逆時針旋轉了九十度,剛好從3點鐘方向旋轉到了12點鐘方向。

但是這種解決方案並不合適。因為會留下很多後遺症。

比如說,你如果後期需要在cashapelayer加一些東西的話,所有的子layer全部會旋轉。

所以我們需要改變progressLayer的path的創建方式。

var radius:CGFloat = CGRectGetWidth(self.bounds)/2.0
        var center = CGPointMake(radius, radius)
        var startAngle = -M_PI_2
        var endAngle = M_PI_2*3.0
        var circlePath = UIBezierPath(arcCenter: center, radius: radius, startAngle: CGFloat(startAngle), endAngle: CGFloat(endAngle), clockwise: true)
        progressLayer.path = circlePath.CGPath

我們用畫弧線的方法畫一個圓,因為這種方法可以指定startAngle和endAngle。運行一下,看看。

1431999820854974.gif

好的,已經能正確顯示了。

接下來是我們的滾動Label。

這種能上下滾動的label最好的方法就是創建一個scrollView,然後在scrollview中添加十個label,label的內容就是數字0-10.

ok,我們新建一個類。

然後加入如下代碼。

1431999843259287.png

新建這個enum的原因就是我們的十位數和個位數彈跳的速率不同,所以我們在初始化的時候加入了一個type,用來區別創建的是十位數label還是個位數label。

然後就是創建了一個scrollview,在豎直的方向添加了十個label。

並且設置了一下每個label的字體,顏色等等屬性。

然後創建一個開始動畫的方法。

blob.png

然後我們運行一下。

1431999871193395.gif

好的,label可以滾動了。那麼其實問題也來了。

這兩個scroll的滾動速度是一樣的,而我們的需求是十位數的速度要慢一點。

可能熟悉scrollView的同學會知道scrollView有一個屬性叫做decelerationRate 可以改變滑動速率。但是這個屬性有兩個問題,1是只有在pageEnable開啟的情況下才能生效,2是無法很細致的改動。

所以我們用另一個方法,用UIView的animation方法。

代碼如下

if scrollType == .SingleDigitType{
            UIView.animateWithDuration(0.85, animations: { () -> Void in
                self.singleDigitsScroll.contentOffset = CGPointMake(0, CGRectGetHeight(self.bounds) * CGFloat(num))
            })
        }else
        {
            UIView.animateWithDuration(1, animations: { () -> Void in
                self.singleDigitsScroll.contentOffset = CGPointMake(0, CGRectGetHeight(self.bounds) * CGFloat(num))
            })
        }

相信不用我做過多解釋也能看懂。就是更改scrollView的contentoffset。

ok,核心組件已經完成。那麼我們把它組裝起來。

最後運行效果如下。

1431999892994653.gif

最後項目地址在這:https://github.com/zangqilong198812/HotGirls

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved