[Python爬虫] Selenium爬取新浪微博移动端热点话题及评论 (下)

这篇文章主要讲述了使用python+selenium爬取新浪微博的热点话题和评论信息。其中使用该爬虫的缺点是效率极低,傻瓜式的爬虫,不能并行执行等,但是它的优点是采用分析DOM树结构分析网页源码并进行信息爬取,同时它可以通过浏览器进行爬取中间过程的演示及验证码的输入。这篇文章对爬虫的详细过程就不再论述了,主要是提供可运行的代码和运行截图即可。希望文章对你有所帮助吧~

参考文章
[python爬虫] Selenium爬取新浪微博内容及用户信息
[Python爬虫] Selenium爬取新浪微博客户端用户信息、热点话题及评论 (上)
[Python爬虫] 在Windows下安装PIP+Phantomjs+Selenium
[Python爬虫] Selenium实现自动登录163邮箱和Locating Elements介绍
http://selenium-python.readthedocs.org/locating-elements.html

实现过程
运行截图如下所示,它通过调用Firefox浏览器,然后输入移动端网址自动登陆新浪微博,它会自动输入用户名及密码,但需要用户在我设置暂停20秒内输入验证码。登陆后它会自动跳转到微博主题搜索页面,当用户输入关键词"欢乐颂"后,就会对返回的微博信息及评论进行爬取了,爬取过程中需要注意翻页即可。



运行结果
运行结果如下图所示:两个文件,一个是微博内容评论对应的URL,一个是爬取URL中的评论信息。





源代码
源代码如下:

# coding=utf-8

"""  
Created on 2016-04-24 @author: Eastmount

功能: 爬取新浪微博用户的信息及微博评论
网址:http://weibo.cn/ 数据量更小 相对http://weibo.com/

正确的方法是把所有的URL都获取再依次访问

"""    

import time            
import re            
import os    
import sys  
import codecs  
import shutil
import urllib 
from selenium import webdriver        
from selenium.webdriver.common.keys import Keys        
import selenium.webdriver.support.ui as ui        
from selenium.webdriver.common.action_chains import ActionChains



#先调用无界面浏览器PhantomJS或Firefox    
#driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe")    
driver = webdriver.Firefox()
wait = ui.WebDriverWait(driver,10)


#全局变量 文件操作读写信息
inforead = codecs.open("SinaWeibo_List_best_1.txt", 'r', 'utf-8')
infofile = codecs.open("SinaWeibo_Info_best_1.txt", 'w', 'utf-8')


#********************************************************************************
#                            第一步: 登陆weibo.cn 
#        该方法针对weibo.cn有效(明文形式传输数据) weibo.com见学弟设置POST和Header方法
#                LoginWeibo(username, password) 参数用户名 密码
#********************************************************************************

def LoginWeibo(username, password):
    try:
        #输入用户名/密码登录
        print u'准备登陆Weibo.cn网站...'
        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)  #密码 name=password_6785
        #elem_rem = driver.find_element_by_name("remember")
        #elem_rem.click()             #记住登录状态

        #重点:暂停时间输入验证码(http://login.weibo.cn/login/ 手机端需要)
        time.sleep(20)

        #点击submit按钮登陆方式或输入回车键登陆方式
        #elem_sub = driver.find_element_by_name("submit")
        #elem_sub.click()              
        elem_pwd.send_keys(Keys.RETURN)
        time.sleep(2)
        
        #获取Coockie 
        print driver.current_url
        print driver.get_cookies()  #获得cookie信息 dict存储
        print u'输出Cookie键值对信息:'
        for cookie in driver.get_cookies(): 
            #print cookie
            for key in cookie:
                print key, cookie[key]
                    
        #driver.get_cookies()类型list 仅包含一个元素cookie类型dict
        print u'登陆成功...'
        
        
    except Exception,e:      
        print "Error: ",e
    finally:    
        print u'End LoginWeibo!\n\n'


#**********************************************************************************************
#                  第二步: 访问个人页面http://weibo.cn/5824697471并获取信息
#                                VisitPersonPage()
#        编码常见错误 UnicodeEncodeError: 'ascii' codec can't encode characters文件utf-8编码
#**********************************************************************************************

