Python爬蟲-部落格園首頁推薦部落格排行(整合詞雲+郵件傳送)

Empirefree發表於2019-05-14

1.前提

    總體思路,利用多執行緒(mutiSpider)爬取部落格園首頁推薦部落格,根據使用者名稱爬取該使用者的閱讀排行榜(TopViewPosts),評論排行榜(TopFeedbackPosts),推薦排行榜(TopDiggPosts),然後對得到的資料進行處理(合併目錄),再進行基本排序(這裡我們已閱讀排行榜為例),排序閱讀最多的文章,然後利用詞雲(wordcloud)生成圖片,最後傳送郵件給自己。(有興趣的小夥伴可以部署到伺服器上!)

  1.1參考連結:

   大神部落格:https://www.cnblogs.com/lovesoo/p/7780957.html (推薦先看這個,我是在此部落格基礎上進行改進與擴充了的)

   詞雲下載:https://www.lfd.uci.edu/~gohlke/pythonlibs/#wordcloud (我下載的這個wordcloud-1.5.0-cp36-cp36m-win32.whl)

   郵件傳送:https://www.runoob.com/python/python-email.html (菜鳥教程推薦)

 1.2實現效果:

 

 

 

2.環境配置:

  python3.6.5(對應cp36,最好記住這個,因為以後下載一些whl檔案都會用到)

  pycharm + QQ郵箱授權碼 + wordcloud-1.5.0-cp36-cp36m-win32.whl

  win10,64位(雖然我是64位,但是下載詞雲win_amd64.whl不相容,改成win32.whl就相容了)

2.0  讀者需要提供的東西

  1.詞雲所需要的圖片(我是avatar.jpg)與電腦字型(具體見View_wordcloud函式

  2.郵箱的SMTP授權碼(密碼就是授權碼)

  3.預設所有程式碼、圖片等都在同一資料夾下面。

)

2.1需要匯入的庫(詞雲 + 郵件 + 爬蟲)

  注:1.requests,beatuifulsoup,是爬蟲需要,wordcloud,jieba,是詞雲需要,smtplib,email是郵件需要,其餘都是些基本Python語法

    2.安裝wordcloud詞雲的時候容易報錯,官方連結 ,官網下載然後在本地cmd下pip install 即可。

 

3.編寫爬蟲

3.1部落格園首頁推薦部落格

  選中XHR,找到https://www.cnblogs.com/aggsite/UserStats,直接requests獲取,返回的是html格式

 

 

#coding:utf-8
import requests

r=requests.get('https://www.cnblogs.com/aggsite/UserStats')
print r.text

  然後可以需要對資料進行基本處理,一種是使用Beautiful Soup解析Html內容,另外一種是使用正規表示式篩選內容。

  其中BeautifulSoup解析時,我們使用的是CSS選擇器.select方法,查詢id="blogger_list" > ul >li下的所有a標籤元素,同時對結果進行處理,去除了"更多推薦部落格"及""部落格列表(按積分)連結。

使用正規表示式篩選也是同理:我們首先構造了符合條件的正規表示式,然後使用re.findall找出所有元素,同時對結果進行處理,去除了"更多推薦部落格"及""部落格列表(按積分)連結。

這樣我們就完成了第一步,獲取了首頁推薦部落格列表。

 1 #coding:utf-8
 2 import requests
 3 import re
 4 import json
 5 from bs4 import BeautifulSoup
 6 
 7 # 獲取推薦部落格列表
 8 r = requests.get('https://www.cnblogs.com/aggsite/UserStats')
 9 
10 # 使用BeautifulSoup解析
11 soup = BeautifulSoup(r.text, 'lxml')
12 users = [(i.text, i['href']) for i in soup.select('#blogger_list > ul > li > a') if 'AllBloggers.aspx' not in i['href'] and 'expert' not in i['href']]
13 print json.dumps(users,ensure_ascii=False)
14 
15 # 也可以使用使用正規表示式
16 user_re=re.compile('<a href="(http://www.cnblogs.com/.+)" target="_blank">(.+)</a>')
17 users=[(name,url) for url,name in re.findall(user_re,r.text) if 'AllBloggers.aspx' not in url and 'expert' not in url]
18 print json.dumps(users,ensure_ascii=False)
View Code

  然後,這裡就能獲取推薦使用者的部落格了,我們接下來需要進入某個使用者部落格,找到介面sidecolumn.aspx,這個介面返回了我們需要的資訊:隨筆分類,點選Headers檢視介面呼叫資訊,可以看到這也是一個GET型別介面,路徑含有部落格使用者名稱,且傳入引數blogApp=使用者名稱:檢視Header:

https://www.cnblogs.com/meditation5201314/mvc/blog/sidecolumn.aspx?blogApp=meditation5201314,直接傳送requests請求即可

   

 

   

 

#coding:utf-8
import requests

user='meditation5201314'
url = 'http://www.cnblogs.com/{0}/mvc/blog/sidecolumn.aspx'.format(user)
blogApp = user
payload = dict(blogApp=blogApp)
r = requests.get(url, params=payload)
print r.text

  到此,便可以獲得部落格的分類目錄及文章數量資訊,其餘2個我就不展示了,總共3個功能,獲取使用者的閱讀排行榜(TopViewPosts),評論排行榜(TopFeedbackPosts),推薦排行榜(TopDiggPosts),具體見推薦部落格 另外多執行緒爬蟲程式碼也在這裡面,比較簡單,然後就是對資料進行排序處理了。見如下程式碼

具體完整程式碼

  1 #!/usr/bin/env python 
  2 # -*- coding: utf-8 -*- 
  3 # @Time : 2019/5/7 21:37 
  4 # @Author : Empirefree 
  5 # @File : __init__.py.py 
  6 # @Software: PyCharm Community Edition
  7 
  8 import requests
  9 import re
 10 import json
 11 from bs4 import BeautifulSoup
 12 from  concurrent import futures
 13 from wordcloud import WordCloud
 14 import jieba
 15 import os
 16 from os import path
 17 import smtplib
 18 from email.mime.text import MIMEText
 19 from email.utils import formataddr
 20 from email.mime.image import MIMEImage
 21 from email.mime.multipart import MIMEMultipart
 22 
 23 def Cnblog_getUsers():
 24     r = requests.get('https://www.cnblogs.com/aggsite/UserStats')
 25     # 使用BeautifulSoup解析推薦部落格
 26     soup = BeautifulSoup(r.text, 'lxml')
 27     users = [(i.text, i['href']) for i in soup.select('#blogger_list > ul > li > a') if
 28              'AllBloggers.aspx' not in i['href'] and 'expert' not in i['href']]
 29     #print(json.dumps(users, ensure_ascii=False))
 30     return  users
 31 def My_Blog_Category(user):
 32     myusers = user
 33     category_re = re.compile('(.+)\((\d+)\)')
 34     url = 'https://www.cnblogs.com/{0}/mvc/blog/sidecolumn.aspx'.format(myusers)
 35     blogApp = myusers
 36     payload = dict(blogApp = blogApp)
 37     r = requests.get(url, params=payload)
 38     # 使用BeautifulSoup解析推薦部落格
 39     soup = BeautifulSoup(r.text, 'lxml')
 40     category = [re.search(category_re, i.text).groups() for i in soup.select('.catListPostCategory > ul > li') if
 41                 re.search(category_re, i.text)]
 42     #print(json.dumps(category, ensure_ascii=False))
 43     return dict(category=category)
 44 
 45 def getPostsDetail(Posts):
 46     # 獲取文章詳細資訊:標題,次數,URL
 47     post_re = re.compile('\d+\. (.+)\((\d+)\)')
 48     soup = BeautifulSoup(Posts, 'lxml')
 49     return [list(re.search(post_re, i.text).groups()) + [i['href']] for i in soup.find_all('a')]
 50 
 51 def My_Blog_Detail(user):
 52     url = 'http://www.cnblogs.com/mvc/Blog/GetBlogSideBlocks.aspx'
 53     blogApp = user
 54     showFlag = 'ShowRecentComment, ShowTopViewPosts, ShowTopFeedbackPosts, ShowTopDiggPosts'
 55     payload = dict(blogApp=blogApp, showFlag=showFlag)
 56     r = requests.get(url, params=payload)
 57 
 58     print(json.dumps(r.json(), ensure_ascii=False))
 59     #最新評論(資料有點不一樣),閱讀排行榜 評論排行榜 推薦排行榜
 60     TopViewPosts = getPostsDetail(r.json()['TopViewPosts'])
 61     TopFeedbackPosts = getPostsDetail(r.json()['TopFeedbackPosts'])
 62     TopDiggPosts = getPostsDetail(r.json()['TopDiggPosts'])
 63     #print(json.dumps(dict(TopViewPosts=TopViewPosts, TopFeedbackPosts=TopFeedbackPosts, TopDiggPosts=TopDiggPosts),ensure_ascii=False))
 64     return dict(TopViewPosts=TopViewPosts, TopFeedbackPosts=TopFeedbackPosts, TopDiggPosts=TopDiggPosts)
 65 
 66 
 67 def My_Blog_getTotal(url):
 68     # 獲取部落格全部資訊,包括分類及排行榜資訊
 69     # 初始化部落格使用者名稱
 70     print('Spider blog:\t{0}'.format(url))
 71     user = url.split('/')[-2]
 72     print(user)
 73     return dict(My_Blog_Detail(user), **My_Blog_Category(user))
 74 
 75 def mutiSpider(max_workers=4):
 76     try:
 77         with futures.ThreadPoolExecutor(max_workers=max_workers) as executor:  # 多執行緒
 78         #with futures.ProcessPoolExecutor(max_workers=max_workers) as executor:  # 多程式
 79             for blog in executor.map(My_Blog_getTotal, [i[1] for i in users]):
 80                 blogs.append(blog)
 81     except Exception as e:
 82         print(e)
 83 def countCategory(category, category_name):
 84     # 合併計算目錄數
 85     n = 0
 86     for name, count in category:
 87         if name.lower() == category_name:
 88             n += int(count)
 89     return n
 90 
 91 if __name__ == '__main__':
 92     #Cnblog_getUsers()
 93     #user = 'meditation5201314'
 94     #My_Blog_Category(user)
 95     #My_Blog_Detail(user)
 96     print(os.path.dirname(os.path.realpath(__file__)))
 97     bmppath = os.path.dirname(os.path.realpath(__file__))
 98     blogs = []
 99 
