JB的Python之旅-爬虫篇-新浪微博内容爬取

前言

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

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

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

PC找内容

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

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

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

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

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

转战手机版

就这样,转战手机版,手机版苍老师连接以下:https://m.weibo.cn/u/1739928273;
老规矩,F12刷新网页,而后把请求一条一条过,结果发现一个玩意:app

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

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

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

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

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

type=uid
value=1739928273
containerid=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 requests

url = "https://m.weibo.cn/api/container/getIndex"
#请求的url

headers = {
    "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).content
print(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"(.*?)<span.*>(.*?)")
text = re.sub(pattern,"",text)
复制代码

从上面能够看到问题例子以下,那咱们只须要把里面的内容都干掉就行了~

啊啊啊啊啊啊啊啊啊
<span class = "url-icon"><img alt = [允悲] src = "https://user-gold-cdn.xitu.io/2018/6/29/1644b1bf47628785?w=32&h=32&f=png&s=2591" style = "width:1em; height:1em;"/></span> 
复制代码

结果以下:

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

这尼玛,竟然有个换行符??那咱们把获取到的text打印如下~

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

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

import urllib
kw = urllib.request.quote("很紧张啊啊啊啊↵<s")
print(kw)
复制代码

输出的内容长这样的:

%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,那假如咱们把上面这串结果匹配成空格,是否是就能解决问题?

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

啊啊啊啊啊啊啊啊啊
<s
复制代码

最后会发现%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 requests
import re
import urllib


url = "https://m.weibo.cn/api/container/getIndex"
#请求的url

headers = {
    "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 requests
import re
import urllib
import codecs


url = "https://m.weibo.cn/api/container/getIndex"
#请求的url

headers = {
    "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"<.*?>|转发微博|查看图片|查看动图|&gt;")
                #这里就是把<>符号内的都匹配出来,正则规则
                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=100103
type%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匹配处理,结果发现不行,后来换个角度,弄成编码的格式就发现问题了;

好了,本文到此,谢谢你们~

相关文章
相关标签/搜索