def VisitPersonPage(user_id):

    try:
        global infofile       #全局文件变量
        url = "http://weibo.com/" + user_id
        driver.get(url)
        print u'准备访问个人网站.....', url
        print u'个人详细信息'
        
        #用户id
        print u'用户id: ' + user_id

        #昵称 关注数 粉丝数 微博数 个人资料其它信息
        #URL http://weibo.cn/5824697471/follow   
        
        
    except Exception,e:      
        print "Error: ",e
    finally:    
        print u'VisitPersonPage!\n\n'
        

#********************************************************************************
#                  第三步: 访问http://weibo.cn/search/ (手机端) 页面搜索热点信息
#                         爬取微博信息及评论,注意评论翻页的效果和微博的数量
#********************************************************************************    

def GetComment(key):
    try:
        global infofile       #全局文件变量
        driver.get("http://weibo.cn/search/")
        print u'搜索热点主题关键词:', key

        #输入主题并点击搜索
        item_inp = driver.find_element_by_xpath("//div[@class='c']/form/div/input") #name=keyword
        item_inp.send_keys(key)
        item_inp.send_keys(Keys.RETURN)    #采用点击回车直接搜索


        #内容
        #content = driver.find_elements_by_xpath("//div[@class='content clearfix']/div/p")
        comment = driver.find_elements_by_xpath("//a[@class='cc']")
        content = driver.find_elements_by_xpath("//div[@class='c']")
        print content
        all_comment_url = []               #存储所有文件URL
        i = 0
        j = 0
        infofile.write(u'开始:\r\n')
        print u'长度', len(content)
        while i<len(content):
            #print content[i].text
            if (u'收藏' in content[i].text) and (u'评论' in content[i].text): #过滤其他标签
                print content[i].text
                infofile.write(u'微博信息:\r\n')
                infofile.write(content[i].text + '\r\n')
                div_id = content[i].get_attribute("id")
                print div_id
                while(1):  #存在其他包含class=cc 如“原文评论”
                    url_com = comment[j].get_attribute("href")
                    if ('comment' in url_com) and ('uid' in url_com):
                        print url_com
                        infofile.write(u'评论信息:\r\n')
                        infofile.write(url_com+'\r\n')
                        all_comment_url.append(url_com)    #保存到变量里
                        j = j + 1
                        break
                    else:
                        j = j + 1
                    
            i = i + 1

        #http://weibo.cn/search/?pos=search
        print driver.current_url
        
        #python中文转换url编码 urllib.quote(key) urllib.unquote转回来
        #转码失败
        #http://weibo.cn/search/mblog?hideSearchFrame=&keyword=欢乐颂&page=2
        #url = "http://weibo.cn/search/mblog?hideSearchFrame=&keyword="+ key_url + "&page=2"


        #获取10个下页
        N = 2
        while N<=10:
            #后面采用换页 第一次为方便给大家解决方法就采用获取搜索框id回车访问
            url_get = driver.find_element_by_xpath("//div[@id='pagelist']/form/div/a")
            url = url_get.get_attribute("href") 
            print url #获取下页
            driver.get(url)
            comment = driver.find_elements_by_xpath("//a[@class='cc']")
            content = driver.find_elements_by_xpath("//div[@class='c']")
            print content
            i = 0
            j = 0                        #第一个<a class='cc' href>是多余的
            print u'长度', len(content)
            infofile.write(u'\r\n下页:\r\n')
            while i<len(content):
                #print content[i].text
                if (u'收藏' in content[i].text) and (u'评论' in content[i].text):
                    print content[i].text
                    infofile.write(u'微博信息:\r\n')
                    infofile.write(content[i].text + '\r\n')
                    #获取该信息id值 通过id获取评论超链接
                    #先获取:<div id="M_Du3npzqSd" class="c"> 
                    #再获取:<a class="cc" href="http://weibo.cn/comment/#cmtfrm"></a>
                    div_id = content[i].get_attribute("id")
                    print div_id
                    '''
                    url = driver.find_elements_by_xpath("//div[@id=" + div_id + "]/a")
                    print url
                    for u in url:
                        print u.get_attribute("href")
                    '''
                    while(1):  #存在其他包含class=cc 如“原文评论”
                        url_com = comment[j].get_attribute("href")
                        if ('comment' in url_com) and ('uid' in url_com):
                            print url_com
                            infofile.write(u'评论信息:\r\n')
                            infofile.write(url_com + '\r\n')
                            all_comment_url.append(url_com)
                            j = j + 1
                            break
                        else:
                            j = j + 1

                i = i + 1
            N = N + 1
        else:
            print u'结束爬取评论URL 对齐while循环'
            

        #方位评论URL并进行爬取
        print u'\n\n评论'
        infocomment = codecs.open("SinaWeibo_Info_best_2.txt", 'w', 'utf-8')
        for url in all_comment_url:
            print url
            driver.get(url)
            #driver.refresh()
            time.sleep(2)
            infocomment.write(url+'\r\n')
            test = driver.find_elements_by_class_name('c')
            print len(test)
            #Error:  Message: Element not found in the cache -
            #perhaps the page has changed since it was looked up
            #http://www.51testing.com/html/21/n-862721-2.html
            #异常的说明已经很明显了:在cache中找不到元素,在元素被找到之后页面变换了。
            #这就说明,当当前页面发生跳转之后,存在cache中的关于这个页面的元素也被清空了。
            k = 0
            while k<len(test): 
                print test[k].text
                infocomment.write(test[k].text + '\r\n')
                k = k + 1
            infocomment.write('\r\n')
        infocomment.close()
                                      


    except Exception,e:      
        print "Error: ",e
    finally:    
        print u'VisitPersonPage!\n\n'
        print '**********************************************\n'

        
        
