【Python爬虫实例学习篇】——5、【超详细记录】从爬取微博评论数据(免登陆)到生成词云

【Python爬虫实例学习篇】——5、【超详细记录】从爬取微博评论数据(免登陆)到生成词云

近段时间新型冠状病毒的问题引起了全国人民的广泛关注,对于这一高传染性的病毒,人们有着不同的声音,而我想通过大数据看看大多数人是怎么想的。

精彩部分提醒:

(1)微博评论页详情链接为一个js脚本
(2)获取js脚本链接需要该条微博的mid参数
(3)获取mid参数需要访问微博主页
(4)访问微博主页需要先进行访客认证
(5)微博主页几乎是由弹窗构成,所有html代码被隐藏在FM.view()函数的参数中,该参数是json格式

工具:

  1. Python 3.6
  2. requests 库
  3. json 库
  4. lxml 库
  5. urllib 库
  6. jieba 库(进行分词)
  7. WordCloud 库(产生词云)

目录:

  1. 爬取微博评论数据
  2. GetWeiBoRemark.py
  3. 生成词云

#### 1、爬取微博篇论数据

央视新闻官方微博置顶的第一条微博为例,爬取其评论数据。

(1) 寻找评论页

第一步:寻找评论页

先用Ctrl+Shift+C 选取评论标签查看其html代码,发现其链接为一个js脚本,那么尝试用fddler看看能不能抓到这个js脚本的包,得到这个js的地址。


找到疑似js包,将数据解码,确认是我们要找的包。

同时,根据“查看更多”可以确定跳转的链接,将这一结果在json解析结果中搜索,可以进一步确定这个js包就是我们要找的包。接下来需要确定这个js包是来源于哪。


第二步:找到js包地址

js包地址为:https://weibo.com/aj/v6/comme...”,链接很长,且参数很多,根据以往经验,我们尝试删除一些参数进行访问测试。
经过测试发现只需==mid==这一个参数即可获取该数据包。
所以有效js包地址为:https://weibo.com/aj/v6/comme...“。

用XPath Helper进行调试,没有问题,接下来在python上实现这部分代码,==(经过后面测试发现获取评论数据只需获取微博的mid即可,因此下面这几步可以跳过,但是为例保证探索过程的完整性,我将其留在了这里)==

得到评论页的代码:

import requests
from lxml import etree

requests.packages.urllib3.disable_warnings()

name="cctvxinwen"
home_url='https://weibo.com/'+name
headers={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3970.5 Safari/537.36"
}

session=requests.session()
res=session.get(home_url,headers=headers,timeout=20,verify=False)
html=etree.HTML(res.content.decode('gb2312',"ignore"))
mid_list=html.xpath("//div/@mid")

本以为可以轻松获取新闻主页,结果发现若是没有附带cookie的话,会自动跳转到微博登陆验证页面。

对于我们的爬虫来说,只需要从第8号包开始访问即可,8号包需要提交的数据为(常量):

cb=gen_callback&fp={"os":"1","browser":"Chrome80,0,3970,5","fonts":"undefined","screenInfo":"1920*1080*24","plugins":"Portable Document Format::internal-pdf-viewer::Chrome PDF Plugin|::mhjfbmdgcfjbbpaeojofohoefgiehjai::Chrome PDF Viewer|::internal-nacl-plugin::Native Client"}

需要注意的是,在使用requests库时,需要向协议头中添加:
=='Content-Type': 'application/x-www-form-urlencoded'==,否则返回数据为空。

9号包的链接为:
https://passport.weibo.com/vi...,其中_rand参数可以忽略。这里需要注意,添加tid参数时,tid参数需要 url编码
在完成9号包的访问后,就可以获取央视新闻微博的主页了:

第一部分的代码为:

import requests
import json
from urllib import parse
from lxml import etree

requests.packages.urllib3.disable_warnings()
session = requests.session()
session.verify = False
session.timeout = 20

