今天用递归写了个知乎所有用户信息的爬虫,源代码放在了github上,有兴趣的同学可以上去下载一下看看,源码地址:https://github.com/xiaobeibei26/zhihu_user_spider
这里介绍一下代码逻辑以及分页分析,首先看网页,这里本人随便选了一个大V作为入口,然后点开他的关注列表,如图
注意,本人爬虫的全称都是处于非登录状态的。
这里的粉丝列表以及关注者列表都是后台ajax请求得到的数据(没有听过ajax的童鞋别慌,ajax请求跟普通浏览器的请求没有区别,它主要就是在我们浏览网页时候偷偷给服务器发送的请求,就是为了节省流量以及减少请求数,不然每次看点新数据都全部刷新网页,服务器压力很大的,所以有了这玩意),
然后我们找到粉丝列表以及关注者列表的URL,这个很简单,在chrome浏览器下面点击一下页数切换就可以找到,如图
找到关注者以及粉丝的URL就好办理,下面看一看这些数据,这里以粉丝的数据举例,如图,是一段json
这里找到了粉丝的数据,不过这里不是用户的详细信息,只有部分数据,不过他提供了一个token_url,我们就可以获取这个ID访问用户的详细信息了,我们看看每个用户的详细信息怎么提取。
这里楼主发现,在观看粉丝或者关注列表的时候,网页是会自动触发该用户详细信息的请求,如图
这次获得的是用户详细信息查询的URL,这里看一看这个详细信息的URL,如图
上面介绍了网页的基础分析,下面说一下代码的思路,这次爬虫用到了递归,本次用的scrapy抓取以及mogodb数据库存储的。
首先本人是用了一个大V作为爬虫第一个网页,然后分三步,第一步是爬了该大V的详细信息然后存入数据库,第二步是爬取了该大V的粉丝,第三是爬取了该大V的关注者(其实就是爬取粉丝或者关注者的token_url),完成之后,利用爬取的粉丝以及关注者的数据构造他们每个人详细信息的url,然后挖取详细信息存入数据库。到这里递归第一步算是完成了,然后爬虫会从每一个粉丝和关注者入手,分别爬取他们的粉丝以及关注者的详细数据,不断递归
在代码里面还有加入了一些自动翻页的功能,有兴趣可以看看。
这里贴两张代码图
第一张是我们item里面定义要抓取的数据
import scrapy
class ZhihuUserItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
answer_count = scrapy.Field()#回答数量
articles_count = scrapy.Field()#写过的文章数
follower_count = scrapy.Field()#粉丝数量
following_count = scrapy.Field()#关注了多少人
educations=scrapy.Field()#教育背景
description = scrapy.Field()#个人描述
locations = scrapy.Field()#所在地
url_token =scrapy.Field()#知乎给予的每个人用户主页唯一的ID
name=scrapy.Field()#用户昵称
employments = scrapy.Field()#工作信息
business=scrapy.Field()#一些工作或者商业信息的合集
user_type =scrapy.Field()#用户类型,可以是个人,也可以是团体等等
headline =scrapy.Field()#个人主页的标签
voteup_count = scrapy.Field()#获得的赞数
thanked_count=scrapy.Field()#获得的感谢数
favorited_count = scrapy.Field()#被收藏次数
avatar_url = scrapy.Field()#头像URl
第二张是主爬虫的代码,有兴趣的可以去github看看详细的
# -*- coding: utf-8 -*-
import scrapy
from scrapy import Request
import json
from zhihuuser.items import ZhihuUserItem#导入我们刚刚定义的items,这里从文件最外层开始带入
class ZhihuSpider(scrapy.Spider):
name = "zhihu"
allowed_domains = ["www.zhihu.com"]
start_urls = ['http://www.zhihu.com/']
start_user ='wang-tuan-jie-55'#开始放进去的第一个用户的ID
include_follow='data[*].answer_count, articles_count, gender, follower_count, is_followed, is_following, badge[?(type = best_answerer)].topics'
#上面这个是查询粉丝或者关注列表里面的用户需要附带的参数
include_userinfo='locations,employments,gender,educations,business,voteup_count,thanked_Count,follower_count,following_count,cover_url,following_topic_count,following_question_count,following_favlists_count,following_columns_count,avatar_hue,answer_count,articles_count,pins_count,question_count,commercial_question_count,favorite_count,favorited_count,logs_count,marked_answers_count,marked_answers_text,message_thread_token,account_status,is_active,is_force_renamed,is_bind_sina,sina_weibo_url,sina_weibo_name,show_sina_weibo,is_blocking,is_blocked,is_following,is_followed,mutual_followees_count,vote_to_count,vote_from_count,thank_to_count,thank_from_count,thanked_count,description,hosted_live_count,participated_live_count,allow_message,industry_category,org_name,org_homepage,badge[?(type=best_answerer)].topics'
#上面这个是查询个人信息需要附带的一个参数
followers_url = 'https://www.zhihu.com/api/v4/members/{user_name}/followers?include={include_follow}&offset={offset}&limit={limit}'
#获取粉丝列表的url,里面的参数分别是用户的ID,查询参数,这个在浏览器复制就可以了,offset表示第几页的粉丝或者关注者,limit表示每页的数量,这里网页上默认是20
followees_url = 'https://www.zhihu.com/api/v4/members/{user_name}/followees?include={include_follow}&offset={offset}&limit={limit}'
# 获取关注列表的URL,根上面的就差了一个字母
userinfo_url= 'https://www.zhihu.com/api/v4/members/{user_name}?include={include_userinfo}'
#上面这个是提取用户信息信息的url
def start_requests(self):
yield Request(url=self.userinfo_url.format(user_name=self.start_user,include_userinfo=self.include_userinfo),callback=self.get_user_info)
#上面是访问第一个用户,获取详细信息
yield Request(url=self.followers_url.format(user_name=self.start_user,include_follow=self.include_follow,offset=0,limit=20),callback=self.get_followers_parse)
#上面是访问第一个用户的粉丝列表,下面是访问关注列表
yield Request(url=self.followees_url.format(user_name=self.start_user,include_follow=self.include_follow,offset=0,limit=20),callback=self.get_followees_parse)
def get_user_info(self,response):#获取用户信息信息
data = json.loads(response.text)
#print(data)
item = ZhihuUserItem()
for Field in item.fields:#可以获取在item里面定义的key值,就是那些locations,employments等
#print(Field)
if Field in data.keys():
item[Field]=data.get(Field)#获取字典里面的值
yield item
yield Request(url=self.followers_url.format(user_name=data.get('url_token'),include_follow=self.include_follow,offset=0,limit=20),callback=self.get_followers_parse)
yield Request(url=self.followees_url.format(user_name=data.get('url_token'), include_follow=self.include_follow, offset=0,limit=20), callback=self.get_followees_parse)
def get_followers_parse(self, response):#获取粉丝列表
try:#这里添加的异常是防止有些用户没有粉丝
followers_data = json.loads(response.text)
try:
if followers_data.get('data'): # data里面是一个由字典组成的列表,每个字典是粉丝的相关信息
for one_user in followers_data.get('data'):
user_name = one_user['url_token']#提取url_token然后访问他的详细信息
yield Request(url=self.userinfo_url.format(user_name=user_name,include_userinfo=self.include_userinfo),callback=self.get_user_info)
#将所有粉丝或者关注者的url_token提取出来,放进一开始我们构造的用户详细信息的网址里面,提取他们的信息
if 'paging' in followers_data.keys() and followers_data.get('paging').get('is_end') ==False:
yield Request(url=followers_data.get('paging').get('next'),callback=self.get_followers_parse)
except Exception as e:
print(e,'该用户没有url_token')
except Exception as e:
print(e,' 该用户没有粉丝')
def get_followees_parse(self,response):#获取关注者的函数
try:#这里添加的异常是防止有些用户没有关注者
followees_data = json.loads(response.text)
try:
if followees_data.get('data'):
for one_user in followees_data.get('data'):
user_name = one_user['url_token']#提取url_token然后访问他的详细信息
yield Request(url=self.userinfo_url.format(user_name=user_name,include_userinfo=self.include_userinfo),callback=self.get_user_info)
#将所有粉丝或者关注者的url_token提取出来,放进一开始我们构造的用户详细信息的网址里面,提取他们的信息
if 'paging' in followees_data.keys() and followees_data.get('paging').get('is_end') ==False:#判断是否有下一页
yield Request(url=followees_data.get('paging').get('next'),callback=self.get_followees_parse)
except Exception as e:
print(e,'该用户没有url_token或者data')
except Exception as e:
print(e,' 该用户没有粉丝')
代码一共不足80行,运行了一分钟就抓了知乎一千多个用户的信息,
pipline代码如图
from pymongo import MongoClient
class ZhihuuserPipeline(object):
def __init__(self):
self.client = MongoClient(host='123.207.126.72',port=27017)
self.database = self.client['zhuhu_spider']
self.db = self.database['zhuhu_user_infomation']
def process_item(self, item, spider):#这里以每个用户url_token为ID,有则更新,没有则插入
self.db.update({'url_token':item['url_token']},dict(item),True)
return item
def close_spider(self,spider):
self.client.close()
这里上张结果图
最近忙完别的事了,终于可以天天写爬虫了,不知道大家这篇有什么问题不,可以随便向我提
最后提一提,爬取一定要伪装好headers,里面有些东西服务器每次都会检查