100     # 獲取推薦部落格列表
101     users = Cnblog_getUsers()
102     #print(users)
103     #print(json.dumps(users, ensure_ascii=False))
104 
105     # 多執行緒/多程式獲取部落格資訊
106     mutiSpider()
107     #print(json.dumps(blogs,ensure_ascii=False))
108 
109     # 獲取所有分類目錄資訊
110     category = [category for blog in blogs if blog['category'] for category in blog['category']]
111 
112     # 合併相同目錄
113     new_category = {}
114     for name, count in category:
115         # 全部轉換為小寫
116         name = name.lower()
117         if name not in new_category:
118             new_category[name] = countCategory(category, name)
119     sorted(new_category.items(), key=lambda i: int(i[1]), reverse=True)
120     print(new_category)
121     TopViewPosts = [post for blog in blogs for post in blog['TopViewPosts']]
122     sorted(TopViewPosts, key=lambda i: int(i[1]), reverse=True)
123     print(TopViewPosts)
View Code

  

 4.生成詞雲

  對推薦部落格內容進行處理(List格式),有關詞雲具體使用可以百度,簡單介紹就是在給定的img和txt生成圖片,就是把2者結合起來,font_path是自己電腦本機上的,去C盤下面搜一下就行,不一定大家都一樣。

  注:詞雲安裝:這個比較複雜,我在pycharm下面install 沒安裝好,我是先去官網下載了whl檔案,然後在cmd下

pip install  wordcloud-1.5.0-cp36-cp36m-win32.whl

  ,然後把生成的資料夾重新放入到pycharm的venv/Lib/site_packages/下面,然後就弄好了(個人推薦這種辦法,百試不爽!)

 

