Scrapy-Jieba-WordCloud-Matplotlib從爬取數據到生成圖表

目標

這個小項目的目的就是通過完成從爬取數據到分析數據一整個流程的工作,來對所需要用到的工具進行學習。
整個流程大致步驟爲:

  1. 使用scrapy對網站進行爬取,獲得每一條短評內容和日期
  2. 使用jieba分詞對短評內容進行分詞
  3. 使用wordcloud根據分詞後的數據生成詞雲圖
  4. 對短評的發表日期進行統計
  5. 使用matplotlib生成短評發表日期的線狀圖

在一切開始之前順帶提一句,本次使用的是python3.

Scrapy

Scrapy是一個開源合作的網頁數據抽取框架。快速簡單且延展性強。(官網介紹)

安裝使用

通過pip安裝是最簡單的辦法。只要在安裝python時有同時安裝pip就可以通過這個途徑安裝。
安裝辦法就是使用命令行,輸入:

pip install scrapy

以下的其他包也是通過同樣途徑進行安裝。但是需要提到的是,這個安裝途徑在下載安裝包時不支持斷點續傳,也就是說如果在下載途中網絡報錯就要重來。在網絡出問題的時候,可能會反覆遇到網絡報錯導致無法下載。這時可以複製pip安裝時控制檯打印出的下載地址,通過瀏覽器或者下載工具進行下載。下載完成後,控制檯cd到下載好的whl文件所在位置,然後輸入:

pip install xxxx.whl

生成項目

Scrapy可以通過命令行命令快速生成項目。只需要在控制檯cd到想要創建項目的文件夾,然後輸入:

scrapy startproject project_name

輸入命令之後在目標文件夾就會生成一個項目。項目內有多個py文件。下面是需要改動以達成目的的py文件的介紹。

items

項目items的定義文件。在這個文件裏可以定義項目中的items的內容,用於存儲爬取到的數據。
但在本項目中,一個item只要有評論和日期兩項就夠了。

import scrapy

class commentItem(scrapy.Item):
    date = scrapy.Field()
    comment = scrapy.Field()

pipelines

pipelines是用於定義爬取到的數據如何處理。數據被爬取後整理爲多個item,然後被移交至pipeline這裏進行後續處理。
在本項目中,pipeline負責將items存到txt文件中。

import os

class ScrapyspiderPipeline(object):
    def process_item(self, item, spider):
        base_dir = os.getcwd()
        fileName_1 = base_dir + 'shawshank_douban_comment.txt'
        fileName_2 = base_dir + 'shawshank_douban_date.txt'
        with open(fileName_1,'a') as fp:
            fp.write(item['comment'])
        with open(fileName_2,'a') as fp:
            fp.write(item['date'])
        return item

需要注意的是,在pipelines.py文件自動生成的註釋中也有提到,要在settings文件中增添pipeline的信息。

settings

設置文件,在這個文件中對爬蟲進行各式各樣的設置。比如上面說到的增添pipeline的信息:

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'scrapySpider.pipelines.ScrapyspiderPipeline': 300,
}

另外還有,在爬取時爲了防止爬取請求頻率過高觸發網站反爬蟲機制,需要讓爬蟲的爬取間歇性暫停,也可以在settings文件中設置:

DOWNLOAD_DELAY = 2
RANDOMIZE_DOWNLOAD_DELAY = True

另外還可以設置請求的user-agent:

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'

還有是否遵守網站的robot規則:

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

spider

最後是爬蟲的主體,這個文件不是跟隨項目自動生成的,而是我們自己寫的。

# -*- coding: utf-8 -*-
from scrapy import Request
import scrapy
from scrapySpider.items import commentItem

import jieba


class DoubanSpider(scrapy.Spider):
    name = 'douban'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
    }

    def start_requests(self):
        return [scrapy.FormRequest('https://accounts.douban.com/login', formdata={'form_email': 'username', 'form_password': 'password', 'login': '登錄'}, callback = self.logged_in)] 

    def logged_in(self, response): 
        yield Request('https://movie.douban.com/subject/1292052/comments', callback = self.parse) 

    def parse(self, response):
        item = commentItem()
        comments = response.xpath('//div[@class="comment"]')

        for comment in comments:
            item['date'] = comment.xpath('.//span[@class="comment-time "]/text()').extract()[0][21:32]
            item['comment'] = comment.xpath('.//span[@class="short"]/text()').extract()[0]+"\n"
            yield item

        next_url = response.xpath('//a[@class="next"]/@href').extract()
        if next_url:
            next_url = 'https://movie.douban.com/subject/1292052/comments' + next_url[0]
            yield Request(next_url, headers=self.headers)

在這個文件中定義了本次項目的爬蟲‘DoubanSpider’類,它繼承自scrapy.spider類。類成員屬性有它的名字name=‘douban’,這個名字會用於啓動它。
爬蟲的成員方法包括:

  1. start_requests:用於發起請求
  2. logged_in:用於登錄,這在本個項目中需要用到,因爲豆瓣短評只允許查看前十頁,要登錄後才能查看全部。這個方法用於模擬登錄。發送的formdata的數據名‘form_email’和‘form_password’是在豆瓣登錄頁面查的。在豆瓣進行一次登錄然後在開發者工具看它發出的postrequest的數據即可。
    一張截圖
  3. parse:這個方法用於解析爬取到的頁面。在logged_in函數中設置的回調函數就是它。
    使用xpath對頁面的內容進行抽取,獲得短評內容和日期,並整理爲item
    最後在頁面中獲取下一頁的url,並把url提交給scheduler。

