对于QQ空间的数据一直来是垂涎不已,老早就想偷过来研究研究,这几天闲下来便开始动手。。。

整个程序的流程为:登录-->获取cookie-->获取所有的好友qq_number-->根据所有的好友qq遍历他们的说说-->get所有好友的说说数据

程序跑了20多分钟就跑完了,,共282好友,,跑了60000+说说

有些个人隐私我抹掉了。。甭介意。嘿嘿

1.登录-->获取cookie

打开http://i.qq.com/,如下图

但大多数时候是这样的

我们这里使用账号密码登录,为了方便使用selenium自动化神器(关于selenium的用法可以参考https://my.oschina.net/u/3264690/blog/899229,这里不做过多阐述)

QQ账号,QQ密码存储在userinfo.ini文件中,然后用configparser将其读取出来

读取的代码如下

configparser是一个读取配置文件的库,这里读取的格式为get('[配置文件中括号里的值]',‘相对应的key值’)

  1. import configparser
  2. config = configparser.ConfigParser(allow_no_value=False)
  3. config.read('userinfo.ini')
  4. self.__username =config.get('qq_info','qq_number')
  5. self.__password=config.get('qq_info','qq_password')

用户信息读取出来后就可以登录了

有些盆友用selenium的时候,可能会发现有些元素定位不到,这是因为有些网页套了一个iFrame

selenium根据id定位到该iframe

  1. self.web.switch_to_frame('login_frame')

自动登录且获取cookie的代码

  1. def login(self):
  2. self.web.switch_to_frame('login_frame')
  3. log=self.web.find_element_by_id("switcher_plogin")
  4. log.click()
  5. time.sleep(1)
  6. username=self.web.find_element_by_id('u')
  7. username.send_keys(self.__username)
  8. ps=self.web.find_element_by_id('p')
  9. ps.send_keys(self.__password)
  10. btn=self.web.find_element_by_id('login_button')
  11. time.sleep(1)
  12. btn.click()
  13. time.sleep(2)
  14. self.web.get('https://user.qzone.qq.com/{}'.format(self.__username))
  15. cookie=''
  16. for elem in self.web.get_cookies():
  17. cookie+=elem["name"]+"="+ elem["value"]+";"
  18. self.cookies=cookie
  19. self.get_g_tk()
  20. self.headers['Cookie']=self.cookies
  21. self.web.quit()

2.获取所有好友的QQ_number

研究好久后发现在QQ空间主页中权限设置页面中,点击仅限QQ好友,会有下面这样的页面出来

按F12后研究js文件发现有这样一个文件

这个js文件里有好友的qq_number

于是请求这个文件得到qq_number

  1. def get_frends_url(self):
  2. url='https://h5.qzone.qq.com/proxy/domain/base.qzone.qq.com/cgi-bin/right/get_entryuinlist.cgi?'
  3. params = {"uin": self.__username,
  4. "fupdate": 1,
  5. "action": 1,
  6. "g_tk": self.g_tk}
  7. url = url + parse.urlencode(params)
  8. return url
  9. def get_frends_num(self):
  10. t=True
  11. offset=0
  12. url=self.get_frends_url()
  13. while(t):
  14. url_=url+'&offset='+str(offset)
  15. page=self.req.get(url=url_,headers=self.headers)
  16. if "\"uinlist\":[]" in page.text:
  17. t=False
  18. else:
  19. if not os.path.exists("./frends/"):
  20. os.mkdir("frends/")
  21. with open('./frends/'+str(offset)+'.json','w',encoding='utf-8') as w:
  22. w.write(page.text)
  23. offset += 50

这里有一个函数self.g_tk()它返回一个加密的p_skey , 在这个js文件中qzfl_v8_2.1.61.js,有这样一段代码

  1. QZFL.pluginsDefine.getACSRFToken = function(url) {
  2. url = QZFL.util.URI(url);
  3. var skey;
  4. if (url) {
  5. if (url.host && url.host.indexOf("qzone.qq.com") > 0) {
  6. try {
  7. skey = parent.QZFL.cookie.get("p_skey");
  8. } catch (err) {
  9. skey = QZFL.cookie.get("p_skey");
  10. }
  11. } else {
  12. if (url.host && url.host.indexOf("qq.com") > 0) {
  13. skey = QZFL.cookie.get("skey");
  14. }
  15. }
  16. }
  17. if (!skey) {
  18. skey = QZFL.cookie.get("p_skey") || (QZFL.cookie.get("skey") || (QZFL.cookie.get("rv2") || ""));
  19. }
  20. return arguments.callee._DJB(skey);
  21. };
  22. QZFL.pluginsDefine.getACSRFToken._DJB = function(str) {
  23. var hash = 5381;
  24. for (var i = 0, len = str.length;i < len;++i) {
  25. hash += (hash << 5) + str.charCodeAt(i);
  26. }
  27. return hash & 2147483647;
  28. };

把它写成python版的如下

  1. def get_g_tk(self):
  2. p_skey = self.cookies[self.cookies.find('p_skey=')+7: self.cookies.find(';', self.cookies.find('p_skey='))]
  3. h=5381
  4. for i in p_skey:
  5. h+=(h<<5)+ord(i)
  6. print('g_tk',h&2147483647)
  7. self.g_tk=h&2147483647

因为将好友信息存储为json文件,因此需要解析文件信息

  1. #coding:utf-8
  2. import json
  3. import os
  4. def get_Frends_list():
  5. k = 0
  6. file_list=[i for i in os.listdir('./frends/') if i.endswith('json')]
  7. frends_list=[]
  8. for f in file_list:
  9. with open('./frends/{}'.format(f),'r',encoding='utf-8') as w:
  10. data=w.read()[95:-5]
  11. js=json.loads(data)
  12. # print(js)
  13. for i in js:
  14. k+=1
  15. frends_list.append(i)
  16. return frends_list
  17. frends_list=get_Frends_list()
  18. print(frends_list)

3.获取所有好友说说

与之前类似,进入好友的说说主页后发现也有这样一个js文件将所有说说以json形式显示出来

类似的,写了获取说说的代码(经过测试,参数中的num最好写20,否则会出现未知的结果。。。)

  1. def get_mood_url(self):
  2. url='https://h5.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6?'
  3. params = {
  4. "sort":0,
  5. "start":0,
  6. "num":20,
  7. "cgi_host": "http://taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6",
  8. "replynum":100,
  9. "callback":"_preloadCallback",
  10. "code_version":1,
  11. "inCharset": "utf-8",
  12. "outCharset": "utf-8",
  13. "notice": 0,
  14. "format":"jsonp",
  15. "need_private_comment":1,
  16. "g_tk": self.g_tk
  17. }
  18. url = url + parse.urlencode(params)
  19. return url
  20. def get_mood_detail(self):
  21. from getFrends import frends_list
  22. url = self.get_mood_url()
  23. for u in frends_list[245:]:
  24. t = True
  25. QQ_number=u['data']
  26. url_ = url + '&uin=' + str(QQ_number)
  27. pos = 0
  28. while (t):
  29. url__ = url_ + '&pos=' + str(pos)
  30. mood_detail = self.req.get(url=url__, headers=self.headers)
  31. print(QQ_number,u['label'],pos)
  32. if "\"msglist\":null" in mood_detail.text or "\"message\":\"对不起,主人设置了保密,您没有权限查看\"" in mood_detail.text:
  33. t = False
  34. else:
  35. if not os.path.exists("./mood_detail/"):
  36. os.mkdir("mood_detail/")
  37. if not os.path.exists("./mood_detail/"+u['label']):
  38. os.mkdir("mood_detail/"+u['label'])
  39. with open('./mood_detail/'+u['label']+"/" +str(QQ_number)+"_"+ str(pos) + '.json', 'w',encoding='utf-8') as w:
  40. w.write(mood_detail.text)
  41. pos += 20
  42. time.sleep(2)

将需要的说说数据存入数据库

  1. #存入数据库
  2. def dataToMysql():
  3. con=pymysql.connect(
  4. host='127.0.0.1',
  5. user='root',
  6. password="×××",
  7. database='qq_z',
  8. port=3306,
  9. )
  10. cur=con.cursor()
  11. sql="insert into info (qq_number,created_time,content,commentlist,source_name,cmtnum,name) values ({},{},{},{},{},{},{});"
  12. d=[i for i in os.listdir('mood_detail') if not i.endswith('.xls')]
  13. for ii in d:
  14. fl=[i for i in os.listdir('mood_detail/'+ii) if i.endswith('.json')]
  15. print('mood_detail/'+ii)
  16. k=1
  17. for i in fl:
  18. with open('mood_detail/'+ii+"/"+i,'r',encoding='latin-1') as w:
  19. s=w.read()[17:-2]
  20. js=json.loads(s)
  21. print(i)
  22. for s in js['msglist']:
  23. m=-1
  24. if not s['commentlist']:
  25. s['commentlist']=list()
  26. cur.execute(sql.format(int(i[:i.find('_')]),s['created_time'],str(s['content']),str([(x['content'],x['createTime2'],x['name'],x['uin']) for x in list(s['commentlist'])]),str(s['source_name']),int(s['cmtnum']),str(s['name'])))
  27. k+=1
  28. con.commit()
  29. con.close()

将需要的说说数据存入Excel

  1. def dataToExcel():
  2. d=[i for i in os.listdir('mood_detail') if not i.endswith('.xls')]
  3. for ii in d:
  4. wb=xlwt.Workbook()
  5. sheet=wb.add_sheet('sheet1',cell_overwrite_ok=True)
  6. sheet.write(0,0,'content')
  7. sheet.write(0,1,'createTime')
  8. sheet.write(0,2,'commentlist')
  9. sheet.write(0,3,'source_name')
  10. sheet.write(0,4,'cmtnum')
  11. fl=[i for i in os.listdir('mood_detail/'+ii) if i.endswith('.json')]
  12. print('mood_detail/'+ii)
  13. k=1
  14. for i in fl:
  15. with open('mood_detail/'+ii+"/"+i,'r',encoding='latin-1') as w:
  16. s=w.read()[17:-2]
  17. js=json.loads(s)
  18. print(i)
  19. for s in js['msglist']:
  20. m=-1
  21. sheet.write(k,m+1,str(s['content']))
  22. sheet.write(k,m+2,str(s['createTime']))
  23. if not s['commentlist']:
  24. s['commentlist']=list()
  25. sheet.write(k,m+3,str([(x['content'],x['createTime2'],x['name'],x['uin']) for x in list(s['commentlist'])]))
  26. sheet.write(k,m+4,str(s['source_name']))
  27. sheet.write(k,m+5,str(s['cmtnum']))
  28. k+=1
  29. if not os.path.exists('mood_detail/Excel/'):
  30. os.mkdir('mood_detail/Excel/')
  31. try:
  32. wb.save('mood_detail/Excel/'+ii+'.xls')
  33. except Exception:
  34. print("error")

4.分析数据

24小时发布的说说数

大家在中午和晚上发布的说说比较多,凌晨比较少

说说最多排行top20

说说最少排行top20

果然,,闷骚的人发的说说比较多。。。哈哈哈

从2000年到2018年,说说分布如下

看来我的朋友们年轻的时候蛮闷骚,,随着年纪增大,,说说越来越少。。

感谢https://zhuanlan.zhihu.com/p/24656161给我的提示。。。少走了许多弯路

数据抓取速度贼快,,20分钟抓取了我所有好友(282+)60000+说说。。

项目已传到

码云 : https://git.oschina.net/nanxun/QQ_zone.git

github : https://github.com/nanxung/QQ_zone.git

朋友们,觉得有用来个star噢。。蟹蟹。。。

抓取60000+QQ空间说说做一次数据分析的更多相关文章

  1. C#使用Selenium实现QQ空间数据抓取 登录QQ空间

    经@吃西瓜的星星提醒 首先我们介绍下Selenium Selenium也是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE.Mo ...

  2. 360浏览器7.1抓触屏QQ空间包

  3. 通过Scrapy抓取QQ空间

    毕业设计题目就是用Scrapy抓取QQ空间的数据,最近毕业设计弄完了,来总结以下: 首先是模拟登录的问题: 由于Tencent对模拟登录比较讨厌,各个防备,而本人能力有限,所以做的最简单的,手动登录后 ...

  4. python爬虫(一)_爬虫原理和数据抓取

    本篇将开始介绍Python原理,更多内容请参考:Python学习指南 为什么要做爬虫 著名的革命家.思想家.政治家.战略家.社会改革的主要领导人物马云曾经在2015年提到由IT转到DT,何谓DT,DT ...

  5. arpspoof+driftnet+ ARP欺骗简单图片抓取

    arpspoof+driftnet+ ARP欺骗简单图片抓取 driftnet是一款简单而使用的图片捕获工具,可以很方便的在网络数据包中抓取图片.该工具可以实时和离线捕获指定数据包中是图片 环境 受害 ...

  6. [原创.数据可视化系列之十二]使用 nodejs通过async await建立同步数据抓取

    做数据分析和可视化工作,最重要的一点就是数据抓取工作,之前使用Java和python都做过简单的数据抓取,感觉用的很不顺手. 后来用nodejs发现非常不错,通过js就可以进行数据抓取工作,类似jqu ...

  7. Scrapy爬虫框架教程(四)-- 抓取AJAX异步加载网页

    欢迎关注博主主页,学习python视频资源,还有大量免费python经典文章 sklearn实战-乳腺癌细胞数据挖掘 https://study.163.com/course/introduction ...

  8. shell脚本抓取网页信息

    利用shell脚本分析网站数据 # define url time=$(date +%F) mtime=$(date +%T) file=/abc/shell/abc/abc_$time.log ht ...

  9. QQ空间认证之数据篇

    最近,我们发现可以利用开通企鹅媒体平台的方式开通QQ公众号从而绑定我们的QQ号,这样我们所绑定的QQ号就成了认证空间了. 虽说这样很快捷的就认证了我们的QQ空间,但是,起有利也有弊.任何事情都不是十全 ...

随机推荐

  1. PO/VO/POJO/BO/VO图解

  2. [0] WCF开发下,提示HTTP 无法注册 URL 进程不具有此命名空间的访问权限

    Visual Studio以管理员的身份运行就可以了.

  3. 高性能队列Disruptor系列2--浅析Disruptor

    1. Disruptor简单介绍 Disruptor是一个由LMAX开源的Java并发框架.LMAX是一种新型零售金融交易平台,这个系统是建立在 JVM 平台上,核心是一个业务逻辑处理器,它能够在一个 ...

  4. ionic中点击图片看大图的实现

    在页面上显示了几张图片后,因为是手机端,图片会有点小的感觉,就想着怎么样才能让用户点击小图片看到大图呢,项目中ionic结合angularjs实现了这个功能 1.首先是三张小图上应添加一个函数,当点击 ...

  5. 用 Google 挖掘赚钱思路

    为程序员,如果学了一堆技术却没有用武之地,实在可惜,如何把自己积累的技术利用起来?通俗一点,程序员有哪些赚钱的门路? 比较常见的一种方式是接私活,不过私活的复杂度不一,沟通成本会非常高,另一方面,私活 ...

  6. PHP设计模式:抽象工厂

    示例代码详见https://github.com/52fhy/design_patterns 抽象工厂 抽象工厂(Abstract Factory)是应对产品族概念的.比如说,每个汽车公司可能要同时生 ...

  7. markdown基础

    介绍: markdown是一种可以使用普通文本编译器编写的标记语言,通过简单的标记语法,它可以使普通文本具有一定的格式.说的简单一点,markdown其实就是一种简单的文本,与普通的文本文件(txt文 ...

  8. 用Html5/CSS3做Winform,一步一步教你搭建CefSharp开发环境(附JavaScript异步调用C#例子,及全部源代码)上

    本文为鸡毛巾原创,原文地址:http://www.cnblogs.com/jimaojin/p/7077131.html,转载请注明 CefSharp说白了就是Chromium浏览器的嵌入式核心,我们 ...

  9. sqlserver isnull判断

    --在新增或编辑的时候设置默认值或加isnull判断 Sql isnull函数 ISNULL(columName, 0)<>35 或 ISNULL(columName, '')<&g ...

  10. [leetcode-557-Reverse Words in a String III]

    Given a string, you need to reverse the order of characters in each word within a sentence whilestil ...