def View_wordcloud(TopViewPosts):
    ##生成詞雲
    # 拼接為長文字
    contents = ' '.join([i[0] for i in TopViewPosts])
    # 使用結巴分詞進行中文分詞
    cut_texts = ' '.join(jieba.cut(contents))
    # 設定字型為黑體,最大詞數為2000,背景顏色為白色,生成圖片寬1000,高667
    cloud = WordCloud(font_path='C:\\Windows\\WinSxS\\amd64_microsoft-windows-b..core-fonts-chs-boot_31bf3856ad364e35_10.0.17134.1_none_ba644a56789f974c\\msyh_boot.ttf', max_words=2000, background_color="white", width=1000,
                      height=667, margin=2)
    # 生成詞雲
    wordcloud = cloud.generate(cut_texts)
    # 儲存圖片
    file_name = 'avatar'
    wordcloud.to_file('{0}.jpg'.format(file_name))
    # 展示圖片
    wordcloud.to_image().show()
    cloud.to_file(path.join(bmppath, 'temp.jpg'))

  在上面程式碼中,我們利用cloud.to_file(path.join(bmppath, 'temp.jpg')),儲存了temp.jpg,所以後面傳送的圖片就直接預設是temp.jpg了

  5.傳送郵件:

  去QQ郵箱申請一下授權碼,然後傳送給自己就好了,內容巢狀img這個教麻煩,我查了很久,需要用cid指定一下,有點像ajax和format。

def Send_email():
    my_sender = '1842449680@qq.com'  # 發件人郵箱賬號
    my_pass = 'XXXXXXXX這裡是你的授權碼哎'  # 發件人郵箱密碼
    my_user = '1842449680@qq.com'  # 收件人郵箱賬號,我這邊傳送給自己


    ret = True
    try:

        msg = MIMEMultipart()
        # msg = MIMEText('填寫郵件內容', 'plain', 'utf-8')
        msg['From'] = formataddr(["Empirefree", my_sender])  # 括號裡的對應發件人郵箱暱稱、發件人郵箱賬號
        msg['To'] = formataddr(["Empirefree", my_user])  # 括號裡的對應收件人郵箱暱稱、收件人郵箱賬號
        msg['Subject'] = "部落格園首頁推薦部落格內容詞雲"  # 郵件的主題,也可以說是標題

        content = '<b>SKT 、<i>Empirefree</i> </b>向您傳送部落格園最近內容.<br><p><img src="cid:image1"><p>'
        msgText = MIMEText(content, 'html', 'utf-8')
        msg.attach(msgText)
        fp = open('temp.jpg', 'rb')
        img = MIMEImage(fp.read())
        fp.close()
        img.add_header('Content-ID', '<image1>')
        msg.attach(img)

        server = smtplib.SMTP_SSL("smtp.qq.com", 465)  # 發件人郵箱中的SMTP伺服器,埠是25
        server.login(my_sender, my_pass)  # 括號中對應的是發件人郵箱賬號、郵箱密碼
        server.sendmail(my_sender, [my_user, ], msg.as_string())  # 括號中對應的是發件人郵箱賬號、收件人郵箱賬號、傳送郵件
        server.quit()  # 關閉連線
    except Exception:  # 如果 try 中的語句沒有執行,則會執行下面的 ret=False
        ret = False
    if ret:
        print("郵件傳送成功")
    else:
        print("郵件傳送失敗")    

 

