python3.6.5 爬取糗事百科,開心一下

五力發表於2018-07-10
最近學習requests和lxml的用法。總想找個小專案來練練手,想了想,就爬個糗事百科來練習一下吧。


主要待實現以下功能:
1. 每按回車,就顯示一條糗百,開心一下.
2. 輸入open再回車,則用預設瀏覽器開啟當前這條糗百
3. 輸入q再回車,退出小程式
4. 如輸入的是數字,則代表只有>=輸入數字點贊數的內容才能顯示
5. 過濾掉image/video,只顯示純text的糗事
6. 顯示的形式為:當前序號/內容/使用者投票資訊/當前糗百的Link
 
主要使用的第三方庫為requests+lxml
當前code並未做異常處理,如在網路不通或不暢應該會出現異常報錯退出,有興趣的同學請自行完善。


總思路分為以下幾個部分:
1. 初始化首頁的內容(糗百部分)
2. 獲取非首頁的內容
3. 總獲取的內容中獲取一條糗百資訊用於顯示. 


用的第三方庫為requests + lxml,安裝方法:
pip install requests

pip install lxml



話不多說,上code吧.

#!/user/bin/env python3
#-*- coding:utf-8 -*-

'''
主要待實現以下功能:
1. 每按回車,就顯示一條糗百,開心一下.
2. 輸入open再回車,則用預設瀏覽器開啟當前這條糗百
3. 輸入q再回車,退出小程式
4. 如輸入的是數字,則代表只有>=輸入數字點贊數的內容才能顯示
5. 過濾掉image/video,只顯示純text的糗事
6. 顯示的形式為:當前序號/內容/使用者投票資訊/當前糗百的Link
 
主要使用的第三方庫為requests+lxml+xpath
當前code並未做異常處理,如在網路不通或不暢應該會出現異常報錯退出,有興趣的同學請自行完善。
'''

from lxml import etree
from time import sleep
import requests
import os
import webbrowser

