在成功獲取微博用戶的列表之后,我們可以對每個用戶的主頁內容進行爬取了
環境
tools
1、chrome及其developer tools
2、python3.6
3、pycharm
Python3.6中使用的庫
1 import urllib.error 2 import urllib.request 3 import urllib.parse 4 import urllib 5 import json 6 import pandas as pd 7 import time 8 import random 9 import re 10 from datetime import datetime 11 from lxml import etree
爬取字段確定
首先,我們只管的瀏覽用戶主頁,點擊全部微博,觀察我們能獲取到的信息:
- 用戶id
- 微博id
- 微博時間
- 微博內容
- 微博發布平台
- 微博評論數
- 微博點贊數
- 微博轉發數
- 原微博id
- 原微博用戶id
- 原微博用戶名
- 原微博內容
- 原微博評論數
- 原微博點贊數
- 原微博轉發數
然后,我們利用Chrome的developer tools觀察用戶個人主頁所能獲取到的主要內容,發現有些轉發內容如果過長,無法直接通過用戶主頁進行爬取,而需要點進該條微博鏈接,對原微博進行爬取。
因此,我們可以爬取原微博的url,通過解析原微博url的內容來獲取原微博的具體內容。
最終,通過綜合情況,最后確定的字段為:
- 用戶id——uid
- 微博id——mid
- 微博時間——time
- 微博發布平台——app_source
- 微博內容——content
- 微博評論數、點贊數、轉發數——others
- 微博地址——url
- 是否轉發——is_repost
- 原微博id——rootmid
- 原微博用戶id——rootuid
- 原微博名——rootname
- 原微博地址——rooturl
加載頁包抓取
在對用戶的微博內容進行爬取時,最為困難的是解決網頁加載的問題。微博需要兩次加載,才能載入微博的全部內容,並進入下一頁,因此,如何抓取到加載頁的包是我們工作中最為重要的部分。
這里,我們需要借助Chrome的開發者工具,抓取頁面加載時出現的包
發現加載的時間段中,出現了一個xhr類型的文件,長得最像我們需要的加載包:
https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=100505&profile_ftype=1&is_all=1&pagebar=0&pl_name=Pl_Official_MyProfileFeed__22&id=1005051956890840&script_uri=/p/1005051956890840/home&feed_type=0&page=1&pre_page=1&domain_op=100505&__rnd=1517384223025
再加載一次試驗一下,發現出現它又出現了:
https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=100505&profile_ftype=1&is_all=1&pagebar=1&pl_name=Pl_Official_MyProfileFeed__22&id=1005051956890840&script_uri=/p/1005051956890840/home&feed_type=0&page=1&pre_page=1&domain_op=100505&__rnd=1517384677638
看到pl_name=Pl_Official_MyProfileFeed__22基本上就准了,為了保險起見,我們再點開鏈接看看,然后發現——果然是熟悉的配方,熟悉的味道~~
仔細解析這段url,發現:
- is_all是頁面屬性,表示全部微博
- page和pre_page都表示頁數
- id是用戶id【uid】和domain【100505】的結合體
- script_uri是當前用戶的主頁url字段
- pagebar長得最像加載頁,第一個加載頁為0,第二個加載頁為1
- __rnd是時間戳,可以省略
結合初始網頁沒有pre_page和pagebar這兩個字段,我們去掉這兩個字段,運行一下url,觀察一下所得到的內容,發現為加載前的用戶發布的微博內容。
因此我們可以將用戶主頁的每一頁分為三個部分,分成三個url進行解析,獲取整個頁面的內容。
具體代碼如下:
1 # 初始化url 2 def getBeginURL(self): 3 begin_url = 'https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=100505&is_all=1&id=100505'+str(self.uid)+\ 4 '&script_uri=/u/1956890840&domain_op=100505&page=' 5 return begin_url 6 7 # 設置加載頁url,並獲取html內容 8 def getHTML(self,page_num,extend_part = ''): 9 # extend_part為獲取加載頁的擴展字段 10 url = self.getBeginURL()+str(page_num)+extend_part 11 data = urllib.request.urlopen(url).read().decode('utf-8') 12 html = json.loads(data)['data'] 13 return html 14 15 for x in range(3): 16 if x == 0: # 初始頁面 17 extend_part = '' 18 elif x == 1: 19 b = x - 1 20 extend_part = '&pre_page=' + str(i) + '&pagebar=' + str(b) 21 elif x == 2: 22 b = x - 1 23 extend_part = '&pre_page=' + str(i) + '&pagebar=' + str(b) 24 html = self.getHTML(i, extend_part) 25 page = etree.HTML(html)
以上,最為頭大的問題就解決啦~
博主寫代碼的時候為了解決加載包的問題頭疼了好幾天,結果發現Chrome的開發者工具比我想的還要強大的多,不由的感嘆自己的愚蠢。發現了加載包的規律有,后面的一切都水到渠成,迅速的完成了微博內容爬取的代碼~
下面是我的代碼,各位可以參考。
博主為了可以實時更新內容,設置了爬取微博的時間段,可以避免每次都爬取頁數而造成微博重復爬取的麻煩。
代碼還有很多需要改進的地方,希望各位多多交流~

1 import urllib.error 2 import urllib.request 3 import urllib.parse 4 import urllib 5 import json 6 import pandas as pd 7 import time 8 import random 9 import re 10 from datetime import datetime 11 from datetime import timedelta 12 from lxml import etree 13 14 class getWeiboContent(): 15 """ 16 微博內容爬取: 17 mid 18 time 19 app_source 20 content 21 url 22 others(repost, like, comment) 23 is_repost 24 rootmid 25 rootname 26 rootuid 27 rooturl 28 """ 29 def __init__(self, uid, begin_date=None, begin_page=1, interval=None, flag=True): 30 self.uid = uid # 微博用戶ID 31 self.begin_page = begin_page # 起始頁 32 self.interval = interval # 需要爬取的頁數,默認為None 33 self.begin_date = begin_date # 爬取的微博的起始發布日期,默認為None 34 self.flag = flag 35 36 # 初始化url 37 def getBeginURL(self): 38 begin_url = 'https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=100505&is_all=1&id=100505'+str(self.uid)+\ 39 '&script_uri=/u/1956890840&domain_op=100505&page=' 40 return begin_url 41 42 # 設置加載頁url,並獲取html內容 43 def getHTML(self,page_num,extend_part = ''): 44 url = self.getBeginURL()+str(page_num)+extend_part 45 print(url) 46 data = urllib.request.urlopen(url).read().decode('utf-8') 47 html = json.loads(data)['data'] 48 return html 49 50 # 爬取每條微博的內容,輸出字典 51 def getContent(self,node): 52 dic = {} 53 dic['mid'] = node.xpath('./@mid')[0] 54 print('mid:'+dic['mid']) 55 dic['time'] = node.xpath('.//div[@class="WB_from S_txt2"]/a[1]/@title')[0] 56 app_source = node.xpath('.//div[@class="WB_from S_txt2"]/a[2]/text()') 57 if len(app_source) !=0 : # 部分微博不顯示客戶端信息 58 dic['app_source'] = app_source[0] 59 content = node.xpath('./*/*/div[@class="WB_text W_f14"]')[0].xpath('string(.)') 60 dic['content'] = re.compile('\n\s*(.*)').findall(content)[0] 61 others = node.xpath('.//ul[@class="WB_row_line WB_row_r4 clearfix S_line2"]//span[@class="line S_line1"]/span/em[2]/text()') 62 dic['repost_num'] = others[1] 63 dic['comment_num'] = others[2] 64 dic['like_num'] = others[3] 65 detail_info = node.xpath('./div[@class="WB_feed_handle"]/div/ul/li[2]/a/@action-data')[0] 66 dic['url'] = re.compile('&url=(.*?)&').findall(detail_info)[0] 67 rootmid = node.xpath('./@omid') 68 # 判斷是否存在轉發微博 69 if len(rootmid) != 0: 70 dic['is_repost'] = 1 71 dic['rootmid'] = rootmid[0] 72 weibo_expend = node.xpath('./*/*/div[@class="WB_feed_expand"]')[0] 73 rootname = weibo_expend.xpath('./*/*/a[@class="W_fb S_txt1"]/@nick-name') 74 # 判斷原博是否被刪除 75 if len(rootname) != 0: 76 dic['rootuid'] = re.compile('rootuid=(.*?)&').findall(detail_info)[0] 77 dic['rootname'] = re.compile('rootname=(.*?)&').findall(detail_info)[0] 78 dic['rooturl'] = re.compile('rooturl=(.*?)&').findall(detail_info)[0] 79 80 return dic 81 82 # 獲取微博內容 83 def getWeiboInfo(self): 84 i = self.begin_page 85 # 判斷是否划定了爬取頁數 86 if self.interval is None: 87 # 若未划定爬取頁數,則設置自動翻頁參數hasMore=True 88 hasMore = True 89 end_page = False 90 else: 91 # 若划定爬取頁數,則爬取頁數優先 92 end_page = self.begin_page+self.interval 93 hasMore = False 94 # 初始化一個DataFrame用於存儲數據 95 weibo_df = pd.DataFrame() 96 while (i <= end_page | hasMore) and self.flag: 97 for x in range(3): 98 if x == 0: # 初始頁面 99 extend_part = '' 100 elif x == 1: # 第一個加載頁 101 b = x-1 102 extend_part = '&pre_page=' + str(i) + '&pagebar=' + str(b) 103 elif x == 2: # 第二個加載頁 104 b = x-1 105 extend_part = '&pre_page=' + str(i) + '&pagebar=' + str(b) 106 html = self.getHTML(i, extend_part) 107 page = etree.HTML(html) 108 if page is None: 109 break 110 else: 111 detail = page.xpath('//div[@class="WB_cardwrap WB_feed_type S_bg2 WB_feed_like "]') 112 # 判斷用戶是否發過微博 113 if len(detail) == 0: 114 print('該用戶並未發過微博') 115 break 116 weibo = {} 117 weibo['mid'] = [] 118 weibo['time'] = [] 119 weibo['content'] = [] 120 weibo['app_source'] = [] 121 weibo['url'] = [] 122 weibo['repost_num'] = [] 123 weibo['comment_num'] = [] 124 weibo['like_num'] = [] 125 weibo['is_repost'] = [] 126 weibo['rootmid'] = [] 127 weibo['rootname'] = [] 128 weibo['rootuid'] = [] 129 weibo['rooturl'] = [] 130 for w in detail: 131 all_info = self.getContent(w) 132 # 判斷是否設置了微博的開始日期 133 if self.begin_date is None: 134 pass 135 else: 136 weibo_dt = datetime.strptime(all_info['time'], '%Y-%m-%d %H:%M').date() 137 begin_dt = datetime.strptime(self.begin_date, "%Y-%m-%d").date() 138 # 判斷微博發布日期是否在開始日期之后 139 if begin_dt > weibo_dt: 140 # 當微博發布日期在開始日期之后時,停止爬取 141 self.flag = False 142 break 143 weibo['mid'].append(all_info.get('mid', '')) 144 weibo['time'].append(all_info.get('time', '')) 145 weibo['app_source'].append(all_info.get('app_source','')) 146 weibo['content'].append(all_info.get('content', '')) 147 weibo['url'].append(all_info.get('url', '')) 148 weibo['repost_num'].append(all_info.get('repost_num', '')) 149 weibo['comment_num'].append(all_info.get('comment_num', '')) 150 weibo['like_num'].append(all_info.get('like_num', '')) 151 weibo['is_repost'].append(all_info.get('is_repost', 0)) 152 weibo['rootmid'].append(all_info.get('rootmid', '')) 153 weibo['rootname'].append(all_info.get('rootname', '')) 154 weibo['rootuid'].append(all_info.get('rootuid', '')) 155 weibo['rooturl'].append(all_info.get('rooturl', '')) 156 weibo = pd.DataFrame(weibo) 157 weibo['uid'] = self.uid 158 weibo_df = weibo_df.append(weibo,ignore_index=True) 159 # 提取下一頁鏈接 160 if page is None: 161 break 162 else: 163 next_page = page.xpath('//a[@class="page next S_txt1 S_line1"]/@href') 164 if len(next_page) == 0: # 判斷是否存在下一頁 165 self.flag = False 166 print('已是最后一頁') 167 else: 168 page_num = re.compile('page=(\d*)').findall(next_page[0])[0] 169 i = int(page_num) 170 time.sleep(random.randint(5, 10)) # 設置睡眠時間 171 return weibo_df 172 173 if __name__=='__main__': 174 uid = input('請輸入uid:') 175 begin_date = input('請輸入日期,格式為xxxx-xx-xx:') 176 begin_page = input('請輸入開始頁,默認為1:') 177 getWeiboContent(uid, begin_date).getWeiboInfo()