前言
记得半个月之前的一晚,媳妇跟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匹配处理,结果发现不行,后来换个角度,弄成编码的格式就发现问题了;
好了,本文到此,谢谢大家~