name = "cctvxinwen"
home_url = 'https://weibo.com/' + name
url1 = "https://passport.weibo.com/visitor/genvisitor"
urljs='https://weibo.com/aj/v6/comment/small?mid='

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3970.5 Safari/537.36"
}

data = "cb=gen_callback&fp=%7B%22os%22%3A%221%22%2C%22browser%22%3A%22Chrome80%2C0%2C3970%2C5%22%2C%22fonts%22%3A%22undefined%22%2C%22screenInfo%22%3A%221920*1080*24%22%2C%22plugins%22%3A%22Portable%20Document%20Format%3A%3Ainternal-pdf-viewer%3A%3AChrome%20PDF%20Plugin%7C%3A%3Amhjfbmdgcfjbbpaeojofohoefgiehjai%3A%3AChrome%20PDF%20Viewer%7C%3A%3Ainternal-nacl-plugin%3A%3ANative%20Client%22%7D"

# 获取tid
headers.update({'Content-Type': 'application/x-www-form-urlencoded'})
tid = json.loads(session.post(url=url1, headers=headers, data=data).content.decode('utf-8')[36:-2])['data']['tid']
del headers['Content-Type']

# 获取访客cookie
url2 = "https://passport.weibo.com/visitor/visitor?a=incarnate&t=" + parse.quote(
    tid) + "&w=2&c=095&gc=&cb=cross_domain&from=weibo"
session.get(url=url2.encode('utf-8'), headers=headers)

# 访问微博主页,解析获取评论页面
res = session.get(url=home_url, headers=headers)
html = etree.HTML(res.content.decode('utf-8', "ignore"))
# 含有mid的html代码被隐藏在这一个json中
mid_json = json.loads(html.xpath("//script")[38].text[8:-1])
mid_html=etree.HTML(mid_json['html'])
mids=mid_html.xpath("//div/@mid")

# 获取第一条微博的js包地址
urljs=urljs+str(mids[0])
res=session.get(url=urljs, headers=headers)
js_json_html=json.loads(res.content)['data']['html']
print(js_json_html)
print("该微博当前评论数为:"+str(json.loads(res.content)['data']['count']))

# 解析获取评论页地址
js_html=etree.HTML(js_json_html)
url_remark=js_html.xpath("//a[@target='_blank']/@href")[-1]
url_remark="https:"+url_remark
(2) 获取并评论

获取评论页后,我们非常容易的就能找到评论数据的json包,如图所示:

2、 GetWeiBoRemark.py

import requests
import json
from urllib import parse
from lxml import etree

# 预设
requests.packages.urllib3.disable_warnings()
session = requests.session()
session.verify = False
session.timeout = 20
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3970.5 Safari/537.36"
}


def getweiboremark(name, index=1, num=-1):
    # name参数代表微博主页的名称
    # index参数代表目标微博的序号
    # num代表要爬的评论数,-1代表所有评论
    # 返回结果为评论列表
    home_url = 'https://weibo.com/' + name
    tid = get_tid()
    get_cookie(tid=tid)
    mids = get_mids(home_url=home_url)
    remark_data = get_remarkdata(num=num, mids=mids, index=index)
    return remark_data


def get_tid():
    # 获取tid
    url1 = "https://passport.weibo.com/visitor/genvisitor"
    data = "cb=gen_callback&fp=%7B%22os%22%3A%221%22%2C%22browser%22%3A%22Chrome80%2C0%2C3970%2C5%22%2C%22fonts%22%3A%22undefined%22%2C%22screenInfo%22%3A%221920*1080*24%22%2C%22plugins%22%3A%22Portable%20Document%20Format%3A%3Ainternal-pdf-viewer%3A%3AChrome%20PDF%20Plugin%7C%3A%3Amhjfbmdgcfjbbpaeojofohoefgiehjai%3A%3AChrome%20PDF%20Viewer%7C%3A%3Ainternal-nacl-plugin%3A%3ANative%20Client%22%7D"

    headers.update({'Content-Type': 'application/x-www-form-urlencoded'})
    tid = json.loads(session.post(url=url1, headers=headers, data=data).content.decode('utf-8')[36:-2])['data']['tid']
    del headers['Content-Type']
    return tid


def get_cookie(tid, session=session):
    # 获取访客cookie
    url2 = "https://passport.weibo.com/visitor/visitor?a=incarnate&t=" + parse.quote(
        tid) + "&w=2&c=095&gc=&cb=cross_domain&from=weibo"
    session.get(url=url2.encode('utf-8'), headers=headers)


def get_mids(home_url, session=session):
    # 访问微博主页,解析获取评论页面
    # 要想获取mids,必须先获取cookie
    res = session.get(url=home_url, headers=headers)
    try:
        html = etree.HTML(res.content.decode('utf-8', "ignore"))
        # 含有mid的html代码被隐藏在这一个json中
        mid_json = json.loads(html.xpath("//script")[38].text[8:-1])
        mid_html = etree.HTML(mid_json['html'])
        mids = mid_html.xpath("//div/@mid")
        mids[0]
        return mids
    except Exception:
        mids = get_mids(home_url, session=session)
        return mids


def get_remarkdata(num, mids, index=1):
    # 获取评论数据
    url_remarkdata = 'https://weibo.com/aj/v6/comment/big?ajwvr=6&id={mid}&page={page}&from=singleWeiBo'
    page = 1
    remark_data_new = []
    current_num = 0
    while True:
        print("-" * 50)
        url_remarkdata_new = url_remarkdata.format(mid=str(mids[index]), page=str(page))
        page = page + 1
        print("正在采集第 " + str(page - 1) + " 页评论!")
        res = session.get(url=url_remarkdata_new, headers=headers)
        remark_html = etree.HTML(json.loads(res.content.decode(encoding='utf-8'))['data']['html'])
        remark_data = remark_html.xpath("//div[@class='list_con']/div[1]/text()")
        remark_num = json.loads(res.content.decode(encoding='utf-8'))['data']['count']
        if page == 2:
            print("本条微博共有 "+str(remark_num)+' 个评论!')
            if num == -1:
                num = remark_num
            elif num > remark_num:
                num = remark_num
        for i in remark_data:
            if i[0:1] == ':':
                i = i[1:]
                remark_data_new.append(i.strip())
        current_num = len(remark_data_new)
        print("当前已采集:" + str(current_num) + " 个评论,剩余:" + str(num - current_num) + "个待采集!")
        if (num <= current_num):
            break
    return remark_data_new

def save_remarkdata(name,data):
    with open(name,'w',encoding='utf-8') as fp:
        fp.write(data)
        fp.flush()
        fp.close()

结果展示:

3、生成词云

代码如下:

from wordcloud import WordCloud
import matplotlib.pyplot as plt
import jieba
import GetWeiBoRemark

def producewordcloud(data,mask=None):
    word_cloud=WordCloud(background_color="white",font_path='msyh.ttc',mask=mask,max_words=200,max_font_size=100,width=1000,height=860).generate(' '.join(jieba.cut(data,cut_all=False)))
    plt.figure()
    plt.imshow(word_cloud, interpolation='bilinear')
    plt.axis("off")  # 不显示坐标轴
    plt.show()

if __name__ == '__main__':
    # cctvxinwen
    remark_data=GetWeiBoRemark.getweiboremark(name="cctvxinwen",index=2,num=100)
    str_remark_data=''
    for i in remark_data:
        str_remark_data=str_remark_data+str(i)
    GetWeiBoRemark.save_remarkdata(name='cctvxinwen.txt',data=str_remark_data)
    producewordcloud(str_remark_data)

==目标微博==

==微信公众号:==

你可能感兴趣的:(python)