Python爬蟲-博客園首頁推薦博客排行(整合詞雲+郵件發送)


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郵箱內嵌套圖片,自己去官網找的。)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM