1. 程式人生 > >[python爬蟲] Selenium爬取新浪微博內容及使用者資訊

[python爬蟲] Selenium爬取新浪微博內容及使用者資訊

登入入口
新浪微博登入常用介面:
http://login.sina.com.cn/ 
對應主介面:
http://weibo.com/
但是個人建議採用手機端微博入口:http://login.weibo.cn/login/

其原因是手機端資料相對更輕量型,同時基本資料都齊全,可能缺少些個人基本資訊,如"個人資料完成度"、"個人等級"等,同時粉絲ID和關注ID只能顯示20頁,但完全可以作為語料進行大部分的驗證。

通過比較下面兩張圖,分別是PC端手機端,可以發現內容基本一致:



手機端下圖所示,其中圖片相對更小,同時內容更精簡。




完整原始碼
下面程式碼主要分為三部分:
1.LoginWeibo(username, password) 登入微博
2.VisitPersonPage(user_id) 訪問跟人網站,獲取個人資訊
3.獲取微博內容,同時http://weibo.cn/guangxianliuyan?filter=0&page=1實現翻頁
  1. # coding=utf-8
  2. """   
  3. Created on 2016-02-22 @author: Eastmount 
  4. 功能: 爬取新浪微博使用者的資訊 
  5. 資訊:使用者ID 使用者名稱 粉絲數 關注數 微博數 微博內容 
  6. 網址:http://weibo.cn/ 資料量更小 相對http://weibo.com/ 
  7. """
  8. import time              
  9. import re              
  10. import os      
  11. import sys    
  12. import codecs    
  13. import shutil  
  14. import urllib   
  15. from selenium import webdriver          
  16. from selenium.webdriver.common.keys import Keys          
  17. import selenium.webdriver.support.ui as ui          
  18. from selenium.webdriver.common.action_chains import ActionChains  
  19. #先呼叫無介面瀏覽器PhantomJS或Firefox    
  20. #driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe")    
  21. driver = webdriver.Firefox()      
  22. wait = ui.WebDriverWait(driver,10)  
  23. #全域性變數 檔案操作讀寫資訊
  24. inforead = codecs.open("SinaWeibo_List.txt"'r''utf-8')  
  25. infofile = codecs.open("SinaWeibo_Info.txt"'a''utf-8')  
  26. #********************************************************************************
  27. #                  第一步: 登陸weibo.cn 獲取新浪微博的cookie
  28. #        該方法針對weibo.cn有效(明文形式傳輸資料) weibo.com見學弟設定POST和Header方法
  29. #                LoginWeibo(username, password) 引數使用者名稱 密碼
  30. #                             驗證碼暫停時間手動輸入
  31. #********************************************************************************
  32. def LoginWeibo(username, password):  
  33.     try:  
  34.         #**********************************************************************
  35.         # 直接訪問driver.get("http://weibo.cn/5824697471")會跳轉到登陸頁面 使用者id
  36.         #
  37.         # 使用者名稱<input name="mobile" size="30" value="" type="text"></input>
  38.         # 密碼 "password_4903" 中數字會變動,故採用絕對路徑方法,否則不能定位到元素
  39.         #
  40.         # 勾選記住登入狀態check預設是保留 故註釋掉該程式碼 不保留Cookie 則'expiry'=None
  41.         #**********************************************************************
  42.         #輸入使用者名稱/密碼登入
  43.         print u'準備登陸Weibo.cn網站...'
  44.         driver.get("http://login.weibo.cn/login/")  
  45.         elem_user = driver.find_element_by_name("mobile")  
  46.         elem_user.send_keys(username) #使用者名稱
  47.         elem_pwd = driver.find_element_by_xpath("/html/body/div[2]/form/div/input[2]")  
  48.         elem_pwd.send_keys(password)  #密碼
  49.         #elem_rem = driver.find_element_by_name("remember")
  50.         #elem_rem.click()             #記住登入狀態
  51.         #重點: 暫停時間輸入驗證碼
  52.         #pause(millisenconds)
  53.         time.sleep(20)  
  54.         elem_sub = driver.find_element_by_name("submit")  
  55.         elem_sub.click()              #點選登陸
  56.         time.sleep(2)  
  57.         #獲取Coockie 推薦 http://www.cnblogs.com/fnng/p/3269450.html
  58.         print driver.current_url  
  59.         print driver.get_cookies()  #獲得cookie資訊 dict儲存
  60.         print u'輸出Cookie鍵值對資訊:'
  61.         for cookie in driver.get_cookies():   
  62.             #print cookie
  63.             for key in cookie:  
  64.                 print key, cookie[key]  
  65.         #driver.get_cookies()型別list 僅包含一個元素cookie型別dict
  66.         print u'登陸成功...'
  67.     except Exception,e:        
  68.         print"Error: ",e  
  69.     finally:      
  70.         print u'End LoginWeibo!\n\n'
  71. #********************************************************************************
  72. #                  第二步: 訪問個人頁面http://weibo.cn/5824697471並獲取資訊
  73. #                                VisitPersonPage()
  74. #        編碼常見錯誤 UnicodeEncodeError: 'ascii' codec can't encode characters 
  75. #********************************************************************************
  76. def VisitPersonPage(user_id):  
  77.     try:  
  78.         global infofile  
  79.         print u'準備訪問個人網站.....'
  80.         #原創內容 http://weibo.cn/guangxianliuyan?filter=1&page=2
  81.         driver.get("http://weibo.cn/" + user_id)  
  82.         #**************************************************************************
  83.         # No.1 直接獲取 使用者暱稱 微博數 關注數 粉絲數
  84.         #      str_name.text是unicode編碼型別
  85.         #**************************************************************************
  86.         #使用者id
  87.         print u'個人詳細資訊'
  88.         print'**********************************************'
  89.         print u'使用者id: ' + user_id  
  90.         #暱稱
  91.         str_name = driver.find_element_by_xpath("//div[@class='ut']")  
  92.         str_t = str_name.text.split(" ")  
  93.         num_name = str_t[0]      #空格分隔 獲取第一個值 "Eastmount 詳細資料 設定 新手區"
  94.         print u'暱稱: ' + num_name   
  95.         #微博數 除個人主頁 它預設直接顯示微博數 無超連結
  96.         #Error:  'unicode' object is not callable
  97.         #一般是把字串當做函式使用了 str定義成字串 而str()函式再次使用時報錯
  98.         str_wb = driver.find_element_by_xpath("//div[@class='tip2']")    
  99.         pattern = r"\d+\.?\d*"#正則提取"微博[0]" 但r"( .? )"總含[] 
  100.         guid = re.findall(pattern, str_wb.text, re.S|re.M)  
  101.         print str_wb.text        #微博[294] 關注[351] 粉絲[294] 分組[1] @他的
  102.         for value in guid:  
  103.             num_wb = int(value)  
  104.             break
  105.         print u'微博數: ' + str(num_wb)  
  106.         #關注數
  107.         str_gz = driver.find_element_by_xpath("//div[@class='tip2']/a[1]")  
  108.         guid = re.findall(pattern, str_gz.text, re.M)  
  109.         num_gz = int(guid[0])  
  110.         print u'關注數: ' + str(num_gz)  
  111.         #粉絲數
  112.         str_fs = driver.find_element_by_xpath("//div[@class='tip2']/a[2]")  
  113.         guid = re.findall(pattern, str_fs.text, re.M)  
  114.         num_fs = int(guid[0])  
  115.         print u'粉絲數: ' + str(num_fs)  
  116.         #***************************************************************************
  117.         # No.2 檔案操作寫入資訊
  118.         #***************************************************************************
  119.         infofile.write('=====================================================================\r\n')  
  120.         infofile.write(u'使用者: ' + user_id + '\r\n')  
  121.         infofile.write(u'暱稱: ' + num_name + '\r\n')  
  122.         infofile.write(u'微博數: ' + str(num_wb) + '\r\n')  
  123.         infofile.write(u'關注數: ' + str(num_gz) + '\r\n')  
  124.         infofile.write(u'粉絲數: ' + str(num_fs) + '\r\n')  
  125.         infofile.write(u'微博內容: ' + '\r\n\r\n')  
  126.         #***************************************************************************
  127.         # No.3 獲取微博內容
  128.         # http://weibo.cn/guangxianliuyan?filter=0&page=1
  129.         # 其中filter=0表示全部 =1表示原創
  130.         #***************************************************************************
  131.         print'\n'
  132.         print u'獲取微博內容資訊'
  133.         num = 1
  134.         while num <= 5:  
  135.             url_wb = "http://weibo.cn/" + user_id + "?filter=0&page=" + str(num)  
  136.             print url_wb  
  137.             driver.get(url_wb)  
  138.             #info = driver.find_element_by_xpath("//div[@id='M_DiKNB0gSk']/")
  139.             info = driver.find_elements_by_xpath("//div[@class='c']")  
  140.             for value in info:  
  141.                 print value.text  
  142.                 info = value.text  
  143.                 #跳過最後一行資料為class=c
  144.                 #Error:  'NoneType' object has no attribute 'groups'
  145.                 if u'設定:面板.圖片'notin info:  
  146.                     if info.startswith(u'轉發'):  
  147.                         print u'轉發微博'
  148.                         infofile.write(u'轉發微博\r\n')  
  149.                     else:  
  150.                         print u'原創微博'
  151.                         infofile.write(u'原創微博\r\n')  
  152.                     #獲取最後一個點贊數 因為轉發是後有個點贊數
  153.                     str1 = info.split(u" 贊")[-1]  
  154.                     if str1:   
  155.                         val1 = re.match(r' (.?) ', str1).groups()[0]  
  156.                         print u'點贊數: ' + val1  
  157.                         infofile.write(u'點贊數: ' + str(val1) + '\r\n')  
  158.                     str2 = info.split(u" 轉發")[-1]  
  159.                     if str2:   
  160.                         val2 = re.match(r' (.?) ', str2).groups()[0]  
  161.                         print u'轉發數: ' + val2  
  162.                         infofile.write(u'轉發數: ' + str(val2) + '\r\n')  
  163.                     str3 = info.split(u" 評論")[-1]  
  164.                     if str3:  
  165.                         val3 = re.match(r' (.?) ', str3).groups()[0]  
  166.                         print u'評論數: ' + val3  
  167.                         infofile.write(u'評論數: ' + str(val3) + '\r\n')  
  168.                     str4 = info.split(u" 收藏 ")[-1]  
  169.                     flag = str4.find(u"來自")  
  170.                     print u'時間: ' + str4[:flag]  
  171.                     infofile.write(u'時間: ' + str4[:flag] + '\r\n')  
  172.                     print u'微博內容:'
  173.                     print info[:info.rindex(u" 贊")]  #後去最後一個贊位置
  174.                     infofile.write(info[:info.rindex(u" 贊")] + '\r\n')  
  175.                     infofile.write('\r\n')  
  176.                     print'\n'
  177.                 else:  
  178.                     print u'跳過', info, '\n'
  179.                     break
  180.             else:  
  181.                 print u'next page...\n'
  182.                 infofile.write('\r\n\r\n')  
  183.             num += 1
  184.             print'\n\n'
  185.         print'**********************************************'
  186.     except Exception,e:        
  187.         print"Error: ",e  
  188.     finally:      
  189.         print u'VisitPersonPage!\n\n'
  190.         print'**********************************************\n'
  191. #*******************************************************************************
  192. #                                程式入口 預先呼叫
  193. #*******************************************************************************
  194. if __name__ == '__main__':  
  195.     #定義變數
  196.     username = '1520161***'#輸入你的使用者名稱
  197.     password = '**********'#輸入你的密碼
  198.     user_id = 'guangxianliuyan'#使用者id url+id訪問個人 
  199.     #操作函式
  200.     LoginWeibo(username, password)      #登陸微博
  201.     #driver.add_cookie({'name':'name', 'value':'_T_WM'})
  202.     #driver.add_cookie({'name':'value', 'value':'c86fbdcd26505c256a1504b9273df8ba'})
  203.     #注意
  204.     #因為sina微博增加了驗證碼,但是你用Firefox登陸一次輸入驗證碼,再呼叫該程式即可,因為Cookies已經保證
  205.     #會直接跳轉到明星微博那部分,即: http://weibo.cn/guangxianliuyan
  206.     #在if __name__ == '__main__':引用全域性變數不需要定義 global inforead 省略即可
  207.     print'Read file:'
  208.     user_id = inforead.readline()  
  209.     while user_id!="":  
  210.         user_id = user_id.rstrip('\r\n')  
  211.         VisitPersonPage(user_id)         #訪問個人頁面
  212.         user_id = inforead.readline()  
  213.         #break
  214.     infofile.close()  
  215.     inforead.close()  
