Python爬蟲之小說資訊爬取與資料視覺化分析

qwer1030274531發表於2021-01-09

一、小說資料的獲取

獲取的資料為起點中文網的小說推薦周榜的所有小說資訊。

原始碼對所有想要獲取的資料都有註釋。

# -*- coding: utf-8 -*-"""
Created on Mon Jan  4 22:59:11 2021
"""import requestsfrom bs4 import BeautifulSoupimport os.pathimport csvimport timeimport pymysqlimport randomclass DrawBookMessage():
    def __init__(self):
        
        """
        定義建構函式,初始化最初網址,方便後面呼叫,不必重複寫
        """
        self.baseUrl='
        #定義baseurl目標網址
    
    def User_Agent(self):
        """
        定義5個代理IP隱藏身份,用5個IP隨機選取,以防止被檢測到連結物件而終止訪問
        """
        user_agent1 = 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0'
        user_agent2 = 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19'
        user_agent3 ='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36'
        #user_agent4 ='Mozilla/5.0 (Android; Mobile; rv:14.0) Gecko/14.0 Firefox/14.0'
        #這個代理在我的開發環境中會發生訪問錯誤,作者註釋在這裡,便於提醒自己
        user_agent5 ='Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3'
        lst = [user_agent1,user_agent2,user_agent3,user_agent5]
        return random.choice(lst)#返回隨機IP
    def getHtml(self,url):
        """
        透過隨機IP訪問網頁獲取網頁的內容
        """
        user_agent = self.User_Agent()#獲取隨機IP
        headers = {"User-Agent":user_agent}
        request = requests.get(url,headers=headers).text#透過IP訪問網頁,並且獲取網頁內容請求
        return request #返回
    
    def commonsdk(self,url):
        """ 
        把文字型別轉換為<class 'bs4.BeautifulSoup'>型別,
        之後還會用BeautifulSoup庫來提取資料,如果這不是一個BeautifulSoup物件,
        我們是沒法呼叫相關的屬性和方法的,所以,這是非常重要。
        """
        html = self.getHtml(url)
        doc=BeautifulSoup(html,'lxml')#轉換為BeautifulSoup物件
        return doc    
    def get_page_size(self,url):
        '''獲取頁面總數'''
        doc = self.commonsdk(url)
        self.pageNum = doc.find("div",class_="pagination fr")['data-pagemax']
        return int(self.pageNum)
    
    def draw_base_list(self,url):
        '''初級網頁內容'''
        doc = self.commonsdk(url)
        listt=doc.find('div',class_ = "book-img-text").find_all('div',class_ = 'book-mid-info')
        for x in listt:
            self.bookName = x.find('h4').text.strip()#書名
            self.bookUrl ='https:'+x.find('h4').find('a')['href']#書的二級網址
            self.bookAuthor = x.find('p').find(class_='name').text.strip()#
            self.bookType = x.find('p').find('a',class_='').text.strip()#小說的型別
            self.bookStatus = x.find('p').find('span').text.strip()#小說更新的狀態
            self.draw_Second_list()#呼叫獲取二級網頁內容
            self.dict_data()#呼叫生成字典的函式
            
           
    def draw_Second_list(self):
        '''獲取二級網頁內容'''
        doc = self.commonsdk(self.bookUrl)
        listt1 = doc.find('div',class_="book-info")
        self.bookIntrodaction = listt1.find(class_="intro").text.strip()#獲取小說簡介
        listt2 = doc.find(class_="fans-interact cf")
        if listt2.find(class_ ='ticket rec-ticket')==None:#ticket rec-ticket為空,ticket rec-ticket hidde不為空,有月票的標籤
            self. monthTickets = listt2.find(class_ ='ticket month-ticket').find(class_ = 'num').text#小說月票
            self. weekTickets =  listt2.find(class_ ='ticket rec-ticket hidden').find(class_ = 'num').text#小說周票
        if listt2.find(class_ ='ticket rec-ticket hidden')==None:#ticket rec-ticket不為空,ticket rec-ticket hidde為空,沒有有月票的標籤
            self. monthTickets=0#沒有月票標籤時,月票為0
            self. weekTickets =  listt2.find(class_ ='ticket rec-ticket').find(class_ = 'num').text #小說周票
        self.weekWardNum = listt2.find(class_= 'rewardNum').text#小說本週打賞人數
        
        
    def dict_data(self):
        """
        定義一個人方法生成需要存入資料的字典
        """
        ctime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime());#當前爬取時間
        data={
                '書名':self.bookName,
                '作者':self.bookAuthor,
                '型別':self.bookType,
                '狀態':self.bookStatus,
                '月票':int(self. monthTickets),
                '周票':int(self.weekTickets),
                '本週打賞人數':int(self.weekWardNum),
                '本書簡介':self.bookIntrodaction,
                '爬取時間':ctime   
                }
        print(data)
        print("="*50)
        self.write_to_MySQL(data,"spiders","bookMessage")#寫入資料庫
        self.write_to_scv(data,'bookMessage.csv')#寫入.CVS檔案
        pass
    
    def write_to_scv(self,dic,filename):
        """寫入csv檔案"""
        file_exists = os.path.isfile(filename)#判斷是否為檔案
        with open(filename, 'a',encoding='gb18030',newline='') as f: #a表示追加模式不覆蓋原檔案中的內容,newline = "" 表示讀取的換行符保持不變,原來是啥,讀出來還是啥
            headers=dic.keys()
            w =csv.DictWriter(f,delimiter=',',lineterminator='\n',fieldnames=headers)#建立一個物件
            if not file_exists :
                 w.writeheader()
            w.writerow(dic)#單行寫入
        print('當前行寫入csv成功!')
        pass
        
    def write_to_MySQL(self,dic,database,table_name): 
        """ 寫入資料庫"""
        keys = ', '.join(dic.keys())
        values = ', '.join(['% s'] * len(dic))#動態的構造佔位符
        db = pymysql.connect(host='localhost', user='root', password=(自己的資料庫密碼), port=3306, db=database)#連線資料庫
        cursor = db.cursor()#資料庫連線物件
        sql = 'INSERT INTO {table}({keys}) VALUES ({values})'.format(table=table_name, keys=keys, values=values)#插入語句
        try:
            if cursor.execute(sql, tuple(dic.values())):
                 print('Successful')
                 db.commit()#commit是把查詢語句提交到資料庫內
        except:
            print('Failed')
            db.rollback()
        cursor.close()#關閉物件
        db.close()#關閉資料庫釋放資源
    
  if __name__ == '__main__':
    """主函式"""
    drawBook = DrawBookMessage()
    page = drawBook.get_page_size(drawBook.baseUrl)
    for x in range(1,page+1):
        drawBook.draw_base_list(drawBook.baseUrl+'?page='+str(x))
    123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145

看到這裡不知道是否對你有所幫助?看不懂的可以評論區留言,順便點個贊。嘻嘻嘻嘻嘻嘻!
在這裡插入圖片描述

二、資料的分析與視覺化

相信大家對pandas都有了解吧,我就直入主題——資料分析。

2.1、Python讀取資料表時,有時候會發生一個很頑固的錯誤

在這裡插入圖片描述
在這裡一般性如果表中有中文的話,讀表的時候就會報這個錯誤,這裡把預設編碼改為gbk一般就能解決這個問題。即encoding=‘gbk’。

2.2、檢視錶的統計資訊

(1)describe()檢視錶的相關資訊資訊

data.describe()
   # count:數量統計,此列共有多少有效值
   #std:標準差
   #min:最小值
   #25%:四分之一分位數
   #50%:二分之一分位數
   #75%:四分之三分位數
   #max:最大值
   #mean:均值123456789

在這裡插入圖片描述
透過這個結果可以得出各票數的情況和打賞的人數。
(2)根據周票數對小說排序
我們透過排序可以篩選出周票數前5的小說

data.sort_values(by = '周票',ascending = False).head(5)1

在這裡插入圖片描述
可以知道當前這5本小說人氣很高。很多人還喜歡看完結的小說,畢竟一次性看完的感覺特別爽。我們也把幾本完結的小說獲取出來

data.loc[data['狀態']=='完本'].sort_values(by = '周票',ascending = False)1

在這裡插入圖片描述
可以得出最近100流行小說中只有2本是完結的。

2.3視覺化圖分析 anhui/

(1)折線圖
對於讀者,都有不同的喜好都市、有的喜好玄幻、有的喜歡輕小說……讓我們看一下最近這些小說各票數綜合情況。透過下面打程式碼可以得出折線圖情況。

import matplotlib.pyplot as pltimport pandas as pdimport numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] #解決橫座標不能顯示中文的況plt.rcParams['axes.unicode_minus'] = False#解決橫座標不能顯示中文的情況#三條縱座標的值y1 = data.groupby('型別').sum()['本週打賞人數']#求和y2 = data.groupby('型別').mean()['周票']#求平均值y3 = data.groupby('型別').mean()['月票']#求平均值x=list(dict(y1).keys())#橫座標值fig = plt.figure(figsize=(8,6), dpi=100)#指定畫布大小plt.plot(x,y1,c='red',label='打賞票和')指定折線的顏色和標籤
plt.plot(x,y2,c='green',label='周票均值')plt.plot(x,y3,c='blue',label='月票均值')plt.legend(loc='upper left')#標籤靠左plt.ylabel('周票平局值、月票平局值、打賞票和',fontsize=15)改變定橫座標名稱以及字型大小。
plt.title("小說票數折線圖")#圖名plt.xlabel('小說型別',fontsize=15)plt.show()12345678910111213141516171819

在這裡插入圖片描述
透過折線圖可以直觀的看書流行的趨勢,可以分析到奇幻小說最受歡迎,讀者最多。

(2)柱狀圖 changchun

在這裡插入程式碼片plt.rcParams['figure.figsize']=(8,3)#圖形大小data.groupby(['型別']).mean().plot(kind = 'bar')plt.xticks(rotation=0)#橫座標的角度plt.ylabel('number of people',fontsize = 15)#縱座標名plt.xlabel('Date',fontsize = 15)#橫座標名12345

在這裡插入圖片描述
柱狀圖可以出月票、周票、以及打賞人數沒有很直接的關係。比如奇幻周票高,但是其他兩項的票數卻非常低。

(3)餅狀圖

sizes = []for booktype in x:#x是上文折線圖中橫座標,即小說所有的型別。
    bookTypeNum=len(data[data['型別']==booktype])#獲取各種小說的數量
    sizes.append(bookTypeNum)plt.figure(figsize=(10,15)) #調節圖形大小plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = Falseplt.pie(
    sizes,
    labels=x,#指定顯示的標籤
    autopct='%1.1f%%'#資料保留固定小數位)#plt.axis('equal')# x,y軸刻度設定一致#本文中可以不用plt.title('小說型別受歡迎的分佈圖比')plt.legend(loc='upper left')# 左上角顯示plt.show()12345678910111213141516

在這裡插入圖片描述
透過餅狀圖可以得出奇幻、都市、仙俠、輕小說幾類小說很受大家追捧。
(4)詞雲圖

import jiebaimport wordcloud
string = ''for i in range(len(x)):
    string = str + (x[i])*int(sizes[i])print(string)string=' '.join(string)w = wordcloud.WordCloud(background_color='white',font_path='simfang.ttf')#這裡指明font_path時的字型一定要是自己電腦C:\windows\Fonts下包含有的字型,不然會報錯,或者出現亂碼。w.generate(string)w.to_file(r"bookMessage.png")12345678910

在這裡插入圖片描述
根據詞雲的字的大小可以看出當下最受大家追捧的小說的型別。
(5)散點圖

import matplotlib.pyplot as pltimport numpy as np
plt.rcParams['figure.figsize']=(12,8)np.random.seed(0) #執行多次每次獲取的隨機數都是一樣的colors = np.random.rand(100)size = np.random.rand(20)*1000#隨機大小plt.scatter(a,b,c=colors,s=size,alpha=0.7,marker='*')plt.xlabel('月票數',fontsize = 15)#橫座標名plt.ylabel('周票數',fontsize = 15)#縱座標名plt.title("月票和周票散點圖")plt.show()1234567891011

在這裡插入圖片描述
透過散點圖可以得出大部分小說的月票和周票的數目很少,只有少數的小說月票和周票數目多。往往這些小說就是當下最火的小說。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2748646/,如需轉載,請註明出處,否則將追究法律責任。

相關文章