class SpideQSBK:

    def __init__(self):
        self.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36'}
        # 糗事百科頁碼
        self.pagenum = 2
        # 用於記錄當前處於datalist中的位置
        self.curdatalistId = 0
        # 最低點贊數(顯示的內容由最低點贊數約束,default=0)
        self.mininumOfZan = 0
        # 糗事內容儲存在datalist中
        self.datalist = self.initQSData()
        
    def saveQbrec2Txt(self, qbrecordlist, file):
        for item in range(len(qbrecordlist)):
            if item >= len(qbrecordlist)-2:
                file.write('\n')
            file.write(qbrecordlist[item].replace('\n', ''))
            file.write('\n\n')
    
    # 初始化資料,獲取首頁的資料
    def initQSData(self):
        # 首頁的url
        url = 'https://www.qiushibaike.com'
        # 獲取首頁的資料
        initdatalist = self.getPageData(url)
        return initdatalist
    
    # 過濾指定url頁面的資料
    def getPageData(self, url):
        # 用來儲存頁面的資料
        pageDataList = []
        r = requests.get(url, headers = self.headers).text
        html = etree.HTML(r)
        
        # 當前頁面糗百的數目.
        pageTotalQiubaiNum = len(html.xpath('//div[@id="content-left"]/child::*'))
        # 當前糗百的投票資訊
        voteinfo = []
        
        # 將頁面資料經過濾後來後pageDataList中
        for i in range(1, pageTotalQiubaiNum):
            filterpre = '//div[@id="content-left"]/div[' + str(i) + ']'
            findfilter = filterpre + '/a/div[@class="content"]/span/text()'
            qiushibaikecontent = html.xpath(findfilter)
        
            # 當前糗百的subpage
            href = html.xpath(filterpre + '/a[@class="contentHerf"]/@href')[0]
            # 投票情況數(點贊數)
            voteinfo = html.xpath(filterpre + '//span[@class="stats-vote"]/i[@class="number"]/text()')

            # 過濾掉點贊數不符合要求的記錄
            if int(voteinfo[0]) < self.mininumOfZan:
                continue
                
            # 投票情況數(評論數)
            discussNum = html.xpath(filterpre + '//span[@class="stats-comments"]//i[@class="number"]/text()')
            # 將兩個list合併
            voteinfo.extend(discussNum)
            voteinfo.insert(0, '點贊: ')
            voteinfo.insert(2, '     評論: ')
            # 將list轉化為字串
            voteinfostr = ''.join(voteinfo)
    
            # 判斷當前糗百是否顯示不全(如果有檢視全文欄位則是顯示不全)
            isExistViewFulltext = html.xpath(filterpre + '//span[@class="contentForAll"]')
            # 判斷當前糗百是否存在圖片
            isExistImg = html.xpath(filterpre + '//img[@class="illustration"]')
            # 判斷當前糗百是否存在video
            isExistVideo = html.xpath(filterpre + '//video')
            
            # 過濾掉帶image和video的糗百
            if len(isExistImg) == 1 or len(isExistVideo) == 1:
                continue
            
            # 如當前糗百顯示不全(有檢視全文欄位),測需要單獨開啟這首糗百來獲取完整的糗百內容
            urlpre = 'https://www.qiushibaike.com'
            if len(isExistViewFulltext) != 1:
                qiushibaikecontent.append(voteinfostr)
                qiushibaikecontent.append(urlpre + href)
                pageDataList.append(qiushibaikecontent)
            else:
                newUrl = urlpre + href
                newr = requests.get(newUrl, headers = self.headers).text
                newHtml = etree.HTML(newr)
                newqiushicontent = newHtml.xpath('//div[@class="content"]/text()')
                newqiushicontent.append(voteinfostr)
                newqiushicontent.append(newUrl)
                pageDataList.append(newqiushicontent)
                
        return pageDataList   
    
    # 載入非首頁的資料,pagenum為列表的頁碼
    def getNextpageData(self, pageNum):
        url = 'https://www.qiushibaike.com/8hr/page/' + str(pageNum)
        nextdatalist = self.getPageData(url)
        return nextdatalist 
    
    # 獲取一個糗百資料
    def getOneHappy(self):
        # 先大第一頁開始讀記錄,如讀完了就獲取下一頁的資料記錄. 
        datalistlen = len(self.datalist)-1
        
        # 如果序號要找到,就返回資料,如找不到,則再載入一個頁面的資料
        if self.curdatalistId <= datalistlen:
            data = self.datalist[self.curdatalistId]
            # 獲取資料後將當前item設定為空,以後佔太多的記憶體空間
            self.curdatalistId = self.curdatalistId + 1
            return data
        else:
            # 將新頁面的資料直接加在之前的資料後面
            self.datalist.clear()
            self.datalist = self.getNextpageData(self.pagenum)
            self.pagenum  = self.pagenum + 1
            
            timer = 0
            while len(self.datalist) == 0:
                # 為防止要求的點贊數太高一直查詢不到而不斷的迴圈
                if timer >= 10:
                    self.mininumOfZan = 0
                    return ['伺服器無響應! 點贊數限制恢復為0, 這是Server端開的一個玩笑,是不是很好笑, 我都忍不住笑出了聲, 哇哈哈哈*_*!']
                
                timer = timer + 1
                sleep(0.8)    
                print(f'正在查詢資料,第{timer}次... ...')
                self.datalist = self.getNextpageData(self.pagenum)
                self.pagenum  = self.pagenum + 1
                
            self.curdatalistId = 0
            return self.datalist[self.curdatalistId]
    
    # 開始嗨皮一下了        
    def startHappy(self):
        print('是時候嗨皮一下了(資料from糗事百科)!!!')
        print('Enter:接著嗨皮, q:退出, open:用瀏覽器檢視, 輸入數字:設定最低點贊數!')
        recordId = 2
        # 直接輸出第一條糗百
        happycontent = self.getOneHappy()
        precontent = happycontent
        print(f'\n\n第1條:\n')
        for item in range(len(happycontent)):
            if item == len(happycontent)-2:
                print('\n')
            print(happycontent[item].replace('\n', ''))
            
        while True:
            openurl = happycontent[-1].strip()
            enter = input().strip().lower()
            if enter == 'q':
                break
            elif enter == 'open':
                webbrowser.open(openurl)
            else:
                if enter.isdigit() == True:
                    self.mininumOfZan = int(enter)
                    self.datalist = self.initQSData()
                os.system('cls')
                print('是時候嗨皮一下了(資料from糗事百科)!!!')
                print('Enter:接著嗨皮, q:退出, open:用瀏覽器檢視, 輸入數字:設定最低點贊數!')
                
                happycontent = self.getOneHappy()
                # 有時happycontent會無故為空,如為空,則自動獲取下一條,直到不為空為止
                while(len(happycontent) == 0):
                    happycontent = self.getOneHappy()
                # while(cmp(happycontent, precontent) == 0):
                    # happycontent = self.getOneHappy()
                    
                precontent = happycontent
                print('\n')
                print(f'第{recordId}條:\n')
                for item in range(len(happycontent)):
                    if item == len(happycontent)-2:
                        print('\n')
                    print(happycontent[item].replace('\n', ''))
                recordId = recordId + 1
    
if __name__ == '__main__':
    kaixin = SpideQSBK()
    kaixin.startHappy()

此code在後期可能為做一定的修改,如要獲取最新code,請關注以下github:

https://github.com/vitamincqb/Toolset  下的:Happy_moment pro.py

相關文章