PS:發現CSDN編輯器的BUG,只要包含( ) 如:r' (.?) '就會自動換行 (⊙o⊙)


登入頁面
首先,為什麼需要登入呢?
因為新浪微博很多資料如果不登入是不能獲取或訪問的,如微博的粉絲列表、個人詳細資訊、微博下一頁等等,當你點選這些超連結時就會自動跳轉到登入介面,這是開發者對其進行的保護措施。同時,各個公司都會提供API介面讓開發者進行操作,但此處我是使用Selenium模擬瀏覽器操作進行爬取的。


其中登入如上圖所示,函式LoginWeibo(username, password) 實現,它會自動開啟瀏覽器並輸入使用者名稱和密碼。在登入過程中由於會涉及到驗證碼,所以我採用暫停20秒,當用戶手動輸入驗證碼並且時間到後會自動點選按鈕登入。核心程式碼如下:
driver.get("http://login.weibo.cn/login/")
elem_user = driver.find_element_by_name("mobile")
elem_user.send_keys(username)      #使用者名稱
elem_pwd = driver.find_element_by_xpath("/html/body/div[2]/form/div/input[2]")
elem_pwd.send_keys(password)       #密碼
elem_sub = driver.find_element_by_name("submit")
elem_sub.click()              #點選登陸


