运维学python之爬虫高级篇(七)scrapy爬取知乎关注用户存入mongodb

首先,祝大家开工大吉!
本篇将要介绍的是从一个用户开始,通过抓关注列表和粉丝列表,实现用户的详细信息抓取并将抓取到的结果存储到 MongoDB。

1 环境需求

基础环境沿用之前的环境,只是增加了MongoDB(非关系型数据库)和PyMongo(Python 的 MongoDB 连接库),默认我认为大家都已经安装好并启动 了MongoDB 服务。

项目创建、爬虫创建、禁用ROBOTSTXT_OBEY设置略(可以参考上一篇)

2 测试爬虫效果

我这里先写一个简单的爬虫,爬取用户的关注人数和粉丝数,代码如下:

# -*- coding: utf-8 -*-
import scrapy

class ZhuHuSpider(scrapy.Spider):
    """
    知乎爬虫
    """
    name = ‘zhuhu‘
    allowed_domains = [‘zhihu.com‘]
    start_urls = [‘https://www.zhihu.com/people/wo-he-shui-jiu-xing/following‘]

    def parse(self, response):
        # 他关注的人数
        tnum = response.css("strong.NumberBoard-itemValue::text").extract()[0]
        # 粉丝数
        fnum = response.css("strong.NumberBoard-itemValue::text").extract()[1]
        print("他关注的人数为:%s" % tnum)
        print("他粉丝的人数为:%s" % fnum)

pychram中运行的结果如下:

出现500错误了,我们加上headers再试试,我们直接在settings.py中设置,如下:

再次执行看看结果:

这次就正常获取到我们需要的信息了

3 爬取分析

我们就用中本聪的主页作为分析入口吧,主页如下:
https://www.zhihu.com/people/satoshi_nakamoto/following
分析用户关注列表如下:

鼠标放到用户图像上,会显示详细信息如下:

这里要注意我用的是火狐浏览器,选择网络--XHR来获取信息

ajax技术的核心是XMLHttpRequest对象(简称XHR),这是由微软首先引入的一个特性,其他浏览器提供商后来都提供了相同的实现。XHR为向服务器发送请求和解析服务器响应提供了流畅的接口,能够以异步方式从服务器取得更多信息,意味着用户单击后,可以不必刷新页面也能取得新数据。

通过上面的请求我们可以获取的连接如下:

#用户详细信息
https://www.zhihu.com/api/v4/members/li-kang-65?include=allow_message,is_followed,is_following,is_org,is_blocking,employments,answer_count,follower_count,articles_count,gender,badge[?(type=best_answerer)].topics

https://www.zhihu.com/api/v4/members/jin-xiao-94-7?include=allow_message,is_followed,is_following,is_org,is_blocking,employments,answer_count,follower_count,articles_count,gender,badge[?(type=best_answerer)].topics

#关注的人信息
https://www.zhihu.com/api/v4/members/satoshi_nakamoto/followees?include=data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics&offset=0&limit=20

通过分析上面的链接可以看出
1.用户详细信息链接组成:https://www.zhihu.com/api/v4/members/{user}?include={include}
其中user是用户的url_token,include是allow_message,is_followed,is_following,is_org,is_blocking,employments,answer_count,follower_count,articles_count,gender,badge[?(type=best_answerer)].topics
2.关注人信息链接组成:https://www.zhihu.com/api/v4/members/satoshi_nakamoto/followees?include={include}&offset={offset}&limit={limit}
其中include为data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics,offset为分页偏移量,limit为每页用户数量,可以通过下图看出:

第一页

第二页

第三页

4 开始爬取

我们还是先写一个简易的爬虫,把功能先实现,代码如下:

# -*- coding: utf-8 -*-
import scrapy

class ZhuHuSpider(scrapy.Spider):
    """
    知乎爬虫
    """
    name = ‘zhuhu‘
    allowed_domains = [‘zhihu.com‘]
    # 用户详细信息地址
    user_detail = ‘https://www.zhihu.com/api/v4/members/{user}?include={include}‘
    # 用户详细信息中的include
    user_include = ‘allow_message,is_followed,‘                    ‘is_following,‘                    ‘is_org,is_blocking,‘                    ‘employments,‘                    ‘answer_count,‘                    ‘follower_count,‘                    ‘articles_count,‘                    ‘gender,‘                    ‘badge[?(type=best_answerer)].topics‘
    # 关注的人地址
    follow_url = ‘https://www.zhihu.com/api/v4/members/{user}/followees?include={include}&offset={offset}&limit={limit}‘
    # 关注的人include
    follow_include = ‘data[*].answer_count,‘                      ‘articles_count,‘                      ‘gender,‘                      ‘follower_count,‘                      ‘is_followed,‘                      ‘is_following,‘                      ‘badge[?(type=best_answerer)].topics‘
    # 初始user
    start_user = ‘satoshi_nakamoto‘

    def start_requests(self):
        # 这里重新定义start_requests方法,注意这里的format用法
        yield scrapy.Request(self.user_detail.format(user=self.start_user, include=self.user_include),
                             callback=self.parse_user)
        yield scrapy.Request(self.follow_url.format(user=self.start_user, include=self.follow_include, offset=20, limit=20),
                             callback=self.parse_follow)

    def parse_user(self, response):
        print(‘user:%s‘ % response.text)

    def parse_follow(self, response):
        print(‘follow:%s‘ % response.text)

输出结果如下:

这里需要注意的是authorization信息一定要在headers中添加,不然会报错,authorization在headers中的形式如下:

测试发现authorization值在一段时间内是不会发生变化的,是否永久不变还有待考证。

5 parse_user编写

parse_user方法用来解析用户的详细数据,存储并发现此用户的关注列表,返回给parse_follow方法来处理,用户详细存储字段如下:

为了省事我把所有字段都添加到items.py中(如果运行spider后报错,提示字段未找到,就将那个字段添加进来即可),如下:

class UserItem(scrapy.Item):
    """
    定义了响应报文中json的字段
    """
    is_followed = scrapy.Field()
    avatar_url_template = scrapy.Field()
    user_type = scrapy.Field()
    answer_count = scrapy.Field()
    is_following = scrapy.Field()
    url = scrapy.Field()
    type = scrapy.Field()
    url_token = scrapy.Field()
    id = scrapy.Field()
    allow_message = scrapy.Field()
    articles_count = scrapy.Field()
    is_blocking = scrapy.Field()
    name = scrapy.Field()
    headline = scrapy.Field()
    gender = scrapy.Field()
    avatar_url = scrapy.Field()
    follower_count = scrapy.Field()
    is_org = scrapy.Field()
    employments = scrapy.Field()
    badge = scrapy.Field()
    is_advertiser = scrapy.Field()

parse_user方法代码如下:

    def parse_user(self, response):
        """
        解析用户详细信息方法
        :param response: 获取的内容,转化为json格式
        """
        # 通过json.loads方式转换为json格式
        results = json.loads(response.text)
        # 引入item类
        item = UserItem()
        # 通过循环判断字段是否存在,存在将结果存入items中
        for field in item.fields:
            if field in results.keys():
                item[field] = results.get(field)
        # 直接返回item
        yield item
        # 将获取的用户通过format方式组合成新的url,调用callback函数交给parse_follow方法解析
        yield scrapy.Request(self.follows_url.format(user=results.get(‘url_token‘),
                                                     include=self.follow_include, offset=0, limit=20),
                             callback=self.parse_follow)

6 parse_follow方法编写

首先也要将获取的response转换为json格式,获取关注的用户,对每一个用户继续爬取,同时也要处理分页。可以看下面两个图:

重新编写后的parse_follow方法如下:

    def parse_follow(self, response):
        """
        解析关注的人列表方法
        """
        # 格式化response
        results = json.loads(response.text)
        # 判断data是否存在,如果存在就继续调用parse_user解析用户详细信息
        if ‘data‘ in results.keys():
            for result in results.get(‘data‘):
                yield scrapy.Request(self.user_detail.format(user=result.get(‘url_token‘), include=self.user_include),
                                     callback=self.parse_user)
        # 判断paging是否存在,如果存在并且is_end参数为False,则继续爬取下一页,如果is_end为True,说明为最后一页
        if ‘paging‘ in results.keys() and results.get(‘paging‘).get(‘is_end‘) == False:
            next_page = results.get(‘paging‘).get(‘next‘)
            yield scrapy.Request(next_page, callback=self.parse_follow)

运行爬虫后的结果如下图:

可以看到一直在获取内容。

7 存入mongodb

7.1 item pipeline

存储使用MongoDB,我们需要修改Item Pipeline,参照官网示例修改的代码如下:

class ZhiHuspiderPipeline(object):
    """
    知乎数据存入monogodb数据库类,参考官网示例
    """

    collection_name = ‘user‘

    def __init__(self, mongo_uri, mongo_db):
        """
        初始化参数
        :param mongo_uri:mongo uri
        :param mongo_db: db name
        """
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get(‘MONGO_URI‘),
            mongo_db=crawler.settings.get(‘MONGO_DATABASE‘, ‘items‘)
        )

    def open_spider(self, spider):
        # 打开连接
        self.client = pymongo.MongoClient(self.mongo_uri)
        # db_auth因为我的mongodb设置了认证,所以需要这两步,未设置可以注释
        self.db_auth = self.client.admin
        self.db_auth.authenticate("admin", "password")
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        # 这里使用update方法
        self.db[self.collection_name].update({‘url_token‘: item[‘url_token‘]},  dict(item), True)
        return item