#*******************************************************************************
#                                程序入口 预先调用
#         注意: 因为sina微博增加了验证码,但是你用Firefox登陆输入验证码
#         直接跳转到明星微博那部分,即: http://weibo.cn/guangxianliuyan
#*******************************************************************************
    
if __name__ == '__main__':

    #定义变量
    username = '15201615157'             #输入你的用户名
    password = '*********'               #输入你的密码

    #操作函数
    LoginWeibo(username, password)       #登陆微博

    #在if __name__ == '__main__':引用全局变量不需要定义 global inforead 省略即可
    print 'Read file:'
    user_id = inforead.readline()
    while user_id!="":
        user_id = user_id.rstrip('\r\n')
        print user_id
        VisitPersonPage(user_id)         #访问个人页面http://weibo.cn/guangxianliuyan
        user_id = inforead.readline()
        #break

    #搜索热点微博 爬取评论
    key = u'欢乐颂' 
    GetComment(key)
    
    infofile.close()
    inforead.close()
    
    




登陆部分核心代码
其中登陆部分的核心代码及对应DOM树结构分析如下:
#调用Firefox浏览器 
driver =  webdriver.Firefox()
driver.get("http://login.weibo.cn/login/")
#用户名
elem_user = driver.find_element_by_name("mobile") 
elem_user.send_keys(username)      
#密码 name=password_6785
elem_pwd = driver.find_element_by_xpath("/html/body/div[2]/form/div/input[2]")
elem_pwd.send_keys(password)      
#记住登录状态
elem_rem = driver.find_element_by_name("remember")
elem_rem.click()                              
#重点:暂停时间输入验证码(http://login.weibo.cn/login/ 手机端需要)
time.sleep(20)
#点击submit按钮登陆方式或输入回车键登陆方式
elem_sub = driver.find_element_by_name("submit")
elem_sub.click()              
elem_pwd.send_keys(Keys.RETURN)




为什么需要从移动端登陆呢?
新浪微博有两个入口:
        新浪微博登录常用接口:http://login.sina.com.cn/ 
        对应主界面:
http://weibo.com/
        但是个人建议采用手机端微博入口:http://login.weibo.cn/login/  
        对应主界面:http://weibo.cn/