如果你登陸過程中Python報錯:
WebDriverException: Message: "Can't load the profile. Profile Dir:
猜測是Firefox版本問題,升級後出現的該問題,建議下載相對較老的版本,總體感覺只要Selenium、Python、Firefox版本一致就不會報錯,可從下面連結中安裝該版本Firefox。
下載地址:http://download.csdn.net/detail/mengh2016/7752097

那麼,登入成功後,為什麼就能訪問或跳轉到不同的頁面呢?
因為登入成功後會儲存Cookies或Session資訊,此時使用者就可以任意跳轉訪問了,否則會重新跳轉會登入介面。這裡使用Selenium的driver.get(url)實現跳轉。

獲取個人資訊
首先很多網站設計都是 URL+使用者ID 訪問個人網站,如柳巖:http://weibo.cn/guangxianliuyan
故定義一個TXT檔案列表包含,所有使用者ID資訊,依次通過讀取檔案爬取其微博資訊:
user_id = inforead.readline()
while user_id!="":
    user_id = user_id.rstrip('\r\n')
    VisitPersonPage(user_id)         #訪問個人頁面
    user_id = inforead.readline()


其中使用者ID列表在SinaWeibo_List.txt 中,如下所示:(明星)
  1. guangxianliuyan  
  2. zhangjiani  
  3. 1862829871
  4. zmqd  
  5. houzimi  
  6. 3125046087
  7. gezhaoen  
  8. 1877716733
  9. ailleenmmm  
  10. linshenblog  
  11. superleoisme  
  12. 2638613703
  13. duiersky  
  14. ws95  
  15. wuwei1003673996  
  16. wuxin  
  17. 1413971423
  18. xiena  
  19. yangxinyu888  
  20. zhangyangguoer0418  
  21. liuyifeiofficial  

通過分析HTML原始碼,獲取節點位置,通過Selenium函式定義位置獲取資訊,然後再通過正則表示式或字串處理獲取想要的值。如獲取暱稱:
str_name = driver.find_element_by_xpath("//div[@class='ut']")
#空格分隔 獲取第一個值 "Eastmount 詳細資料 設定 新手區"
str_t = str_name.text.split(" ")
num_name = str_t[0]     
print u'暱稱: ' + num_name

再如括號之間數字內容:
#微博[294] 關注[351] 粉絲[294] 分組[1] @他的 
str_gz = driver.find_element_by_xpath("//div[@class='tip2']/a[1]")
guid = re.findall(pattern, str_gz.text, re.M)
num_gz = int(guid[0])
print u'關注數: ' + str(num_gz)




資料URL:http://weibo.cn/1644461042/info