这里要说一说update方法,update() 方法用于更新已存在的文档。语法格式如下:

db.collection.update(
   <query>,  # update的查询条件,类似sql update查询内where后面的
   <update>, #  update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
   {
     upsert: <boolean>, # 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
     multi: <boolean>, # 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新
     writeConcern: <document> # 可选,抛出异常的级别。
   }
)

使用update方法,如果查询数据存在的话就更新,不存在的话就插入dict(item),这样就可以去重了。

7.2 settings配置


再次运行spider后结果如下:

也可以看到mongodb中数据,如下:


本篇部分参考:https://www.cnblogs.com/qcloud1001/p/6744070.html

到此本篇完结。

原文地址:http://blog.51cto.com/linuxliu/2072925

时间: 2024-10-09 22:21:18

运维学python之爬虫高级篇(七)scrapy爬取知乎关注用户存入mongodb的相关文章

运维学python之爬虫高级篇(六)scrapy模拟登陆

上一篇介绍了如何爬取豆瓣TOP250的相关内容,今天我们来模拟登陆GitHub. 1 环境配置 语言:Python 3.6.1 IDE: Pycharm 浏览器:firefox 抓包工具:fiddler 爬虫框架:Scrapy 1.5.0 操作系统:Windows 10 家庭中文版 2 爬取前分析 分析登陆提交信息分析登陆信息我使用的是fiddler,fiddler的使用方法就不作介绍了,大家可以自行搜索,首先我们打开github的登陆页面,输入用户名密码,提交查看fiddler获取的信息,我这