啓動爬蟲

在控制檯cd到爬蟲項目所在文件位置,然後輸入:

scrapy crawl douban

即可啓動爬蟲。‘douban’就是爬蟲主體中的name屬性的值。
在爬蟲運行結束後,就可以獲得兩個存儲了爬取到的數據的txt文件。
短評內容
短評日期
爬蟲的工作就到此結束。

Jieba

結巴分詞,是用於將短評內容進行分詞,以便下一步統計詞頻生成詞雲。
結巴是現成的包,只要和scrapy一樣通過pip安裝然後在py文件中import就可以使用了。

pip install jieba

結巴分詞有多種分詞模式:

  1. jieba.cut 方法接受三個輸入參數: 需要分詞的字符串;cut_all 參數用來控制是否採用全模式;HMM 參數用來控制是否使用 HMM 模型
  2. jieba.cut_for_search 方法接受兩個參數:需要分詞的字符串;是否使用 HMM 模型。該方法適合用於搜索引擎構建倒排索引的分詞,粒度比較細
    待分詞的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建議直接輸入 GBK 字符串,可能無法預料地錯誤解碼成 UTF-8
  3. jieba.cut 以及 jieba.cut_for_search 返回的結構都是一個可迭代的 generator,可以使用 for 循環來獲得分詞後得到的每一個詞語(unicode),或者用
  4. jieba.lcut 以及 jieba.lcut_for_search 直接返回 list
  5. jieba.Tokenizer(dictionary=DEFAULT_DICT) 新建自定義分詞器,可用於同時使用不同詞典。jieba.dt 爲默認分詞器,所有全局分詞相關函數都是該分詞器的映射。

不過這次就只需要用到最簡單的jieba.cut,將短評切分即可。
cut方法接收兩個參數,第一個是需要被分詞的字符串,第二是切分模式。模式有全模式和精確模式兩種。全模式會將字符串中所有可能的詞語都輸出。比如“我來到北京清華大學”,全模式會輸出“我/ 來到/ 北京/ 清華/ 清華大學/ 華大/ 大學”,而精確模式只會輸出“我/ 來到/ 北京/ 清華大學”。
本個項目選用精確模式。

WordCloud

詞雲也是現成的通過pip安裝的包。只需要調用generate方法傳入切好詞的字符串,就能自動生成詞雲圖。

text_from_file=open('scrapySpidershawshank_douban_comment.txt','r').read()
Word_spilt_jieba = jieba.cut(text_from_file,cut_all = False)
word_space = ' '.join(Word_spilt_jieba)

my_wordcloud = WordCloud(
    background_color='white', #設置背景顏色
    mask=img,  #背景圖片
    max_words = 200, #設置最大顯示的詞數
    stopwords = STOPWORDS, #設置停用詞
    #設置字體格式,字體格式 .ttf文件需自己網上下載,最好將名字改爲英文,中文名路徑加載會出現問題。
    font_path = 'DFPKingGothicGB-Thin-2.ttf', 
    max_font_size = 100, #設置字體最大值
    random_state=50, #設置隨機生成狀態,即多少種配色方案
    ).generate(word_space)

Matplotlib

這個包是用於繪製圖表的。首先是將Wordcloud生成的詞雲圖繪製出來。

import matplotlib.pyplot as plt

plt.imshow(my_wordcloud)
plt.axis('off')
plt.show()

詞雲圖
可以看到,關於《肖申克的救贖》這部電影,短評最常提及的關鍵詞除了“電影”“自己”這些沒有意義的詞外,就是“希望”“經典”“自由”。
下一步就是根據日期文件繪製線狀圖。

year = [2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018]
ycount = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

month = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
mcount = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


dates = open('scrapySpidershawshank_douban_date.txt').readlines()
for date in dates:
    y = int(date[0:4])-2005
    ycount[y] = ycount[y]+1
    m = int(date[5:7])
    mcount[m-1] = mcount[m-1]+1

pyplot.rcParams['font.sans-serif']=['SimHei']

pyplot.plot(year, ycount)
pyplot.xlabel('年')
pyplot.ylabel('短評數')
pyplot.title('《肖申克的救贖》每年短評數量')
pyplot.yticks([0, 20, 40, 60, 80, 100])
pyplot.xticks([2005, 2007, 2009, 2011, 2013, 2015, 2017, 2018])
pyplot.show()


pyplot.plot(month, mcount)
pyplot.xlabel('月份')
pyplot.ylabel('短評數')
pyplot.title('《肖申克的救贖》短評發表月份統計')
pyplot.yticks([0, 20, 40, 60, 80, 100])
pyplot.xticks([2, 4, 6, 8, 10, 12])
pyplot.show()

首先是讀日期文件,統計短評的年份和月份,存儲爲兩個數組。然後使用pyplot根據這兩個數組分別繪製線狀圖,設置橫豎座標名稱和刻度。pyplot就能繪製出相應的圖表。
短評年份統計
短評月份統計
可以看出,《肖申克的救贖》從2005年以來一直到2015年,熱度緩慢上升,在2010年至2012年到達高峯,然後逐漸下落。但是後來在2017年短評數量卻突然爆發式地増長。
而短評發佈的月份來看,總體還算平均,大致是前半年稍微多一些。

結束

這個項目到此就結束了。十分簡單的一個項目,大部分過程都是使用現有的包達成目的。對數據的分析也不算是十分複雜,但得到的結果還算是比較有趣的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章