查看原文
其他

【视频教程】用python批量抓取简书用户信息

2017-03-19 大邓 大邓带你玩python

你不一定要点蓝字关注我的

请输入标题     bcdef

       前几天在看崔庆才老师的教程,用Scrapy抓知乎用户信息,里面用到了递归。之前我写的爬虫都是将已知的固定数据的网址存到list中,然后遍历list中的网址。这次针对简书,我们使用递归来试一下。

请输入标题     abcdefg

什么是递归

请输入标题     bcdef

       程序(或函数)调用自身的编程技巧称为递归( recursion)。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

请输入标题     abcdefg

递归的优点

请输入标题     bcdef

1、降低问题难度

2、大大地减少了程序的代码量

3、递归的能力在于用有限的语句来定义对象的无限集合

案例实战

如果看图麻烦,可先看大邓录制的视频讲解,请点击链接

https://v.qq.com/txp/iframe/player.html?vid=a0385fr6pmp&width=500&height=375&auto=0

请输入标题     bcdef

      本案例要抓简书高质量用户的信息,如昵称、id、文章数、文集数、文字数、获得的喜欢等。我们只抓一个用户ta所关注的人,至于这个ta的粉丝,我们不抓。相对而言,ta所关注的人比ta的粉丝要质量高端大气上档次些。


1、先从初始用户,如邓旭东HIT所关注的用户列表开始,获得邓旭东所关注的A、B、C等25个用户。

2、再依次抓这25个用户所关注的用户

3、循环前两步


下面我们先分析网址结构

请输入标题     abcdefg

网址url分析

请输入标题     bcdef

我们发现,当我向下滚动关注列表时,网页下方会加载,且在图左下角会多出

following?page=3following?page=4两个xhr类型文件。

将鼠标放置于following?page=3文件上方,悬浮弹出一个网址

http://www.jianshu.com/users/1562c7f16a04/following?page=3

请输入标题     bcdef

我们鼠标点击一下following?page=3 ,点击Headers,发现request url就是


http://www.jianshu.com/users/1562c7f16a04/following?page=3

请输入标题     abcdefg

那么问题来了,page这个数字如何构建呢?

请输入标题     bcdef

我们发现 用户的关注人数除以10,然后取整数再加1,结果就是page的最大数。


比如邓旭东关注了25个人,那么就有page=1,page=2,page=3三个page

现在我们已经构建好了。


网址有了,下面开始分析要抓的数据存放在网页源码的什么位置呢?


网页分析

请输入标题     bcdef

首先我们要先定位多个关注用户的标签,如下图。我们要找到<ul>且该ul标签的属性是

user-list。

请输入标题     bcdef

接下来我们要定位每个用户的数据位置。属性值为info的div标签如下如图

网页解析

请输入标题     bcdef

用户列表数据的ul标签我们使用BeautifulSoup进行定位。返回用户列表ul标签及其子孙标签

请输入标题     abcdefg

请输入标题     bcdef

url = 'http://www.jianshu.com/users/1562c7f16a04/following?page=1'

res = requests.get(url)
bsObj = BeautifulSoup(resp, 'lxml')
user_List_Container = bsObj.findAll('ul', {'class': 'user-list'})[0]

user_List = user_List_Container.contents
user_List = [str(user) for user in user_List if user != '\n']


单个用户数据的div标签我们使用re正则表达匹配出其中的数据。

import re

# 用户ID与name的匹配规律

IdName_pattern = re.compile('<a class="name" href="/u/(.*?)">(.*?)</a>')

#用户的关注、粉丝、文章、文集的匹配规律meta1_pattern = re.compile('<span>关注 (\d+)</span><span>粉丝 (\d+)</span><span>文章 (\d+)</span><span>文集 (\d+)</span>')

# 用户写的字数,获得的喜欢的匹配模式meta2_pattern = re.compile('写了 (\d+) 字,获得了 (\d+) 个喜欢')


for user in user_List:
   Id, Name = re.findall(IdName_pattern, user)[0]
   followingNum, followerNum, articleNum, articleGroupNum = re.findall(meta1_pattern, user)[0]
   wordNum, likeNum = re.findall(self.meta2_pattern, user)[0]


我们把上面两部分构造成三个函数,

请输入标题     bcdef

   #发起请求
   def creat_request(self, userid, page):
       url = self.user_url.format(userid=userid, page=page)
       resp = requests.get(url, headers=self.headers).text
       return resp


   #解析用户列表
   def parse_response(self, resp):
       bsObj = BeautifulSoup(resp, 'lxml')
       user_List_Container = bsObj.findAll('ul', {'class': 'user-list'})[0]
       user_List = user_List_Container.contents
       user_List = [str(user) for user in user_List if user != '\n']
       return user_List


   #解析单个用户的信息
   def parser_user_info(self, user):
       Id, Name = re.findall(self.IdName_pattern, user)[0]
       try:
           followingNum, followerNum, articleNum, articleGroupNum = re.findall(self.meta1_pattern, user)[0]
       except:
           followingNum, followerNum, articleNum, articleGroupNum = ('','','','')
       try:
           wordNum, likeNum = re.findall(self.meta2_pattern, user)[0]
       except:
           wordNum, likeNum = ('','')

       Content = (Id, Name, followingNum, followerNum, articleNum, articleGroupNum, wordNum, likeNum)
       writer.writerow(Content)
       print(Content)
       return Content

请输入标题     bcdef

下面开始构造今天的任务主体,最高潮部分。最难的地方在于对递归的解决,好在总算实现了,可能有点小问题,但是能爬的数据已经挺多的了。


首先,我们要将已经访问的用户的id和following放入ID_container(是一个集合)容器中,以便后面再遇到这个id时,可以验证是否重复访问该id。


其次,用户列表解析 ,用户数据解析。将解析出的用户id和following添加到deque队列中去,待爬去


最后,对队列进行for循环,如果该队列中的元素不在ID_container中,执行函数自身。

请输入标题     bcdef

def get_userlist(self,userid,following):
       ID_container.add((userid,following))
       if following != '':
           num = int(following) / 10
           Page = math.ceil(num)   
           for page in range(1, Page + 1, 1):
               resp = self.creat_request(userid,page)
               user_List = self.parse_response(resp)
               for user in user_List:
                   content = self.parser_user_info(user)
                   Deque.append((content[0],content[2]))
               time.sleep(1)

       for deq in Deque:
           if deq not in ID_container:
               self.get_userlist(deq[0],deq[1])
               print('hello world')

请输入标题     abcdefg

支持

一下

暖心

暖心


我会

坚持

写下去

请输入标题     bcdef

支持后请将支持截图发至后台,您的支持大邓老师铭记在心

请输入标题     abcdefg

回复“简书项目”,即可获得项目源码下载链接

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存