最終完整程式碼:

  1 #!/usr/bin/env python 
  2 # -*- coding: utf-8 -*- 
  3 # @Time : 2019/5/7 21:37 
  4 # @Author : Empirefree 
  5 # @File : __init__.py.py 
  6 # @Software: PyCharm Community Edition
  7 
  8 import requests
  9 import re
 10 import json
 11 from bs4 import BeautifulSoup
 12 from  concurrent import futures
 13 from wordcloud import WordCloud
 14 import jieba
 15 import os
 16 from os import path
 17 import smtplib
 18 from email.mime.text import MIMEText
 19 from email.utils import formataddr
 20 from email.mime.image import MIMEImage
 21 from email.mime.multipart import MIMEMultipart
 22 
 23 def Cnblog_getUsers():
 24     r = requests.get('https://www.cnblogs.com/aggsite/UserStats')
 25     # 使用BeautifulSoup解析推薦部落格
 26     soup = BeautifulSoup(r.text, 'lxml')
 27     users = [(i.text, i['href']) for i in soup.select('#blogger_list > ul > li > a') if
 28              'AllBloggers.aspx' not in i['href'] and 'expert' not in i['href']]
 29     #print(json.dumps(users, ensure_ascii=False))
 30     return  users
 31 def My_Blog_Category(user):
 32     myusers = user
 33     category_re = re.compile('(.+)\((\d+)\)')
 34     url = 'https://www.cnblogs.com/{0}/mvc/blog/sidecolumn.aspx'.format(myusers)
 35     blogApp = myusers
 36     payload = dict(blogApp = blogApp)
 37     r = requests.get(url, params=payload)
 38     # 使用BeautifulSoup解析推薦部落格
 39     soup = BeautifulSoup(r.text, 'lxml')
 40     category = [re.search(category_re, i.text).groups() for i in soup.select('.catListPostCategory > ul > li') if
 41                 re.search(category_re, i.text)]
 42     #print(json.dumps(category, ensure_ascii=False))
 43     return dict(category=category)
 44 
 45 def getPostsDetail(Posts):
 46     # 獲取文章詳細資訊:標題,次數,URL
 47     post_re = re.compile('\d+\. (.+)\((\d+)\)')
 48     soup = BeautifulSoup(Posts, 'lxml')
 49     return [list(re.search(post_re, i.text).groups()) + [i['href']] for i in soup.find_all('a')]
 50 
 51 def My_Blog_Detail(user):
 52     url = 'http://www.cnblogs.com/mvc/Blog/GetBlogSideBlocks.aspx'
 53     blogApp = user
 54     showFlag = 'ShowRecentComment, ShowTopViewPosts, ShowTopFeedbackPosts, ShowTopDiggPosts'
 55     payload = dict(blogApp=blogApp, showFlag=showFlag)
 56     r = requests.get(url, params=payload)
 57 
 58     print(json.dumps(r.json(), ensure_ascii=False))
 59     #最新評論(資料有點不一樣),閱讀排行榜 評論排行榜 推薦排行榜
 60     TopViewPosts = getPostsDetail(r.json()['TopViewPosts'])
 61     TopFeedbackPosts = getPostsDetail(r.json()['TopFeedbackPosts'])
 62     TopDiggPosts = getPostsDetail(r.json()['TopDiggPosts'])
 63     #print(json.dumps(dict(TopViewPosts=TopViewPosts, TopFeedbackPosts=TopFeedbackPosts, TopDiggPosts=TopDiggPosts),ensure_ascii=False))
 64     return dict(TopViewPosts=TopViewPosts, TopFeedbackPosts=TopFeedbackPosts, TopDiggPosts=TopDiggPosts)
 65 
 66 
 67 def My_Blog_getTotal(url):
 68     # 獲取部落格全部資訊,包括分類及排行榜資訊
 69     # 初始化部落格使用者名稱
 70     print('Spider blog:\t{0}'.format(url))
 71     user = url.split('/')[-2]
 72     print(user)
 73     return dict(My_Blog_Detail(user), **My_Blog_Category(user))
 74 
 75 def mutiSpider(max_workers=4):
 76     try:
 77         with futures.ThreadPoolExecutor(max_workers=max_workers) as executor:  # 多執行緒
 78         #with futures.ProcessPoolExecutor(max_workers=max_workers) as executor:  # 多程式
 79             for blog in executor.map(My_Blog_getTotal, [i[1] for i in users]):
 80                 blogs.append(blog)
 81     except Exception as e:
 82         print(e)
 83 def countCategory(category, category_name):
 84     # 合併計算目錄數
 85     n = 0
 86     for name, count in category:
 87         if name.lower() == category_name:
 88             n += int(count)
 89     return n
 90 
 91 def View_wordcloud(TopViewPosts):
 92     ##生成詞雲
 93     # 拼接為長文字
 94     contents = ' '.join([i[0] for i in TopViewPosts])
 95     # 使用結巴分詞進行中文分詞
 96     cut_texts = ' '.join(jieba.cut(contents))
 97     # 設定字型為黑體,最大詞數為2000,背景顏色為白色,生成圖片寬1000,高667
 98     cloud = WordCloud(font_path='C:\\Windows\\WinSxS\\amd64_microsoft-windows-b..core-fonts-chs-boot_31bf3856ad364e35_10.0.17134.1_none_ba644a56789f974c\\msyh_boot.ttf', max_words=2000, background_color="white", width=1000,
 99                       height=667, margin=2)
