刚学了一点爬虫的时候,就想着去爬一下空间说说,后来在打印源码的那一步就卡住了,没有一点点防备,也没有一丝顾虑,我就这样被他拒之门外。毫无疑问这个想法瞬间就被扼杀了,完全不知道打印出来的源码为什么一点空间的内容都没有,到了现在才知道,他的反爬虫不是一般的复杂(这里超级想吐槽一下QQ空间,空间里的干货不多,那个反爬虫做的真的是好,完全无语了)。这里还是得感谢一下网上的大佬交怎么去解决空间中g_tk的加密问题,不然的话这次又要覆没在这里了。
开发环境 :win10 + sublime。下面是这次用到的包 :
#这是本次爬虫所需要用到的包
#用来延时,以便加载所有的网页元素
import time
#自动化工具,以前写过这里就不讲了
from selenium import webdriver
#正则,用来匹配内容
import re
import requests
#cookiejar是用来处理cookie的
from http import cookiejar
其他的包不用过多解释,这说一下cookiejar,cookiejar模块的主要作用是提供可存储的cookie对象,可以与urllib模块配合使用来访问网络资源。我们可以利用cookiejar类创建的对象来捕获cookie,并在后续链接请求时重新发送,可以实现模拟登陆的功能。这里采用的是自动登陆空间,selenium的功能也是很强大了,自动登陆的内容就不讲了,若有需要,可以在下面的博客看解释 :
https://blog.csdn.net/ydydyd00/article/details/80882183
这里就直接贴上自动登陆空间的代码 :
def login():
browser = webdriver.Chrome()
browser.get('https://i.qq.com/')
browser.maximize_window()
time.sleep(2)
browser.switch_to.frame("login_frame")
browser.find_element_by_id('switcher_plogin').click()
browser.find_element_by_id('u').clear()
browser.find_element_by_id("u").send_keys("你的QQ账号")
browser.find_element_by_id('p').clear()
browser.find_element_by_id("p").send_keys("你的QQ密码")
browser.find_element_by_id("login_button").click()
time.sleep(2)
print("空间登陆成功")
browser.switch_to.default_content()
return browser
上述代码利用你的QQ自动登陆了空间,接下来就是要将登陆后的cookie获取,然后通过cookie来构造一个session。这里解释一下cookie和session。cookie机制采用的是客户端保持状态的方案,例如你在浏览器登陆过某一网站,浏览器就会保存你登陆这个网站后的cookie,在你下一次打开这个网站时,浏览器就会利用cookie直接显示登陆后的页面,session机制采用的是在服务器端保持状态的方案,例如当某个客户端请求创建一个session时,服务器首先检查这个客户端的请求里是否包含一个session标识,如果包含,则说明以前已经为此客户端创建过session,服务器会按照这个标识检索对应的session,否则会为客户端创建一个session,并生成一个与session相关联的标识(即session id)。下面是根据cookies来构建session :
def back_session(browser):
#创建一个session对象
my_session = requests.session()
#获取网页的cookie
cooikes = browser.get_cookies()
#创建一个空的cookie字典
cookie = {}
#将登陆后网页的cookies以字典的形式保存
for elem in cooikes:
cookie[elem['name']] = elem['value']
headers = {
'host': 'h5.qzone.qq.com',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'accept': '*/*',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36'
}
#将字典转为cookiejar,这样就可以将cookie赋给session
c = requests.utils.cookiejar_from_dict(
cookie, cookiejar=None, overwrite=True)
my_session.headers = headers
#将cookie赋给session
my_session.cookies.update(c)
return my_session
在构造好session之后就可以通过session直接访问登陆后的网页了。
因为要爬取好友的说说,所以这里先来获取全部好友的QQ,以便后续遍历。这里先来观察获取好友QQ的网站,网址如下 :
因为他是在太长了,所以我把他截成几段来截图,这里需要注意的两个参数是g_tk和qzonetoken,qzonetoken还好,这个可以直接从链接后中获取,而g_tk就很牛X了,他是一个经过加密生成的随机串(你会发现不同时候打开的值是不同的),这里就先采用大佬们的方法解决这个加密问题(通过cookie来获取加密后的g_tk,具体原理也不是很懂) :
def get_g_tk(cookie):
hashes = 5381
for letter in cookie['p_skey']:
hashes += (hashes << 5) + ord(letter)
return hashes & 0x7fffffff
有了获取g_tk的方法后,就来获取所有好友的QQ :
def get_qq(my_session, g_tk, qzonetoken):
url = "https://user.qzone.qq.com/proxy/domain/r.qzone.qq.com/cgi-bin/tfriend/friend_ship_manager.cgi?uin=1837074495&do=1&rd=0.743191121678668&fupdate=1&clean=1&g_tk=" + \
str(g_tk) + "&qzonetoken=" + qzonetoken
#通过sesion来登陆网页
res = my_session.get(url)
#通过正则表达式来找到存储好友QQ的地方
friendlist = re.compile('"uin":(.*?),').findall(res.text)
time.sleep(2)
return friendlist
这样就获取了所有好友的QQ,如果要获取全部好友的说说,只需要获取一个好友的说说,然后迭代friendlist即可,下面来通过登陆后的页面来获取好友的说说 :
def get_message(my_session, qq, g_tk, qzonetoken):
#储存好友说说的网址
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)
res = my_session.get(url)
# 说说总数
num = re.compile('"total":(.*?),').findall(res.text)[0]
content_list = re.compile(
'"certified".*?"conlist":(.*?),', re.S).findall(res.text)
if int(num) % 20 == 0:
page = int(num) / 20
else:
page = int(num) / 20 + 1
for i in range(0, int(page)):
pos = i * 20
#这里采用翻页的方式来爬取好友的说说,pos是好友说说的页码
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
res = my_session.get(url)
content_list = re.compile(
'"certified".*?"conlist":(.*?),', re.S).findall(res.text)
time_list = re.compile(
'"certified".*?"createTime":"(.*?)"', re.S).findall(res.text)
for c, t in zip(content_list, time_list):
#删掉说说中的一下其他符号
c = c.replace('[{"con":', '')
#将说说的时间与内容写入txt文件
with open("qq_text.txt", 'a', encoding='utf-8') as f:
f.write(t)
f.writelines(c)
f.write('\n')
将获取一个好友的说说封装成函数之后就可以在获取其他好友说说的时候直接调用,最后加一个主函数就可以实现对空间说说的爬取。附上全部代码 ,有兴趣的可以将代码跑一遍试一试。
import time
from selenium import webdriver
import re
import requests
from http import cookiejar
def get_g_tk(cookie):
hashes = 5381
for letter in cookie['p_skey']:
hashes += (hashes << 5) + ord(letter)
return hashes & 0x7fffffff
def login():
browser = webdriver.Chrome()
browser.get('https://i.qq.com/')
browser.maximize_window()
time.sleep(2)
browser.switch_to.frame("login_frame")
browser.find_element_by_id('switcher_plogin').click()
browser.find_element_by_id('u').clear()
browser.find_element_by_id("u").send_keys("你的QQ账号")
browser.find_element_by_id('p').clear()
browser.find_element_by_id("p").send_keys("你的QQ密码")
browser.find_element_by_id("login_button").click()
time.sleep(2)
print("空间登陆成功")
browser.switch_to.default_content()
return browser
def back_session(browser):
my_session = requests.session()
cooikes = browser.get_cookies()
cookie = {}
for elem in cooikes:
cookie[elem['name']] = elem['value']
headers = {
'host': 'h5.qzone.qq.com',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'accept': '*/*',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36'
}
c = requests.utils.cookiejar_from_dict(
cookie, cookiejar=None, overwrite=True)
my_session.headers = headers
my_session.cookies.update(c)
return my_session
def get_qq(my_session, g_tk, qzonetoken):
url = "https://user.qzone.qq.com/proxy/domain/r.qzone.qq.com/cgi-bin/tfriend/friend_ship_manager.cgi?uin=1837074495&do=1&rd=0.743191121678668&fupdate=1&clean=1&g_tk=" + \
str(g_tk) + "&qzonetoken=" + qzonetoken
res = my_session.get(url)
friendlist = re.compile('"uin":(.*?),').findall(res.text)
time.sleep(2)
return friendlist
def get_message(my_session, qq, g_tk, qzonetoken):
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)
res = my_session.get(url)
# 说说总数
num = re.compile('"total":(.*?),').findall(res.text)[0]
content_list = re.compile(
'"certified".*?"conlist":(.*?),', re.S).findall(res.text)
if int(num) % 20 == 0:
page = int(num) / 20
else:
page = int(num) / 20 + 1
for i in range(0, int(page)):
pos = i * 20
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
res = my_session.get(url)
content_list = re.compile(
'"certified".*?"conlist":(.*?),', re.S).findall(res.text)
time_list = re.compile(
'"certified".*?"createTime":"(.*?)"', re.S).findall(res.text)
for c, t in zip(content_list, time_list):
c = c.replace('[{"con":', '')
with open("qq_text.txt", 'a', encoding='utf-8') as f:
f.write(t)
f.writelines(c)
f.write('\n')
def main():
driver = login()
html = driver.page_source
xpat = r'window\.g_qzonetoken = \(function\(\)\{ try\{return (.*?);\} catch\(e\)'
qzonetoken = re.compile(xpat).findall(html)[0]
cookies = driver.get_cookies()
cookie = {}
for elem in cookies:
cookie[elem['name']] = elem['value']
g_tk = get_g_tk(cookie)
my_session = back_session(driver)
driver.close()
friendlist = get_qq(my_session, g_tk, qzonetoken)
# for friend in friendlist:
# print(friend)
# print(len(friendlist))
for i in range(0, len(friendlist)):
get_message(my_session, friendlist[i], g_tk, qzonetoken)
time.sleep(8)
if __name__ == '__main__':
main()