快速入門Scrapy--打賞用什么措辭最吸金?

ljf_open 8年前發布 | 37K 次閱讀 Scrapy 網絡爬蟲

Scrapy是一款網絡爬蟲框架,官方文檔的描述如下:

Scrapy是一個為了爬取網站數據,提取結構性數據而編寫的應用框架。 可以應用在包括數據挖掘,信息處理或存儲歷史數據等一系列的程序中。

其最初是為了 頁面抓取 (更確切來說, 網絡抓取 )所設計的, 也可以應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。

以前寫小型爬蟲的話還可以自己寫,用urllib,BeautifulSoup,Requests什么的就能解決了,后來我發現遇到一個新問題又得重新來一遍這些代碼,又得去看前面是怎么寫的,而且自己寫容易怎么高興怎么來,代碼寫的太亂,不好維護,過段時間再來看又要花時間才能看懂。

用框架的好處就是代碼結構清晰,代碼重用,不用對新的問題又重新來一遍代碼,而且功能更強大,能快速解決自己手寫代碼所不能短時間解決的問題。

平臺

  • Windows 8.1
  • Python 2.7.10
  • 簡書

Scrapy安裝

Scrapy完美支持Python 2.x,雖然現在已經慢慢在支持Python 3.x了,但是可能還會遇到不少情況。我剛開始學習Scrapy想用Python 3.5的,都安裝好了,但是運行的時候還是有引包錯誤:

ImportError: cannot import module '_win32stdio'

搜了一些,也沒有解決,而且后面可能還會有很多問題,就暫時等一等它們的更新吧,先用回2.7,解決問題再說。
(By the way,看到了下面這個)

在Windows,Python 3.x下不能簡單的 pip install scrapy 來一條龍安裝scrapy,因為中間會出一些錯誤。
我參考了 【1】 以及 【2】 ,采用安裝wheel文件的方式極其有效。

Python 3.5下Scrapy安裝步驟:

1.安裝Python,這個不說了
2.去 http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 下載合適你的Python版本的lxml的wheel文件,我下載的是 lxml-3.4.4-cp35-none-win32.whl ,下載3.6.0版本好像不得行,在我的平臺上報錯:
lxml-3.6.0-cp35-cp35m-win32.whl is not a supported wheel on this platform ,不支持我的平臺。

下載完后,將whl文件拷貝到Python安裝目錄下,然后cmd進入到你的Python安裝目錄,運行

pip3 install lxml-3.4.4-cp35-none-win32.whl

然后運行:

pip3 install scrapy

在cmd中輸入scrapy,如果輸出版本信息并沒有報錯,那么恭喜你,搞定了,是不是很爽!

Python 2.7下Scrapy的安裝

Python2.7下直接 pip install scrapy ,如果報錯,看報錯的內容是什么,找出問題出在哪個依賴包上,在網上搜索該包的whl文件(符合版本),直接pip install whl文件 來安裝就好了。我是問題處在twisted包上,所以去網上下載了老版本的twisted安裝的。

開始項目

項目目的

我們將所學的馬上利用到實際問題中來。[5]

爬取簡書首頁文章的打賞描述和打賞數,以企獲得打賞描述對打賞數的影響

其實打賞數這個東西和文章的質量是最相關的,但是通過大量數據的挖掘統計,是否能將這種相關性弱化一下,從而顯露出打賞描述和打賞數的關系呢?這就有趣了,值得研究。而且還可以同時學習框架和做有趣的事,豈不是人生一大樂趣。

創建Scrapy項目

通過如下語句創建Scrapy項目:

scrapy startproject jianshu2

然后會生成一個目錄jianshu,目錄結構如下:

jianshu/
    scrapy.cfg
    jianshu/
        __init__.py
        items.py
        pipelines.py
        settings.py
        spiders/
            __init__.py
  • spiders目錄存放主爬取代碼,是整個項目的核心。需要在spider下自己新建自己的爬取程序。
  • scrapy.cfg是項目的配置文件。
  • settings.py是項目的設置文件。
  • items.py定義我們要爬取的字段信息。
  • pipelines.py是項目的管道文件。

定義items.py

首先定義我們需要爬取的字段:

# -*- coding: utf-8 -*-
import scrapy

