scrapy分页抓取网页(本篇一个用户关注列表和粉丝列表(3爬取分析))
优采云 发布时间: 2022-01-07 19:06scrapy分页抓取网页(本篇一个用户关注列表和粉丝列表(3爬取分析))
首先祝大家有个好的开始!
本文要介绍的是从一个用户入手,通过抓取下面的列表和粉丝列表,实现用户的详细信息抓取,并将抓取的结果存储到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 爬行分析
让我们以中本聪的主页作为分析的入口。主页如下:
分析用户关注列表如下:
将鼠标放在用户图像上,会显示详细信息如下:
请注意,我使用的是火狐浏览器,选择网络-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.用户详情链接组成:{user}?include={include}
其中user是用户的url_token,包括allow_message、is_followed、is_following、is_org、is_blocking、employees、answer_count、follower_count、articles_count、gender、badge[?(type=best_answerer)].topics
2. 关注者信息链接组成:{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)
输出如下:
这里需要注意的是,必须在headers中添加授权信息,否则会报错。headers中的授权形式如下:
测试发现授权值在一段时间内不会发生变化,是否会永远保持不变还有待验证。
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 保存到 mongodb7.1 项管道
为了存储和使用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(
, # update的查询条件,类似sql update查询内where后面的
, # update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
{
upsert: , # 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
multi: , # 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新
writeConcern: # 可选,抛出异常的级别。
}
)
使用update方法,如果查询数据存在,就更新,如果不存在,就插入到dict(item)中,这样就可以去掉重复项了。
7.2 设置配置
再次运行spider后的结果如下:
也可以查看mongodb中的数据,如下:
本文中的一些参考资料:
这篇文章到此结束。
本页地址: