
需要的库

需求分析及源码
分析
QQ空间是需要登陆才能爬取的,所以说首先要做的事进行登陆,这里有两种思路进行模拟登陆:
(1)、用selenium+Chrome 进行自动化检测登陆,接着用扫码或者账号自动输入都是可以的,然后获取cookies,再传入requests.Session().get()对要爬取好友的空间进行爬取。
(2)、用opener 和 cookielib 进行对网站cookie的获取,然后再传入requests.Session().get()
我在这里使用的是第一种方法。
Cookies的使用详情在这里: http://cuiqingcai.com/968.html
Session的使用详情在这里:http://www.cnblogs.com/whatbeg/p/5320666.html
2、当我们登陆上的时候进行抓包(炒鸡重要),然后在开发者工具里,找到这个文件,打开之后发现这就是我们想要的数据,所以......我们想要抓取的文件是找到了,但是对应的请求头呢?

在这里的headers,点开后发现,这里有个Request URL,这个就是我们要的URL

再点开Query发现,URL的数据这里都有,但是仔细分析后发现。。。。这里有些东西我们不知道是怎么来的,就是这里的g_tk和qzone_token,不是那么很规则,这个时候,我们多点开几个页面试试,我们会发现,只有这两个参数是改变的,其余的参数都是不变的,所以我们只要解决这两个参数,我们就可以请求到这个json数据。好,接下来要解决的问题就是如何获得g_tk和qzonetoken这两个数据。

这里有个找参数的方法1、可以先在网页上对源代码进行查找,有些参数是隐藏在网页的源码里的,看存不存在,如果存在,用xpath或者bs4库进行解析(我在这里用的是正则表达式来提取),如果不存在,就要用下面的方法。
查找参数
如果网页源码里找不到,可能就是被加密了,水平实在有限,没能破解的出,所以 就百度了一下QQ空间的加密方式,在CSDN的博客上发现了这个解密的代码。

所以在这个登陆模块,我们完成的是登陆上去,获取到相应的参数,然后返回来,以备后用。好啦,参数获取完了,要获取数据了,下面这个模块就是对数据的获取。

数据获取
有了上面的获取的三个数据,那登陆到QQ空间就是一点问题都木有啦,然后创建一个会话(Session()),保持本地与服务器的连接。一个get,我们就把json格式的数据爬取下来了。

但是!!!不能一直爬取下去,所以要对爬取的内容进行一个判别是否爬取结束

如果爬取结束了,就break出循环了,然后再爬取下一个好友的动态。
数据的清洗
还没有学习数据库的内容,所以就将爬取的json文件转为字典,然后对字典进行处理保存为csv文件。用了一个xlwt的库,这个库可以建立并读写数据

然后保存为文件。


注意!!!这里一定要加入异常的处理,因为有些人的QQ空间是不开放的,所以遇到不开放的空间就会出错,为了让程序能够正常的执行,所以必须要加异常处理。
点击运行按钮,一段时间后,就可以查看你爬取的成果了,爬取了6W+的数据.....
数据处理
Python有丰富的数据处理的库,numpy pandas scipy ,由于存储为csv文件,当然电脑上也有tableau和matlab,但是对这两个软件不太熟悉,就就直接用Excel软件进行处理。
以下是我用Excel做的几张图:



源码:
import requestsfrom selenium import webdriverfrom PIL import Imageimport xlwtimport timeimport reimport jsonfrom collections import Iterableimport csvn = 1header = {'Host': 'h5.qzone.qq.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0',
'Accept': '*/*',
'Accept-Language':'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate, br',
'Referer': 'https://user.qzone.qq.com/790178228?_t_=0.22746974226377736',
'Connection':
'keep-alive'}#对表格进行处理file = xlwt.Workbook()table = file.add_sheet('sheet name', cell_overwrite_ok= True)table.write(0,1,"日期")table.write(0,2,"时间")table.write(0,3,"年份")table.write(0,4,"月份")table.write(0,5,"时间点")table.write(0,6,"图片数量")table.write(0,7,"评论数量")table.write(0,8,"手机型号")table.write(0,9,"经度")table.write(0,10,"地点")table.write(0,11,"纬度")table.write(0,12,"位置")table.write(0,13,"内容")#从邮箱里到导出的联系人,然后进行读取操作def getfriends():
csv_reader = csv.reader(open('qq.csv'))
friend = []
for row in csv_reader:
friend.append(row[3])
friend.pop(0)
friends = []
for f in friend:
f = f[:-7]
friends.append(f)
return friends# 这个函数用来解决腾讯g_tk加密算法的函数def get_g_tk(cookie):
hashes = 5381
for letter in cookie['p_skey']:
hashes += (hashes << 5) + ord(letter) # ord()是用来返回字符的ascii码
return hashes & 0x7fffffff#这个函数是用来获取cookie,g_tk,g_qzontoken这三个数据def Login_QQ():
driver = webdriver.Chrome()
start_url = "https://qzone.qq.com/"
driver.get(start_url)
time.sleep(10)
cookie = {}
for elem in driver.get_cookies():
cookie[elem['name']] = elem['value']
html = driver.page_source
g_qzonetoken = re.search(r'window\.g_qzonetoken = \(function\(\)\{ try\{return (.*?);\} catch\(e\)',html)
g_tk = get_g_tk
(cookie)
driver.quit()
print(g_qzonetoken.group(1))
return (cookie, g_tk, g_qzonetoken.group(1))def spyder_info():
s = requests.session()
friends = getfriends()
cookie, g_tk, g_qzonetoken = Login_QQ()
for qq in friends:
global n
for i in range(1000):
pos = i*20
param = {
'uin': qq,
'ftype': '0',
'sort': '0',
'pos': pos,
'num': '20',
'replynum': '100',
'g_tk': g_tk,
'callback': '_preloadCallback',
'code_version': '1',
'format': 'jsonp',
'need_private_comment': '1',
'qzonetoken': g_qzonetoken
}
respond = s.get("https://h5.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6",
params=param, headers = header,cookies=cookie)
r = re.sub("_preloadCallback", "", respond.
text)
test = r[1:-2]
Data = json.loads(test)
if not re.search('lbs', test): # 通过lbs判断此qq的说说是否爬取完毕
print('%s说说下载完成' % qq)
break
try:
for each_data in Data["msglist"]:
# 说说发表的日期1
table.write(n, 1, each_data["createTime"])
# 说说发表的时间2
table.write(n, 2, time.strftime("%H:%M:%S", time.localtime(each_data["created_time"])))
# 说说发表的年份3
table.write(n, 3, time.strftime("%Y", time.localtime(each_data["created_time"])))
# 说说发表的月份4
table.write(n, 4, time.strftime("%m", time.localtime(each_data["created_time"])))
# 说说发表的小时5
table.write(n, 5, time.strftime("%H", time.localtime(each_data[
"created_time"])))
# 统计图片的数量6,以及用的手机型号8
if "pic" in each_data:
table.write(n, 6, each_data["pictotal"])
table.write(n, 8, each_data["source_name"])
else:
table.write(n, 6, 0)
table.write(n, 8, "")
# 统计每个说说的评论数量7
if each_data["commentlist"]:
table.write(n, 7, each_data["commentlist"][-1]["tid"])
else:
table.write(n, 7, 0)
# 获取该条说说的发表位置:9,10,11,12
if "story_info" in each_data:
table.write(n, 9, each_data["story_info"]["lbs"]["pos_x"])
table.write(n, 11, each_data["story_info"]["lbs"]["name"])
table.write(n, 10, each_data["story_info"]["lbs"]["pos_y"])
table.write(n, 12,
each_data["story_info"]["lbs"]["idname"])
else:
table.write(n, 9, "")
table.write(n, 11, "")
table.write(n, 10, "")
table.write(n, 12, "")
# 获取每一条说说的内容13
if each_data["content"]:
table.write(n, 13, each_data["conlist"][0]["con"])
else:
table.write(n, 13, "")
n = n + 1
except:
print("error")
file.save("demo6.xls")#if '__name__' == '__main__':spyder_info()

