博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JB的Python之旅-爬虫篇-新浪微博内容爬取
阅读量:5966 次
发布时间:2019-06-19

本文共 8320 字,大约阅读时间需要 27 分钟。

前言

记得半个月之前的一晚,媳妇跟jb说,你看,苍老师发了条微博,内容为69,后来微博官方关闭了该条微博的评论功能~

虽然不知道69是什么意义,但是看评论,总感觉是要开车了~

听说,苍老师是90后启蒙的一代,虽然没有经历过,但依然心存敬佩,于是乎,就像把苍老师的微博内容都爬出来,看看老师都发了些什么~

PC找内容

打开链接:

https://weibo.com/u/1739928273?refer_flag=1001030101_&is_all=1#_rnd1530254292380
打开后就是苍老师的微博链接,可以看到,下面就是苍老师发布的微博拉,而内容就是我们想要的东西~

像微博这种大厂,想都不用想就知道解析html获取数据这条路是行不通的,那我们F12 刷新下网页,看看请求?

嗯,好多js跟css,那我们一个一个看,看看能不能找到有用的信息;

(5分钟过去了)尼玛,怎么一条微博动态都没看到,怎么获取?

尝试多次,依然找不到解决方案,欲想放弃,此时,三三同学说,用wap版,微博有接口获取!!!

转战手机版

就这样,转战手机版,手机版苍老师链接如下:https://m.weibo.cn/u/1739928273;

老规矩,F12刷新网页,然后把请求一条一条过,结果发现一个玩意:

比起PC版,手机版终于看到有点类似数据的东西了,那我们打开第一条看看~

这里面的text不就是跟苍老师的第一条微博是一样的吗?get~这就是我们需要的东西啦~

那我们点击headers,把request url拿出来分析下:

https://m.weibo.cn/api/container/getIndex?type=uid&value=1739928273&containerid=1076031739928273复制代码

解析这这个url,这url带有3个参数:

type=uidvalue=1739928273containerid=1076031739928273复制代码

这3个参数,唯一能确定的就是value,为什么这么说?回头看看苍老师手机版的链接:https://m.weibo.cn/u/1739928273,由此得知,1739928273就是苍老师微博的ID,不信, 你随便改下试试,可能会跳到其他老师那呢~

这不,简单把最后2位73改成12,就变成另一位美女了~

貌似跑题了,咳咳,刚刚说到哪~

嗯,知道这几个参数,没啥特别的,那我们试试滑动下屏幕,往下拉,拉取更多的数据,最后使用上面的方式,获取url:

https://m.weibo.cn/api/container/getIndex?type=uid&value=1739928273&containerid=1076031739928273&page=3复制代码

与上面的url不同的是,这里多了个参数page=3,不用想都知道,这是代表第三页的意思了~

结果分析,如果是第二页,page=2,第一页的话,是不携带page参数,但是尝试把page=0跟page=1两个情况,返回的数据跟不携带page参数是一直的,所以后面就把首页当做是page=1的情况处理~

ok,现在知道了数据在哪里,翻页怎么弄,那我们就看看请求头把~

咦,
Provisional headers are shown这是什么,其他请求内容没看到?
网上找了说,是这么解释:
请求的资源可能会被(扩展/或其他什么机制)屏蔽掉。
更详细的信息的话,请看:https://segmentfault.com/q/1010000000364871/a-1020000000429614

这个东西对于我们有影响吗?暂时看是没有的,从上图就能看到请求的内容跟携带的参数~

那我们打开看看,下面这条链接是什么内容?

https://m.weibo.cn/api/container/getIndex?type=uid&value=1739928273&containerid=1076031739928273复制代码

嗯,打开之后,是这样的,wtf??这是什么??

此时,赶紧看看苍老师的内容:

内容的对应字段是text,那我们copy去到刚刚那个页面搜索下:

都变成了\u6211\u81ea\u5df这种玩意了~

这个问题,之前在写urllib的时候也说明过:

URL只允许部分ASCLL(数字字母和部分符号),其他的字符(包括汉字)是不符合URL的标准,所以URL需要对这些字符进行URL编码,URL编码的方式是把需要编码的字符转化为 %xx 的形式。通常 URL 编码是基于 UTF-8 的,函数说明也提及到给予UTF-8进行encode~复制代码

Ok,那就说,提取text的内容就好啦~那我们先写个请求吧

# -*- coding:utf-8 -*-import requestsurl = "https://m.weibo.cn/api/container/getIndex"#请求的urlheaders = {    "Host": "m.weibo.cn",    "Referer": "https://m.weibo.cn/u/1739928273",    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",    "Accept":'application/json, text/plain, */*',    "X-Requested-With":"XMLHttpRequest",}#请求头params = {          "type": "uid",          "value": "1739928273",          "containerid": "1076031739928273",          "page": "1"}#请求携带的参数res = requests.get(url,headers=headers,params=params).contentprint(res)复制代码

执行后,得到的结果是这样的:

核对了下,跟网页访问是一样的~