运维学python之爬虫高级篇(五)scrapy爬取豆瓣电影TOP250

对于scrapy我们前面已经介绍了简单的应用,今天我们用一个完整的例子,爬取豆瓣电影TOP250来做一个小的练习,把scrapy阶段做一个总结. 1 环境配置 语言:Python 3.6.1 IDE: Pycharm 浏览器:firefox 爬虫框架:Scrapy 1.5.0 操作系统:Windows 10 家庭中文版 2 爬取前分析 2.1 需要保存的数据 首先确定我们要获取的内容,在items中定义字段,来将非结构化数据生成结构化数据,获取的内容主要包括:排名.电影名称.得分.评论人数.如下

运维学python之爬虫中级篇(五)数据存储(无数据库版)

本篇主要介绍,爬取html数据后,将html的正文内容存储为json或csv格式. 1 json格式存储 选定要爬取的网站后,我们利用之前学过的内容,如:Beautiful Soup.xpath等方式解析,来获取我们希望得到的内容. 1.1 获取数据 首先使用urllib访问页面https://www.lagou.com/zhaopin/Python/?labelWords=label获取html内容,代码如下: from urllib import request try: url = 'ht

运维学python之爬虫中级篇(六)基础爬虫

通过这么多天对爬虫的介绍, 我们对爬虫也有所了解,今天我们将引入一个简单爬虫的技术架构,解释爬虫技术架构中的几个模块,后面的爬虫也多是今天架构的延伸,只不过这个架构是简单的实现,对优化.爬取方式等不是很完善,主要为了方便大家对爬虫的理解和后面的编程. 1 基础架构和流程 简单的爬虫架构由以下几部分构成:爬虫调度器:总体协调其它几个模块的工作URL管理器:负责管理URL,维护已经爬取的URL集合和未爬取的URL集合网页下载器:对未爬取的URL下载网页解析器:解析已下载的html,并从中提取新的UR

