基于Requests和Ajax分析法的新浪微博内容及用户信息爬取

1 项目介绍

  • 新浪微博:新浪微博是一个由新浪网推出,提供微型博客服务类的社交网站。用户可以通过网页、WAP页面、手机客户端、手机短信、彩信发布消息或上传图片。新浪可以把微博理解为“微型博客”或者“一句话博客”。用户可以将看到的、听到的、想到的事情写成一句话,或发一张图片,通过电脑或者手机随时随地分享给朋友,一起分享、讨论;还可以关注朋友,即时看到朋友们发布的信息。

本项目进行了新浪微博-个人主页的所有微博信息采集。以“新浪微博-个人主页的所有微博信息采集”为例。在实操过程中,可根据自身需求,更换新浪微博的其他内容进行数据采集。

2 页面分析

2.1 需求分析

  • 爬取的网址: https://weibo.com/u/5305630013
    在这里插入图片描述
  • 获取的博主信息:
    • 微博博主名称,博主关注数量,博主粉丝数量,博主地址,博主个人简介,博主个人标签。
  • 获取该博主的博客信息:
    • 微博名称,微博发布时间,微博发布内容

2.2 找出微博用户的微博内容api

2.2.1 页面元素审查

一般做爬虫爬取网站时,首选的都是m站,其次是wap站,最后考虑PC站,因为PC站的各种验证最多。当然,这不是绝对的,有的时候PC站的信息最全,而你又恰好需要全部的信息,那么PC站是你的首选。一般m站都以m开头后接域名, 我们这次通过m.weibo.cn去分析微博的HTTP请求。

  • 首先在PC打开微博主页:Python开发者微博主页

  • 接着打开调试页面(F12,或右击检查),先选中右侧手机模式,然后刷新页面,这个时候就出来了m站微博主页:
    https://m.weibo.com/p/id, 也有些是m站微博主页为:https://m.weibo.com/u/id;

  • 注意:
    1)当你打开一个页面,再点开network标签时是不会有信息的,我们需要在打开的情况下,刷新一下页面;
    2)为了防止页面突然的跳转而丢失信息,一定要勾上preserved单选框。

2.2.2 XHR过滤获取API

  • 选择XHR进行过滤,发现有两个已经发送的api请求。(api请求一般都在XHR中,其他网页请求在Doc)

在这里插入图片描述

参数value的值,我们通过采集多个情况进行分析得出,提门是用户的id号;
参数containerid的值,我们通过采集多个情况进行分析得出,它们获取用户内容的containerid为100505+oid,获取微博内容的containerid为107603+oid。

2.2.3 API返回的JSON数据分析

  • 我们在右边选择Preview预览一下json,点击cards中任选一个card,其中的mblog标签下就有我们要的微博内容数据。我们继续观察发现这个json中只有10条数据;
    在这里插入图片描述

  • 那么我们往下滑动到下一页,继续查看请求的api。我们发现在获取下一页数据时的api加了一个值为2的参数page。继续往下翻页,page变成3、4、5…,由此我们推断这个api获取哪一页的数据由page决定。

在这里插入图片描述

2.3 分析返回的json格式的微博内容

  • 通过api我们获取到返回的微博内容数据,我们以其中一个card来分析获取到的数据,微博内容数据在mblog中。

  • 当然可以在Chrome谷歌浏览器中安装一个json-viewer插件, 友好的显示json数据, 如下图所示:

在这里插入图片描述

在这里插入图片描述

3 获取微博内容的代码实现

我们分析完接口之后就可以开始编写爬虫代码。

3.1 获取微博用户信息

def get_user(id):
    # 访问博主详情信息的api地址, 返回json格式;
    weibo_user_url = "https://m.weibo.cn/api/container/getIndex?type=uid&value={0}&containerid=100505{0}".format(id)
    # 获取网页内容;
    weibo_user_info = requests.get(weibo_user_url).json()
    # 获取用户的信息;
    userinfo = weibo_user_info.get("data").get("userInfo")
    # 通过命名元组的方式封装微博用户信息, 便于后面调用;
    from collections import namedtuple
    User = namedtuple('User', 'name follow_count followers_count profile_url description')
    user = User(name=userinfo['screen_name'], follow_count=userinfo['follow_count'],
                followers_count=userinfo['followers_count'],
                profile_url=userinfo['profile_url'], description=userinfo['description'])
    # 返回封装的用户信息;
    return user


if __name__ == '__main__':
    # 定义要爬取的微博大V的微博ID
    id = "5305630013"

    user = get_user(id)
    print("""
                    微博用户信息

          微博名: {0}
          关注数:{1}
          粉丝数:{2}
          微博描述:{3}
     """.format(user.name, user.follow_count, user.followers_count, user.description))

  • 执行效果
    在这里插入图片描述

