前言
随着大数据时代的到来,个人的日常生活总是会与各种各样的推送联系在一起。而这些符合你特性的推送正是对你以往的信息进行分析后得出的结论,因此爬虫变成了许多编程爱好者的利器。但学习爬虫时,一些热门的网页不断在更新反扒措施,而许多书籍和网上的文章还停留在以前的版本,本人在使用爬虫时遇到了各种各样的坑,尤其是在爬取QQ空间时,许多文章都是两年前的。因此,爬完后就想写篇博客来给正在迷茫路上的朋友一点指引。(结尾有彩蛋)
开始
软件环境:Python3.6+selenium包+火狐浏览器(selenium和火狐版本要对应)+pymysql包
准备工作就到这,先说下爬取思路:、
1.先使用selenium登录自己的QQ获取cookies
2.将cookies传给自己构造的requests,接下来就用这个构造的session
为所欲为
3.找到所有好友的QQ号
4.通过构造url对好友的信息进行爬取
~~~Show my code
第一步登录QQ空间,获取Cookies
def login():
driver=webdriver.Firefox()
#打开QQ网页
driver.get("https://qzone.qq.com/")
driver.switch_to_frame('login_frame')
driver.find_element_by_id('switcher_plogin').click()
driver.find_element_by_id('u').clear()
driver.find_element_by_id('u').send_keys('QQ账号')
driver.find_element_by_id('p').clear()
driver.find_element_by_id('p').send_keys('QQ密码')
driver.find_element_by_id('login_button').click()
time.sleep(5)
#得把Frame的定位换回来,否则可能会出错
driver.switch_to.default_content()
return driver
selenium本质其实就是模仿人做事,上面的代码也就是模仿人的动作,对于selenium不太熟悉的可以去百度一下selenium文档,里面解释的很详细,很容易懂。
第二步,成功登录后,将cookies赋给requests
#获取cookies,返回带cookies的requests
def back_session(driver):
mysession=requests.session()
cookies=driver.get_cookies()
cookie={}
#将cookies转换成字典格式
for elem in cookies:
cookie[elem['name']] = elem['value']
headers={ 'host': 'h5.qzone.qq.com',
'accept-encoding':'gzip, deflate, br',
'accept-language':'zh-CN,zh;q=0.8',
'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
'connection': 'keep-alive'
}
c = requests.utils.cookiejar_from_dict(cookie,cookiejar=None,overwrite=True)
mysession.headers=headers
mysession.cookies.update(c)
return mysession
通过上述操作,我们的工作量就已经完成了一半。是不是有点小激动
第三步,找到好友列表——
说起来QQ空间真的是一个神奇的地方,明明爬取的东西没有知乎上的问题有深度,但人家的反扒措施就是让你琢磨不透,各种文件的网址构造更是一个比一个奇葩。可能你会有点不理解,看下面你就懂了。
F12爬虫党必备按钮--然后点击图片中
你会发现形如这样的网址
复制网址在浏览器中打开,你就会看到你所有好友的信息:
然后就是对这一奇葩网址的
经过多次的测试,发现3处地方是需要重点注意的uin=需要填写你自己的QQ号,而g_tk=""(这是空间的加密算法)需要你传入cookies后生成的,你要是感兴趣的话,可以去JS里面找找看
#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
还有一个qzonetoken参数,这是在原来登录网页中获取的可以通过正则表达式构造获得
这两个参数挺重要,在后面的爬取好友的信息构造网址时,同样会用到。
成功得到这两个参数后,我们就可以构造正则表达式来获取所有好友的QQ号代码如下:
这样你就得到了所有好友的QQ号,
然后,就来到了最后一步:第四步---获取好友说说内容信息
点开随机一个好友的网页,可以看到查看他或她的说说有两种类型,一种是动态加载,另一种是按页数加载
由于动态的构造网址会复杂一点,所以我做的是另一种。
点 击
老规矩F12--
然后复制网址,打开
就是说说的内容,和创建时间。说明我们的思路没毛病,接下来就是分析网址,并构造。
多次试验后,发现有五处是需要注意的uin和hostuin需要填写好友的QQ号 ,g_tk和qzonetoken两个参数就是前面提到的。那么怎么跳到下一页信息呢POS参数就很重要了,它所存储的信息一般以20条说说为一页,也就是是说pos=0代表着第一页,pos=20代表第二页。
那么总页数怎么知道呢?在说说内容的那个网站就有说说总数。由于每一页的都会有,所以我们只用爬一页的就可以,然后换算一下。就可以得到总页数
通过上述就能对QQ好友的说说进行爬取
show my code
#对信息进行爬取
def get_spider(mysession,qq,g_tk,qzonetoken):
#这个url是存储的是好友说说的内容和时间
url='https://h5.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6?uin='+str(qq)+'&inCharset=utf-8&outCharset=utf-8&hostUin='+str(qq)+'¬ice=0&sort=0&pos=0&num=20&cgi_host=http://taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6&code_version=1&format=jsonp&need_private_comment=1&g_tk='+str(g_tk)+'&qzonetoken='+str(qzonetoken)
#内容
cpat='"certified".*?"conlist":(.*?),'
#时间
tpat='"certified".*?"createTime":"(.*?)"'
#获取总说说数,由于每个网页都会有,只爬取一次就好
shuoshuo_number_pat='"total":(.*?),'
resp=mysession.get(url)
shuoshuo_number=re.compile(shuoshuo_number_pat).findall(resp.text)[0]
content_list=re.compile(cpat,re.S).findall(resp.text)
#用来检查是否可以爬取
if len(content_list)==0 :
print("该好友说说为0,或你被禁止查看此好友的空间")
return False
#算出页数
if int(shuoshuo_number)%20==0:
page=int(shuoshuo_number)/20
else:
page=int(shuoshuo_number)/20+1
print("一共有"+str(shuoshuo_number)+"条说说")
#爬取接下几页的说说
for i in range(0,int(page)):
#和上面的网址一样,只不过改页数需要变动pos这个参数
pos=i*20
try:
url='https://h5.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6?uin='+str(qq)+'&inCharset=utf-8&outCharset=utf-8&hostUin='+str(qq)+'¬ice=0&sort=0&pos='+str(pos)+'&num=20&cgi_host=http://taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6&code_version=1&format=jsonp&need_private_comment=1&g_tk='+str(g_tk)+'&qzonetoken='+qzonetoken
resp=mysession.get(url)
content_list=re.compile(cpat,re.S).findall(resp.text)
time_list=re.compile(tpat,re.S).findall(resp.text)
print("正在爬取第"+str(i+1)+"页信息----------")
for c,t in zip(content_list,time_list):
try:
c=c.replace('[{"con":','')
in_mysql(t,c,conn)
except Exception as e:
print("有一条说说数据无法写入")
except Exception as e:
print("第"+str(i+1)+"页信息爬取失败")
最终就得到了空间所有好友的说说信息。。。
用的是单线程,不知道用多线程爬取,速度过快会不会被封IP,反正我是没遭遇过。爬取大概了20多分钟吧,一共爬取了空间好友了4W多条说说。
本着不秀不是好carry的心态,我也试了把用Python将文字变成文字云的玩法
直接代码伺候
#coding:utf-8
import matplotlib.pyplot as plt
from wordcloud import WordCloud,ImageColorGenerator,STOPWORDS
import jieba
import numpy as np
from PIL import Image
#读入背景图片
abel_mask = np.array(Image.open("输入背景图片路径"))
#读取要生成词云的文件
text_from_file_with_apath = open('输入存储文字的文件路径',encoding='gbk').read()
my_wordcloud = WordCloud(
background_color='white', # 设置背景颜色
mask = abel_mask, # 设置背景图片
max_words = 200, # 设置最大现实的字数
stopwords = STOPWORDS, # 设置停用词
width=1600,
height=800,
font_path = 'C:/Users/Windows/fonts/simkai.ttf',# 设置字体格式,如不设置显示不了中文
max_font_size = 200, # 设置字体最大值
random_state = 30, # 设置有多少种随机生成状态,即有多少种配色方案
scale=.5
).generate(text_from_file_with_apath)
# 根据图片生成词云颜色
image_colors = ImageColorGenerator(abel_mask)
# 以下代码显示图片
plt.imshow(my_wordcloud)
plt.axis("off")
plt.show()
结果就变成了这样
想着我这么辛苦,就为了这么个玩意。
悄悄是别离的笙箫;
沉默是今晚的康桥!
哎,那就处理数据吧。终于
说多了都是泪啊。。。。。。。。
(Game Over)
Github代码传送门:完整代码