运维学python之爬虫中级篇(八)MongoDB

1 MongoDB MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.具有高性能.高可用性和自动扩展性.MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成.MongoDB 文档类似于 JSON 对象的BSON.字段值可以包含其他文档,数组及文档数组.MongoDB最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引. 1.1 安装Mongo

爬虫学习 Python网络爬虫第三弹《爬取get请求的页面数据》

爬虫学习 Python网络爬虫第三弹<爬取get请求的页面数据> 一.urllib库 urllib是Python自带的一个用于爬虫的库,其主要作用就是可以通过代码模拟浏览器发送请求.其常被用到的子模块在Python3中的为urllib.request和urllib.parse,在Python2中是urllib和urllib2. 二.由易到难的爬虫程序: 1.爬取百度首页面所有数据值 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #导包 4

python之爬虫(十一) 实例爬取上海高级人民法院网开庭公告数据

通过前面的文章已经学习了基本的爬虫知识,通过这个例子进行一下练习,毕竟前面文章的知识点只是一个 一个单独的散知识点,需要通过实际的例子进行融合 分析网站 其实爬虫最重要的是前面的分析网站,只有对要爬取的数据页面分析清楚,才能更方便后面爬取数据 目标站和目标数据目标地址:http://www.hshfy.sh.cn/shfy/gweb/ktgg_search.jsp目标数据:目标地址页面的中间的案开庭公告数据 对数据页面分析从打开页面后可以看到默认的数据是一个月的数据,即当天到下个月该天的通过翻页

【Python网络爬虫四】通过关键字爬取多张百度图片的图片

最近看了女神的新剧<逃避虽然可耻但有用>,同样男主也是一名程序员,所以很有共鸣 被大只萝莉萌的一脸一脸的,我们来爬一爬女神的皂片. 百度搜索结果:新恒结衣 本文主要分为4个部分: 1.下载简单页面 2.爬取多张图片 3.页面解码 4.爬取过程排错处理 1.下载简单页面 通过查看网页的html源码,分析得出,同一张图片共有4种链接: {"thumbURL":"http://img5.imgtn.bdimg.com/it/u=2243348409,3607039200

爬取知乎百万用户信息之总结篇

第一个大错误是没能及时释放非托管资源,导致程序运行长的之后抛出OutOfMemoryException. 这个小Demo主要的非托管资源一个是http请求的httpWebresopne和流,另外一个是RedisCline.导致这个问题出现不是我不知道要释放非托管资源,而是代码疏忽.这个写代码习惯应该是很久了,因为以前程序并没有运行很久在,这个问题并没有暴露出来 刚开始时候是这样写的 using (StreamReader reader = new StreamReader(stream, Enc