中国领先的IT技术网站
|
|

我用Python爬了7W知乎用户信息,终于捕获了心仪小姐姐.....

本文主要讲 scrapy 框架的原理和使用,建议至少在理解掌握 Python 爬虫原理后再使用框架(不要问我为什么,我哭给你看)。

作者:大吉大利小米酱来源:知乎专栏|2017-11-10 14:12

Tech Neo技术沙龙 | 11月25号,九州云/ZStack与您一起探讨云时代网络边界管理实践


双十一就要来了,在举国一片“买买买”的呼声中,单身汪的咆哮声也愈发凄厉了。

作为一个 Python 程序员,要如何找到小姐姐,避开暴击伤害,在智中取胜呢?于是就有了以下的对话:

so~今天我们的目标是,爬社区的小姐姐~而且,我们又要用到新的姿势(雾)了~scrapy 爬虫框架~

本文主要讲 scrapy 框架的原理和使用,建议至少在理解掌握 Python 爬虫原理后再使用框架(不要问我为什么,我哭给你看)。

scrapy 原理

在写过几个爬虫程序之后,我们就会知道,利用爬虫获取数据大概的步骤:

  • 请求网页。
  • 获取网页。
  • 匹配信息。
  • 下载数据。
  • 数据清洗。
  • 存入数据库。

scrapy 是一个很有名的爬虫框架,可以很方便的进行网页信息爬取。那么 scrapy 到底是如何工作的呢?之前在网上看了不少 scrapy 入门的教程,大多数入门教程都配有这张图。

也不知道是这张图实在太经典了,还是程序员们都懒得画图,我第一次看到这个图的时候,心情是这样的。

经过了一番深入的理解,大概知道这幅图的意思,让我来举个栗子(是的,我又要举奇怪的栗子了):

scrapy 原理图之我要吃好吃的

当我们想吃东西的时候,我们会出门,走到街上,寻找一家想吃的店,然后点餐,服务员再通知厨房去做,最后菜到餐桌上,或者被打包带走。这就是爬虫程序在做的事,它要将所有获取数据需要进行的操作,都写好。

而 scrapy 就像一个点餐 APP 一般的存在,在订餐列表(spiders)选取自己目标餐厅里想吃的菜(items),在收货(pipeline)处写上自己的收货地址(存储方式)。

点餐系统(scrapy engine)会根据订餐情况要求商铺(Internet)的厨房(download)将菜做好,由于会产生多个外卖取货订单(request),系统会根据派单(schedule)分配外卖小哥从厨房取货(request)和送货(response)。说着说着我都饿了。。。。

什么意思呢?在使用 scrapy 时,我们只需要设置 spiders(想要爬取的内容),pipeline(数据的清洗,数据的存储方式),还有一个 middlewares,是各功能间对接时的一些设置,就可以不用操心其他的过程,一切交给 scrapy模块来完成。

创建 scrapy 工程

安装 scrapy 之后,创建一个新项目:

  1. $ scrapy startproject zhihuxjj 

我用的是 pycharm 编译器,在 spiders 文件下创建 zhihuxjj.py。

在 zhihuxjj.py 这个文件中,我们要编写我们的爬取规则。

爬取规则制定(spider)

创建好了项目,让我们来看一下我们要吃的店和菜…哦不,要爬的网站和数据。

我选用了知乎作为爬取平台,知乎是没有用户从 1 到 n 的序列 id 的,每个人可以设置自己的个人主页 id,且为唯一。

所以采选了一枚种子用户,爬取他的关注者,也可以关注者和粉丝一起爬,考虑到粉丝中有些三无用户,我仅选择了爬取关注者列表,再通过关注者主页爬取关注者的关注者,如此递归。

对于程序的设计,是这样的。

之后就是种子用户的个人主页,知乎粉丝多的大 V 很多,但是关注多的人就比较难发现了,这里我选择了知乎的黄继新,联合创始人,想必关注了不少优质用户(???)?。