3.2 获取微博内容

  • 此处有个问题还没有解决?
    • Ajax下拉刷新数据, 获取第2页的信息, 默认的网址是通过since_id来处理的, 但是这个参数对应的值没有找到规律;
    • 后来发现, 最早的page参数也可以生效, 因此就先用老方法解决了;
def get_content(id, page=1):
    """爬取某一页的博客信息"""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36",
    }
    params = {
        "type": "uid",
        "value": id,
        "containerid": "107603" + str(id),
        "page": page
    }
    # 访问博主详情信息的api地址, 返回json格式;
    url = "https://m.weibo.cn/api/container/getIndex"
    # 获取网页内容;
    weibo_content_info = requests.get(url, params=params, headers=headers).json()
    # 微博主页内容,每页的全部微博内容
    content_infos = weibo_content_info.get('data').get('cards')
    # 命名元组
    from collections import namedtuple
    Content = namedtuple('Content', "create_time text")
    contents = []
    for content in content_infos:
        content = content.get('mblog')
        created_time = content.get('created_at')
        text = content.get('text')
        contents.append(Content(create_time=created_time, text=text))

    return contents


if __name__ == '__main__':
    id = "5305630013"
    contents = []
    for page in range(100):
        page_content = get_content(id, page)
        print("****************爬取第%s页, 共%s条数据******************************" % (page + 1, len(page_content)))
        for content in page_content:
            print('- ', content.text)
        contents.extend(page_content)
  • 效果显示:

在这里插入图片描述

5 代码封装与重构

import requests
import os


class WeiboSpider(object):
    def __init__(self,  id, ):
        """提供微博用户的id号"""
        self.id = id
        # 访问博主详情信息的api地址, 返回json格式;
        self.url = "https://m.weibo.cn/api/container/getIndex"
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36",
        }
        self.savedir = '/tmp/weiboFile/%s' %(id)
        if not os.path.exists(self.savedir):
            os.makedirs(self.savedir)

    def get_user_info(self):
        """获取的博主信息:微博博主名称,博主关注数量,博主粉丝数量,博主地址,博主个人简介,博主个人标签。"""
        # 访问博主详情信息的api地址, 返回json格式;

        params = {
            "type": "uid",
            "value": id,
            "containerid": "100505" + str(id)
        }
        try:
            # 获取网页内容;
            weibo_user_info = requests.get(self.url, params=params, headers=self.headers).json()
        except:
            print("网页爬取错误")
        else:
            # 获取用户的信息;
            userinfo = weibo_user_info.get("data").get("userInfo")
            # 通过命名元组的方式封装微博用户信息, 便于后面调用;
            from collections import namedtuple
            User = namedtuple('User', 'name follow_count followers_count profile_url description')
            user = User(name=userinfo['screen_name'], follow_count=userinfo['follow_count'],
                        followers_count=userinfo['followers_count'],
                        profile_url=userinfo['profile_url'], description=userinfo['description'])
            # 返回封装的用户信息;
            return user

    def get_content(self, page=1):
        """
        爬取某一页的博客信息, 
        将每页的图片保存到一个目录中
        """

        params = {
            "type": "uid",
            "value": id,
            "containerid": "107603" + str(id),
            "page": page
        }
        try:
            # 获取网页内容;
            weibo_content_info = requests.get(self.url, params=params, headers=self.headers).json()
        except:
            print("网页爬取错误")
        else:
            # 微博主页内容,每页的全部微博内容
            content_infos = weibo_content_info.get('data').get('cards')
            # 命名元组
            from collections import namedtuple
            Content = namedtuple('Content', "create_time text")
            contents = []
            for content in content_infos:
                content = content.get('mblog')
                created_time = content.get('created_at')
                text = content.get('text')
                img = content.get('original_pic', None)
                if img:
                    img_dir = os.path.join(self.savedir, str(page))
                    if not os.path.exists(img_dir):
                        os.makedirs(img_dir)
                    filename = os.path.join(self.savedir, str(page), img.split('/')[-1])
                    with open(filename, 'wb') as f:
                        f.write(requests.get(img).content)
                        print('图片下载成功....')
                contents.append(Content(create_time=created_time, text=text))
            return contents
        
        
if __name__ == '__main__':
    id = "5305630013"
    wb = WeiboSpider(id)
    user = wb.get_user_info()
    pages = int(input("请输入爬取博客的页数:"))

    for page in range(pages):
        contents = wb.get_content(page + 1)
        for content in contents:
            print(content.create_time, content.text)

效果展示

在这里插入图片描述

在这里插入图片描述

拓展

  • 这个项目也可以使用urllib模块实现, 只是requests是基于urllib封装的模块, 使用更加友好.当然也可以使用Scrapy爬虫框架.

参考资料:

猜你喜欢

转载自blog.csdn.net/gf_lvah/article/details/89376889