100     # 生成詞雲
101     wordcloud = cloud.generate(cut_texts)
102     # 儲存圖片
103     file_name = 'avatar'
104     wordcloud.to_file('{0}.jpg'.format(file_name))
105     # 展示圖片
106     wordcloud.to_image().show()
107     cloud.to_file(path.join(bmppath, 'temp.jpg'))
108 
109 def Send_email():
110     my_sender = '1842449680@qq.com'  # 發件人郵箱賬號
111     my_pass = 'XXXXXXXX'  # 授權碼
112     my_user = '1842449680@qq.com'  # 收件人郵箱賬號,我這邊傳送給自己
113 
114 
115     ret = True
116     try:
117 
118         msg = MIMEMultipart()
119         # msg = MIMEText('填寫郵件內容', 'plain', 'utf-8')
120         msg['From'] = formataddr(["Empirefree", my_sender])  # 括號裡的對應發件人郵箱暱稱、發件人郵箱賬號
121         msg['To'] = formataddr(["Empirefree", my_user])  # 括號裡的對應收件人郵箱暱稱、收件人郵箱賬號
122         msg['Subject'] = "部落格園首頁推薦部落格內容詞雲"  # 郵件的主題,也可以說是標題
123 
124         content = '<b>SKT 、<i>Empirefree</i> </b>向您傳送部落格園最近內容.<br><p><img src="cid:image1"><p>'
125         msgText = MIMEText(content, 'html', 'utf-8')
126         msg.attach(msgText)
127         fp = open('temp.jpg', 'rb')
128         img = MIMEImage(fp.read())
129         fp.close()
130         img.add_header('Content-ID', '<image1>')
131         msg.attach(img)
132 
133         server = smtplib.SMTP_SSL("smtp.qq.com", 465)  # 發件人郵箱中的SMTP伺服器,埠是25
134         server.login(my_sender, my_pass)  # 括號中對應的是發件人郵箱賬號、郵箱密碼
135         server.sendmail(my_sender, [my_user, ], msg.as_string())  # 括號中對應的是發件人郵箱賬號、收件人郵箱賬號、傳送郵件
136         server.quit()  # 關閉連線
137     except Exception:  # 如果 try 中的語句沒有執行,則會執行下面的 ret=False
138         ret = False
139     if ret:
140         print("郵件傳送成功")
141     else:
142         print("郵件傳送失敗")
143 
144 if __name__ == '__main__':
145     #Cnblog_getUsers()
146     #user = 'meditation5201314'
147     #My_Blog_Category(user)
148     #My_Blog_Detail(user)
149     print(os.path.dirname(os.path.realpath(__file__)))
150     bmppath = os.path.dirname(os.path.realpath(__file__))
151     blogs = []
152 
153     # 獲取推薦部落格列表
154     users = Cnblog_getUsers()
155     #print(users)
156     #print(json.dumps(users, ensure_ascii=False))
157 
158     # 多執行緒/多程式獲取部落格資訊
159     mutiSpider()
160     #print(json.dumps(blogs,ensure_ascii=False))
161 
162     # 獲取所有分類目錄資訊
163     category = [category for blog in blogs if blog['category'] for category in blog['category']]
164 
165     # 合併相同目錄
166     new_category = {}
167     for name, count in category:
168         # 全部轉換為小寫
169         name = name.lower()
170         if name not in new_category:
171             new_category[name] = countCategory(category, name)
172     sorted(new_category.items(), key=lambda i: int(i[1]), reverse=True)
173     print(new_category)
174     TopViewPosts = [post for blog in blogs for post in blog['TopViewPosts']]
175     sorted(TopViewPosts, key=lambda i: int(i[1]), reverse=True)
176     print(TopViewPosts)
177 
178     View_wordcloud(TopViewPosts)
179     Send_email()
View Code

 

    總結:總體功能就是根據推薦部落格,爬取推薦使用者的閱讀排行榜 評論排行榜 推薦排行榜,然後資料處理成,將處理好的資料整合成詞雲,最後傳送給使用者

 難點1:爬取使用者和部落格所用到的一系列爬蟲知識(正則,解析等等)

 難點2:詞雲的安裝(確實挺麻煩的。。。。。)

 難點3:郵件傳送內容巢狀image(菜鳥教程沒有給出QQ郵箱內巢狀圖片,自己去官網找的。)

相關文章