因为在客户端登陆爬取微博评论信息时,它总是通过JavaScript动态加载的,需要点击按钮才能加载,单纯的获取评论的节点或使用正则表达式爬取评论部分的HTML源码,都是空值,所以采用移动端进行爬取。
它们的主要区别是移动端数据相对更简练,但是内容都基本一样,只是图片较小、关注粉丝数只能显示20页、个人信息缺失些等,供手机用户使用,但是信息是和客户端对应的。
如下图所示,它表示动态加载的评论,可以看到"href=javascript:void(0)"还有一些script函数实现的动态加载。




跳转页面下一页
常用的方法,如我前面的文章讲解爬取虎扑图片、生物预料中,都是通过分析URL的&构成进行的。新浪微博同样也是:
http://weibo.cn/search/mblog?hideSearchFrame=&keyword=欢乐颂&page=2
其中搜索关键词"欢乐颂",只需要修改page页面即可。但是"欢乐颂"中文字符转换为URL编码时总是报错,python使用中文转换url编码方法:urllib.quote(key)。
所以采用了第二种方法,获取下一页的URL,再进行driver.get(url)访问,循环N从2到10。
#获取下页
url_get = driver.find_element_by_xpath("//div[@id='pagelist']/form/div/a")
url = url_get.get_attribute("href")  
driver.get(url)

#评论URL
comment = driver.find_elements_by_xpath("//a[@class='cc']")
#微博内容 需过滤其它class=c 
content = driver.find_elements_by_xpath("//div[@class='c']")


重点获取URL队列依次爬取评论
由于新浪微博需要模拟登陆,所以获取微博的同时获取了评论的URL,但是这里使用的浏览器driver不能同时访问评论信息,因为它在循环中driver还在使用。换句话就是find_elements_by_xpath是获取多个值,当然你可以再开一个driver2,但也需要登陆,再driver2.get(url_comment)即可。
但常用的爬虫通常是有一个URL队列的概念,把爬取的评论URL都存储在一个队列或数组中。该程序循环先N从2到10,爬取10页的微博信息及评论URL,再依次爬取评论信息,如果获得了URL,也能爬取微博内容的。
关键:使用数组存储所有评论的URL队列,再一次driver.get(url)爬取所有微博及评论。
#方位评论URL并进行爬取
print u'\n\n评论'
infocomment = codecs.open("SinaWeibo_Info_best_2.txt", 'w', 'utf-8')
for url in all_comment_url:
      print url
      driver.get(url)

      #driver.refresh()
      time.sleep(2)
      infocomment.write(url+'\r\n')
      test = driver.find_elements_by_class_name('c')
      print len(test)
      k = 0
      while k<len(test): 
            print test[k].text
            infocomment.write(test[k].text + '\r\n')
            k = k + 1
      infocomment.write('\r\n')
infocomment.close()



错误Element not found in the cache
在爬取过程中可能会遇到错误:Error:  Message: Element not found in the cache - perhaps the page has changed since it was looked up
参考:http://www.51testing.com/html/21/n-862721-2.html
异常的说明解释:在cache中找不到元素,在元素被找到之后页面变换了。这就说明,当当前页面发生跳转之后,存在cache中的关于这个页面的元素也被清空了。
通过实验发现,它是使用driver.get(url)访问评论时,太快没有加载页面获取元素,采用的方法是time.sleep(2)显示2秒。希望有更好的解决方法~当然时间设置短点也行。


热门话题置顶
通过比较客户端和移动端的信息,会发现搜索微博热点,它在客户端首先显示的是最近关于该话题非常热门的微博,如"欢乐颂"蒋欣的状态等。





比较移动端和客户端微博信息
这是比较移动到和客户端的微博信息,信息基本是一致的,可能只是因为排序显示问题可能序号稍微微调,同时客户端也采用了热门话题置顶显示的一些推荐相关算法吧!如"一个五点之前就醒了的人....."






然后下面是具体的评论信息,可以发现也是对应的,但是通过手机端就可以显示评论信息,可以进行爬取,而客户端爬取结果为空。








PS:最后希望文章对你有所帮助!其实方法很简单,希望你能理解这种思想,如何分析HTML源码及DOM树结构,然后动态获取自己需要的信息。
(By:Eastmount 2016-05-06 深夜4点半  http://blog.csdn.net/eastmount/ )
相关文章
相关标签/搜索