class Jianshu2Item(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    url = scrapy.Field()
    likeNum = scrapy.Field()

編寫真正的Spider主爬程序

import scrapy

class postSpider(scrapy.spiders.Spider):

    name = 'post'
    start_urls = ['http://www.jianshu.com']

    def parse(self, response):
        articles = response.xpath('//ul[@class="article-list thumbnails"]/li')
        for article in articles:
            url = article.xpath('div/h4/a/@href').extract()
            likeNum = article.xpath('div/div/span[2]/text()').extract()
            print(url,likeNum)

然后試著運行:

scrapy crawl post

來運行我們的爬蟲,中間又報了一次”No module named win32api”錯誤,直接pip install pypiwin32即可。
然后可以看到正確運行了,爬取了20篇文章后,爬蟲自動停止,cmd中打印正常。
中間用到了XPath來解析HTML,找到元素具體的位置,我們找到首頁的HTML的第一篇文章:

articles = response.xpath('//ul[@class="article-list thumbnails"]/li')

這句找到所有文章的HTML段,response是我們爬取時服務器返回的HTML。
我們看到所有文章都包含在 <ul class="article-list thumbnails"> 中,并且以 <li class=have-img> 開頭,所以就不難理解XPath中為什么這么寫了。
有BeautifulSoup基礎的同學應該很好理解XPath了。

使用Item

我們爬取數據肯定不是為了打印出來看一下就算了,而是想要保存數據,一般來說,Spider爬取到數據之后通過items返回,還記得我們之前定義的items么,這時候就可以派上用場了。
寫出完整代碼:

import scrapy
from jianshu2.items import Jianshu2Item

class postSpider(scrapy.spiders.Spider):

    name = 'post'
    start_urls = ['http://www.jianshu.com']

    def parse(self, response):
        articles = response.xpath('//ul[@class="article-list thumbnails"]/li')

        for article in articles:
            url = article.xpath('div/h4/a/@href').extract()
            likeNum = article.xpath('div/div/span[2]/text()').extract()
            item = Jianshu2Item()
            item['url'] = 'http://www.jianshu.com/'+url[0]
            if likeNum == []:
                #print(url,likeNum)
                item['likeNum'] = 0
            else:
                #print(url,int(likeNum[0].split(' ')[-1]))
                item['likeNum'] = int(likeNum[0].split(' ')[-1])
            yield item

執行 scrapy crawl post -o items.json 就把數據保存到json中了。
yield 語句提交item。
注意打賞有可能沒有,所以span也沒有,這里判斷一下。

數據如下:

[
[
{"url": "http://www.jianshu.com//p/6d7bf7d611aa", "likeNum": 1},
{"url": "http://www.jianshu.com//p/e47d86ce78d4", "likeNum": 0},
{"url": "http://www.jianshu.com//p/e69606806d6c", "likeNum": 0},
{"url": "http://www.jianshu.com//p/d7159874c59c", "likeNum": 2},
{"url": "http://www.jianshu.com//p/d38e8074ae94", "likeNum": 0},
{"url": "http://www.jianshu.com//p/6c8a0d0447cd", "likeNum": 0},
{"url": "http://www.jianshu.com//p/beff4ff80b25", "likeNum": 0},
{"url": "http://www.jianshu.com//p/d7e626cf02d7", "likeNum": 0},
{"url": "http://www.jianshu.com//p/524b13db9ce3", "likeNum": 1},
{"url": "http://www.jianshu.com//p/39449bcf9c28", "likeNum": 0},
{"url": "http://www.jianshu.com//p/970412b3c34d", "likeNum": 0},
{"url": "http://www.jianshu.com//p/2f98170f6eda", "likeNum": 1},
{"url": "http://www.jianshu.com//p/e91ab8e7a517", "likeNum": 0},
{"url": "http://www.jianshu.com//p/59a6caf3d965", "likeNum": 1},
{"url": "http://www.jianshu.com//p/ee5432e57dd3", "likeNum": 0},
{"url": "http://www.jianshu.com//p/00b7662bd335", "likeNum": 0},
{"url": "http://www.jianshu.com//p/1815b4071362", "likeNum": 1},
{"url": "http://www.jianshu.com//p/b00f7a2f0295", "likeNum": 0},
{"url": "http://www.jianshu.com//p/7f5fc5a01b75", "likeNum": 0},
{"url": "http://www.jianshu.com//p/84c10f2cf100", "likeNum": 0}
]

我們想將數據直接存在CSV這樣的文件中怎么辦呢?方法就是使用 Feed exports ,在settings.py文件中添加:

FEED_URI=u'D:\Python27\jianshu2\jianshu2\spiders\data.csv'
FEED_FORMAT='CSV'

第一次運行 scrapy crawl post -o data.csv ,然后后面不用加-o data.csv,即可輸出到data.csv中。

獲取求打賞聲明

我們已經獲得了url和打賞數,這已經是一個巨大的進步了。
然而我們還需要根據這個url再進一步爬到文章里面去,并且我們希望在一個爬蟲里面就解決了,不想搞很多爬蟲。
這時候問題轉化為: 如何爬取需要的屬性在不同頁面的items?
這時候我們加一個屬性’quote’,這個屬性在打開url的頁面中。
這時候,看到 這里 ,仿照它的寫法,通過meta傳遞item參數,即相當于

主函數先確定一些參數(‘url’,’likeNum’),剩下的交給另一個函數去做,然后另一個函數算出’quote’參數后把item還給主函數,主函數整合一下item,然后yield生成就好了。

部分代碼:

request = Request(posturl,callback=self.parse_donate)
request.meta['item'] = item
yield request

...

def parse_donate(self, response):
        donate = response.xpath('//div[@class="support-author"]/p/text()').extract()
        item = response.meta['item']
        if len(str(donate)) == 0:
            item['quote'] = ""
        else:
            item['quote'] = str(donate[0].encode('utf-8'))

        return item

爬取多頁

這時候我們發現爬的太少了,只有20篇。又看到首頁下面有一個【點擊查看更多】按鈕,我們試著在代碼中‘按下’這個按鈕,然后獲取下面內容的url,遞歸調用parse即可添加更多的文章。

next_link = selector.xpath('//*[@id="list-container"]/div[@class="load-more"]/button/@data-url').extract()[0]
        if next_link:
            next_link = self.url + str(next_link)
            yield Request(next_link,callback=self.parse)

通過Pipeline寫入json文件

有了item之后,item會被傳遞給Item Pipeline,我們可以在pipelines.py中對item做一些操作。

Item Pipeline的典型應用如下,更多見中文文檔。

  • 清洗HTML數據
  • 驗證item中的數據
  • 查重或者丟棄
  • 保存結果到文件(json,數據庫,csv等)

于是我們編寫pipelines.py如下:

import json
import codecs

class Jianshu2Pipeline(object):

    def __init__(self):
    self.file = codecs.open('items.json','wb','utf-8')

    def process_item(self, item, spider):

        line = json.dumps(dict(item)) + "\n"
        self.file.write(line.decode("unicode_escape"))
        return item

不得不用codecs來解決編碼問題。Python在Windows下的編碼真讓人頭疼。
這時候我們寫道json中,其實url都可以去掉了,我們并不關心。
效果如下:

合并打賞描述,根據打賞數排序

修改pipelines.py文件,用一個全局的字典dict記錄每種語句的打賞數之和,然后根據打賞數排序,寫到新的csv文件中。

# -*- coding: utf-8 -*-
import json
import codecs
from operator import itemgetter

class Jianshu2Pipeline(object):

    def __init__(self):
        self.file = codecs.open('items.json','wb','utf-8')
        self.quote = {}
        self.filecsv = codecs.open('items.csv','w','utf-8')

    def process_item(self, item, spider):

        line = json.dumps(dict(item)) + "\n"
        self.file.write(line.decode("unicode_escape"))

        if item['quote'] in self.quote.keys():
            self.quote[item['quote']] += item['likeNum']
        else:
            self.quote[item['quote']] = item['likeNum']

        self.filecsv.seek(0)
        lis = sorted(self.quote.items(),key=itemgetter(1),reverse=True)
        for i in range(len(lis)):
            line2 = lis[i][0] + '\t' + str(lis[i][1]) + '\r\n'
            self.filecsv.write(line2.decode("utf-8"))

        return item

結語

由結果可以看出,第一條打賞數最多,不難理解,因為這句是默認的打賞描述,所以使用的基數很大,所以不能說明什么。由于數據量太少,只能爬6頁,所以還不是很能說明問題。但是學習scrapy,了解scrapy的目的已經初步達到了,雖然還只是初步學習。但是找出統計上相對能夠吸引人打賞的描述的目的還沒有達到,需要加大數據量。

由結果還可以看出,其實打賞描述的個性化挺強的,很多都是個人信息。所以呢,還是要大數據。

查看源碼點擊進入我的Github: 本文源碼

 

來自: http://whatbeg.com/2016/05/19/learnscrapy.html

 

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