分析一下个人主页可知,个人主页由'https://www.zhihu.com/people/' + 用户 id 组成。

我们要获取的信息是用 callback 回调函数(敲黑板!!划重点!!)的方式设计,这里一共设计了俩个回调函数:用户的关注列表和关注者的个人信息。

使用 chrome 浏览器查看上图的页面可知获取关注列表的 url,以及关注者的用户 id。

将鼠标放在用户名上,如下图:

可以获得个人用户信息的 url,分析 url 可知:

  1. 关注者列表链接构成:'https://www.zhihu.com/api/v4/members/' + '用户id' + '/followees?include=data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics&offset=0&limit=20' 
  2. 个人信息链接构成:'https://www.zhihu.com/api/v4/members/' + '用户id' + '?include=allow_message%2Cis_followed%2Cis_following%2Cis_org%2Cis_blocking%2Cemployments%2Canswer_count%2Cfollower_count%2Carticles_count%2Cgender%2Cbadge%5B%3F(type%3Dbest_answerer)%5D.topics' 

so,我们在上一节中创建的 zhihuxjj.py 文件中写入以下代码:

  1. import json 
  2. from zhihuxjj.items import ZhihuxjjItem 
  3. from scrapy import Spider,Request 
  4.  
  5. class ZhihuxjjSpider(Spider): 
  6.     name='zhihuxjj' #scrapy用于区别其他spider的名字,具有唯一性。 
  7.     allowed_domains = ["www.zhihu.com"] #爬取范围 
  8.     start_urls = ["https://www.zhihu.com/"
  9.     start_user = "jixin" 
  10.     followees_url = 'https://www.zhihu.com/api/v4/members/{user}/followees?include=data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics&offset={offset}&limit=20' #关注列表网址 
  11.     user_url = 'https://www.zhihu.com/api/v4/members/{user}?include=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' #个人信息链接 
  12.     def start_requests(self): 
  13.         yield Request(self.followees_url.format(user=self.start_user,offset=0),callback=self.parse_fo) #回调种子用户的关注列表 
  14.         yield Request(self.user_url.format(user=self.start_user,include = self.user_include),callback=self.parse_user) #回调种子用户的个人信息 
  15.  
  16.     def parse_user(self, response): 
  17.         result = json.loads(response.text) 
  18.         print(result) 
  19.         item = ZhihuxjjItem() 
  20.         item['user_name'] = result['name'
  21.         item['sex'] = result['gender']  # gender为1是男,0是女,-1是未设置 
  22.         item['user_sign'] = result['headline'
  23.         item['user_avatar'] = result['avatar_url_template'].format(size='xl'
  24.         item['user_url'] = 'https://www.zhihu.com/people/' + result['url_token'
  25.         if len(result['locations']): 
  26.             item['user_add'] = result['locations'][0]['name'
  27.         else
  28.             item['user_add'] = '' 
  29.         yield item 
  30.  
  31.     def parse_fo(self, response): 
  32.         results = json.loads(response.text) 
  33.         for result in results['data']: 
  34.             yield Request(self.user_url.format(user=result['url_token'], include=self.user_include),callback=self.parse_user) 
  35.             yield Request(self.followees_url.format(user=result['url_token'], offset=0),callback=self.parse_fo)  # 对关注者的关注者进行遍历,爬取深度depth+=1 
  36.         if results['paging']['is_end'is False: #关注列表页是否为尾页 
  37.             next_url = results['paging']['next'].replace('http','https'
  38.             yield Request(next_url,callback=self.parse_fo) 
  39.         else
  40.             pass  

这里需要划重点的是 yield 的用法,以及 item['name'],将爬取结果赋值给 item,就是告诉系统,这是我们要选的菜…啊呸…要爬的目标数据。

设置其他信息

在 items.py 文件中,按照 spider 中设置的目标数据 item,添加对应的代码。

  1. import scrapy 
  2.  
  3. class ZhihuxjjItem(scrapy.Item): 
  4.    # define the fields for your item here like
  5.    # name = scrapy.Field() 
  6.    user_name = scrapy.Field() 
  7.    sex  = scrapy.Field() 
  8.    user_sign = scrapy.Field() 
  9.    user_url = scrapy.Field() 
  10.    user_avatar = scrapy.Field() 
  11.    user_add = scrapy.Field() 
  12.    pass

在 pipeline.py 中添加存入数据库的代码:

  1. import pymysql 
  2.  
  3. def dbHandle(): 
  4.     conn = pymysql.connect
  5.         host='localhost'
  6.         user='root'
  7.         passwd='数据库密码'
  8.         charset='utf8'
  9.         use_unicode=False 
  10.     ) 
  11.     return conn 
  12.  
  13. class ZhihuxjjPipeline(object): 
  14.     def process_item(self, item, spider): 
  15.         dbObject = dbHandle()  # 写入数据库 
  16.         cursor = dbObject.cursor() 
  17.         sql = "insert into xiaojiejie.zhihu(user_name,sex,user_sign,user_avatar,user_url,user_add) values(%s,%s,%s,%s,%s,%s)" 
  18.         param = (item['user_name'],item['sex'],item['user_sign'],item['user_avatar'],item['user_url'],item['user_add']) 
  19.         try: 
  20.             cursor.execute(sql, param) 
  21.             dbObject.commit() 
  22.         except Exception as e: 
  23.             print(e) 
  24.             dbObject.rollback() 
  25.         return item  

因为使用了 pipeline.py,所以我们还需要在 setting.py 文件中,将 ITEM_PIPELINE 注释解除,这里起到连接两个文件的作用。

到这里,基本就都设置好了,程序基本上就可以跑了。

不过因为 scrapy 是遵循robots.txt法则的,所以让我们来观察一下知乎的法则:https://www.zhihu.com/robots.txt

emmmmmmm,看完法则了吗,很好,然后我们在setting.py中,将ROBOTSTXT_OBEY 改成 False。

好像…还忘了点什么,对了,忘记设置 headers 了。

通用的设置 headers 的方法同样是在 setting.py 文件中,将 DEFAULTREQUESTHEADERS 的代码注释状态取消,并设置模拟浏览器头。

知乎是要模拟登录的,如果使用游客方式登录,就需要添加 authorization,至于这个 authorization 是如何获取的,我,就,不,告,诉,你......

  1. DEFAULT_REQUEST_HEADERS = { 
  2.     "User-Agent""Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
  3.     'authorization''oauth c3cef7c66a1843f8b3a9e6a1e3160e20' 

为了减少服务器压力&防止被封,解除 DOWNLOAD_DELAY 注释状态,这时设置下载延迟,将下载延迟设为 3(robots 法则里要求是 10,但 10 实在太慢了_(:зゝ∠)知乎的程序员小哥哥看不见这句话看不见这句话…

写到这里你会发现,很多我们需要进行的操作,scrapy 都已经写好了,只需要将注释去掉,再稍作修改,就可以实现功能了。scrapy 框架还有很多功能,可以阅读官方文档了解。

运行scrapy文件

写好 scrapy 程序后,我们可以在终端输入。

  1. $ scrapy crawl zhihuxjj 

运行文件,但也可以在文件夹中添加 main.py,并添加以下代码。

然后直接用 pycharm 运行 main.py 文件即可,然后我们就可以愉快的爬知乎用户啦~(小姐姐我来啦~)

查找小姐姐

经过了 X 天的运行,_(:зゝ∠)_爬到了 7w 条用户数据,爬取深度 5。(这爬取速度让我觉得有必要上分布式爬虫了…这个改天再唠)

有了数据我们就可以选择,同城市的用户进行研究了……先国际惯例的分析一下数据。

知乎用户性别分布

在 7w 用户中,明显男性超过了半数,标明自己是女性的用户只占了 30% 左右,还有一部分没有注明性别,优质的小姐姐还是稀缺资源呀~

再来看看小姐姐们都在哪个城市。(从 7w 用户中筛选出性别女且地址信息不为空的用户)

知乎女性用户位置分布

看来小姐姐们还是集中在北上广深杭的,所以想发现优质小姐姐的男孩纸们还是要向一线看齐啊,当然也不排除在二三线的小姐姐们没有标记出自己的地理位置。

emmmmm……这次的分析,就到此为止,你们可以去撩小姐姐们了。

研究小姐姐

意不意外?开不开心?这里还有一章。正所谓,授之以鱼,不如授之以渔;撒了心灵鸡汤,还得加一只心灵鸡腿;找到了小姐姐,我们还要了解小姐姐…………

让我再举个栗子~来研究一个小姐姐。(知乎名:动次,已获取小姐姐授权作为示例。)

知乎用户:动次

让我们来爬一下她的动态,chrome 右键检查翻 network 这些套路我就不说了,直接讲研究目标。

  • 赞同的答案和文章(了解小姐姐的兴趣点) 
  • 发布的答案和文章(了解小姐姐的世界观、人生观、价值观) 
  • 关注的问题和收藏夹(了解小姐姐需求) 
  • 提出的问题(了解小姐姐的疑惑)

代码也不贴了,会放在 GitHub 的,来看一下输出。

研究动次的结果输出

因为你乎风格,所以对停用词进行了一些加工,添加了“如何”、“看待”、“体验”等词语,得到了小姐姐回答问题的词频。小姐姐的回答里出现了喜欢、朋友、爷爷等词语。

动次回答问题的词频

还有!!在关注、赞同和输出中,都有的词(?ω?)。(是不是可以靠美味捕获小姐姐呢……

再来一张刘看山背景的,答题词云。

动次的回答问题词云

后记

本文涉及项目会持续更新,会将研究对象拓展至各平台,并进行后续优化,有兴趣的盆友可以关注 GitHub 项目。

结尾引用知乎用户陈壮壮在《当你追求女生时,你们聊些什么?》的回答。(因为穷我就不申请转载了你们自己点进去看吧(?﹏?),你们只要知道我有颗带你们撩妹的心就行了)

安装scrapy:

http://scrapy-chs.readthedocs.io/zh_CN/1.0/intro/install.html

pycharm编译器:

http://www.jianshu.com/p/23e52f7b8ec7

回调函数:

https://www.zhihu.com/question/19801131

yield的用法:

https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/

robots.txt法则:

https://baike.baidu.com/item/robots%E5%8D%8F%E8%AE%AE/2483797?fr=aladdin&fromid=9518761&fromtitle=robots.txt

scrapy官方文档:

http://scrapy-chs.readthedocs.io/zh_CN/1.0/index.html

动次:https://www.zhihu.com/people/wang-dong-ci/activities

GitHub项目:

https://github.com/otakurice/danshengoustyle

当你追求女生时,你们聊些什么?

https://www.zhihu.com/question/25955712/answer/37668446

【编辑推荐】

  1. Python转JavaScript编译器,天了噜!还能转代码,到底怎么做到的
  2. 2017数据科学与机器学习行业现状调查:Python是最受欢迎的语言
  3. 谷歌推出Tangent开源库,在Python源代码上做自动微分
  4. 支撑百万用户同时在线的高并发直播弹幕系统是如何炼成的?
  5. 号称世界最快句法分析器,Python高级自然语言处理库spaCy!
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

精通ASP+XML+CSS网络开发混合编程

《精通ASP+XML+CSS网络开发混合编程》介绍当前网络开发的主流平台与技术之一的ASP+CSS+XML的知识与应用,全书各知识点均配以实例,按照基础...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊
× Phthon,最神奇好玩的编程语言