https://m.weibo.cn/api/container/getIndex?type=uid&value=1739928273&containerid=1076031739928273&page=1复制代码

接下来可以干嘛?就可以写正则来匹配啦,先找data,然后找cards,然后再获取每个cards下面的text;

如果真的如上面说的,马上写噼里啪啦写正则,就有点冲动了,看下返回结果的格式,感觉是不是像json?

"ok":1,"data":{"cardlistInfo":{"containerid":"1076031739928273","v_p":复制代码

没错,这就是json,那我们就可以换一种方式处理~

res = requests.get(url,headers=headers,params=params)cards  = res.json().get("data").get("cards")#获取carads下的所有项复制代码

获取的就是下面这个截图的所有cards项:

那我们看看cards里面的内容,这是第一个:

这是第二个:

嗯,有发现不同了吗?对的,就是有多了个关注XXX的一项,但是这一项,但是这一项不是我们要的,那怎么搞?

逐个逐个分析,会发现,正常的数据都有这么一项:

那我们就拿这项做判断吧,先判断这项是否等于9,然后再获取mblog,再获取text里面的内容,于是乎就有了下面的代码:

for card in cards:    if card.get("card_type") == 9:        text = card.get("mblog").get("text")        print(text)复制代码

输出的结果如下:

嗯,内容都获取到了,但是有奇怪的东西进来了~

回头看了下,并不是代码的错,而且因为发布的内容有带图片或者表情~

这种情况过滤掉就好了~只需要文字~

pattern = re.compile(r"(.*?)
(.*?)")text = re.sub(pattern,"",text)复制代码

从上面可以看到问题例子如下,那我们只需要把里面的内容都干掉就好了~

啊啊啊啊啊啊啊啊啊[允悲] 复制代码

结果如下:

这里有个不解之谜,就是会看到,会有换行,原因是这样的:

这尼玛,居然有个换行符??那我们把获取到的text打印以下~

我去,这个换行符已经换行了,没办法匹配啊~本来还把想把\n换行符先干掉了,这个就是这个换行符的来源,怎么办?

之前在介绍urllib的时候提及有,urllib有一个quote的方法,函数说明提及到给予UTF-8进行encode;

import urllibkw = urllib.request.quote("很紧张啊啊啊啊↵

输出的内容长这样的:

%E5%BE%88%E7%B4%A7%E5%BC%A0%E5%95%8A%E5%95%8A%E5%95%8A%E5%95%8A%E2%86%B5%3Cs复制代码

那我们把中文都去掉,只留↵看看?

%E2%86%B5复制代码

得到的结果就是这样的,OK,那假如我们把上面这串结果匹配成空格,是不是就能解决问题?

但实际尝试了下,是不行的,那我们就把数据打出来:

啊啊啊啊啊啊啊啊啊

最后会发现%0A才是那个回车符

%8A%0A%3Cspan复制代码

去掉之后,会发现字符的确不见了,而且的的确不会换行了,问题解决;

kw = urllib.request.quote(text)    old_kw = re.sub("%0A","",kw)    new_kw = urllib.request.unquote(old_kw)复制代码

回到正题,按照上面的代码爬下来的东西,好像没啥问题,但认真一看,咦~

这里谁说老师下垂了?

对应的微博内容:

这里,很明显是之前的正则有问题,那我们重新折腾下正则,此处过程就不说了,很痛苦。。最后改成这样:

pattern = re.compile(r"<.*?>")复制代码

意思就是把<>符号内的内容都去掉,得出的结果:

//@李太白的表哥:老师…你好像下垂了……查看图片复制代码

这里可以看到,查看图片也是多余的,那我们也去掉,包括有一些是转发微博的,也都干掉吧,就成这样了~

pattern = re.compile(r"<.*?>|转发微博|查看图片")复制代码

执行下,结果是这样了,看上去很好:

//@李太白的表哥:老师…你好像下垂了……复制代码

ok,这个是一个页面的内容抓取,整体代码如下:

# -*- coding:utf-8 -*-import requestsimport reimport urlliburl = "https://m.weibo.cn/api/container/getIndex"#请求的urlheaders = {    "Host": "m.weibo.cn",    "Referer": "https://m.weibo.cn/u/1739928273",    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",    "Accept":'application/json, text/plain, */*',    "X-Requested-With":"XMLHttpRequest",}#请求头params = {          "type": "uid",          "value": "1739928273",          "containerid": "1076031739928273",          "page": "1"}#请求携带的参数res = requests.get(url,headers=headers,params=params)cards  = res.json().get("data").get("cards")#获取carads下的所有项for card in cards:    if card.get("card_type") == 9:        text = card.get("mblog").get("text")        # kw = urllib.request.quote(text)        # old_kw = re.sub("%0A","",kw)        # new_kw = urllib.request.unquote(old_kw)        # %0A  这串数字对应的就是这个回车字符        pattern = re.compile(r"<.*?>|转发微博|查看图片")        #这里就是把<>符号内的都匹配出来        text = re.sub(pattern,"",text)        print(text)复制代码

其他优化

既然一页搞定了,那我们要爬多页,怎么破?这个很简单啦,直接改page参数就行了

另外还遇到一个问题:

假如我们设定爬取1000页,但是实际上,用户可能只有100页的数据,那脚本还是会一直爬取的~
处理方案,加多一个参数,统计上一次的长度,如果相同,则认为没有新数据,暂停脚本处理

数据这多了,还发现这种东西~当然,也是正则兼容下就行了~

最终代码

结果上面的处理,缝缝补补,最终代码如下:

# -*- coding:utf-8 -*-import requestsimport reimport urllibimport codecsurl = "https://m.weibo.cn/api/container/getIndex"#请求的urlheaders = {    "Host": "m.weibo.cn",    "Referer": "https://m.weibo.cn/u/1761379670",    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",}#请求头params = {          "type": "uid",          "value": "{uid}",          "containerid": "{containerid}",          "page":"{page}"}#请求携带的参数def get_Data(uid="3303658163", containerid="1005053303658163"):    total = 3000  #打算爬取的页数,比如100页    content = []  #存放获取到的微博正文内容    page =1  #页码,从第一页开始算    last_length = 0 #上一个的内容长度,用于对比上一次的总体内容长度跟这次是否一致,如果一致,则认为没有新数据,停止脚本处理    for i in range(total):        params["page"] = str(page)        params['uid'] = uid        params['containerid'] = str(containerid)        res = requests.get(url, headers=headers, params=params)        print(res.json().get("data"))        cards = res.json().get("data").get("cards")        # 获取carads下的所有项        for card in cards:            if card.get("card_type") == 9:                text = card.get("mblog").get("text")                kw = urllib.request.quote(text)                old_kw = re.sub("%0A","",kw)                new_text = urllib.request.unquote(old_kw)                # %0A  这串数字对应的就是这个回车字符                pattern = re.compile(r"<.*?>|转发微博|查看图片|查看动图|>")                #这里就是把<>符号内的都匹配出来,正则规则                text = re.sub(pattern,"",new_text)                content.append(text)        page +=1        if (len(content) == last_length):            print("已经获取不到更多内容,脚本暂停处理")            break        else:            last_length = len(content)            print("抓取第{page}页,目前总共抓取了 {count} 条微博".format(page=page, count=len(content)))            with codecs.open('jb.txt', 'w', encoding='utf-8') as f:                f.write("\n".join(content))if __name__ == '__main__':    get_Data("1761379670", "1005051761379670")复制代码

功能介绍的话,一路看下来就很明朗了,一句话就是,解析json而已;

可能有同学问,上面的代码如何使用?直接copy出来执行即可,如果想爬某人的信息,比如吉泽明步:

https://m.weibo.cn/u/2360092592?uid=2360092592&luicode=10000011&lfid=100103type%3D1%26q%3D%E5%90%89%E6%B3%BD%E6%98%8E%E6%AD%A5复制代码

打开她的手机版微博主页

然后把浏览器的F12,重新刷新下网页,搜索get关键词,从而获得value跟containerid,直接填写到get_Data方法里面即可~

最后的输出结果如下:

说明

该脚本可能依赖于网页结果,一旦网页结构发生变化,该脚本即不适用,请了解~

尝试过20个左右的用户,均可数据,如遇到问题,请留言告知,谢谢~

感谢

本文感谢三三同学的极力支持,否则如研究PC版,估计就凉了~

小结

本文主要解析怎么爬取手机版的微博内容,主要原理是解析json,遇到有趣的问题有2个,第一是正则,想获取什么,把不需要的处理掉就好了,不然什么都()去做,太麻烦了~第二,微博的换行符,一开始还想着\n匹配处理,结果发现不行,后来换个角度,弄成编码的格式就发现问题了;

好了,本文到此,谢谢大家~

转载地址:http://fvtax.baihongyu.com/

你可能感兴趣的文章
kafka指定partition的分区规则
查看>>
深入浅出妙用 Javascript 中 apply、call、bind
查看>>
linux常用运维命令
查看>>
使用silverlight自定义控件时“给定关键字不在字典中”
查看>>
Edison UVALive3488
查看>>
微信如何唤起外部浏览器打开指定链接
查看>>
linux之虚拟机搭建
查看>>
js数据类型以及原型分析
查看>>
laravel model relationship
查看>>
给老谢画的图(平面几何中的动点与最值问题)
查看>>
Step by step guide to set up master and slave machines on Windows
查看>>
理解java中的ThreadLocal 专题
查看>>
先有的资源,能看的速度看,不能看的,抽时间看。说不定那天就真的打不开了(转)...
查看>>
java开发一个应用的总结
查看>>
poj 1131进制转换
查看>>
android:layout_weight属性详解
查看>>
java随机生成字符串和校验
查看>>
[编程] TCP协议概述
查看>>
HashMap 原理?jdk1.7 与 1.8区别
查看>>
阿里云 Debian Linux 布署记录
查看>>