1、分析列表请求
2、分析请求参数
3、分析点赞请求
4、分析点赞参数
5、编写执行代码
我们上次分析过 动态爬虫之QQ好友列表获取 今天这篇继续按照上次的思路来,如果您看过我上篇建议您直接看参数按照以前的操作就好。
首先我们还是打开这个网址 https://h5.qzone.qq.com/mqzone/index 并且登陆
打开chrome 调试工具,找到拦截请求这一块刷新一下页面(为了页面排版,我只截取了一小部分)
然而并没有发现与动态相关代码因为qzone 为了减少客户端的渲染压力提高用户体验,第一次直接在服务端进行渲染返回了
既然是服务端加载而我们又不想消耗很多性能,那只有看一下加载下一页的数据返回是否是服务器渲染 将页面拉到最底部,清空拦截的数据,然后点击加载更多。
这边就看到了qzone动态列表的这个请求
query string parameter
qzonetoken
g_tk
form data
res_type 固定 0
res_attach
refresh_type 固定2
format 固定json
attach_info
这边 qzonetoken 和 g_tk 跟上次一样,我就直接复制过来了
var qzonetoken = window.shine0callback
var g_tk = window.user.getToken()
这边只剩下两参数,仔细的你会看出两个参数其实值是一样的 百度霸屏
所以我们只需要找一个参数就可以取到动态列表(其实在登陆情况下直接 get 请求链接也是可以取到动态的)
这边我们来搜索一下 res_attach 这个参数
有没有熟悉的味道,没错又是放在静态页面中的 FrontPage 里面,所以直接拿出来(注意:在静态页面中提取出来的只能第一次使用,每次请求接口服务器都会返回新的)
var res_attach = FrontPage.data.data.attachinfo
先刷新动态页面,然后选择第一条进行点赞,查看拦截
发现 url 中必有 qzonetoken 和 g_tk 这边分析一下post的参数就好了
这边就不讲 qzonetoken 和 g_tk 了因为这两个参数上面就说过了怎么拿过来 (这边只管发说说的,不管分享的链接和其他的)
form data
opuin:* 点赞的账号 也就是你当前登录的qq 号
unikey:http://user.qzone.qq.com/\**/mood/18623a5f8da5715907360500
curkey: http://user.qzone.qq.com/\*\*\*/mood/18623a5f8da5715907360500
appid: 固定 311 http://qq.gam7.com
opr_type:固定 like
format:固定 purejson
先找一下 unikey 看下这个参数 (unikey 和 curkey 值很多时候是一样的,如果是转发的说说,这两个参数就不一样了,所以不要偷懒)
发现参数是在一个 comm.orglikekey 直接在动态列表数据里面搜索 orglikekey 字段 result.data.vFeeds.comm.orglikekey
直接搜索 curlikekey 发现是在 orglikekey的下面一条 curlikekey
先把这些参数都取过来
# 获取动态列表参数
def paresHtml(self):
qzonetoken = self.driver.execute_script('return window.shine0callback')
g_tk = self.driver.execute_script('return window.user.getToken()')
res_type = '0'
res_attach = self.driver.execute_script('')
refresh_type = '2'
attach_info = res_attach
format = 'json'
拼接参数执行返回结果 这边记住 post 请求一定要设置 XMLHttpRequest 的 withCredentials = true
def sendRequest(self, url, method, data):
cname = 'request_%d' % time.time()
self.driver.execute_script('window.%s = new XMLHttpRequest;' % cname)
self.driver.execute_script('window.%s.withCredentials=true;' % cname)
self.driver.execute_script(
'window.%s.open(\'%s\',\'%s\',false)' % (cname, method, url))
self.driver.execute_script(
'window.%s.onload = function (e){window.%s = window.%s.responseText;}' % (
cname, ('%s_result' % cname), cname))
self.driver.execute_script(
'window.%s.send("%s")' % (cname, data))
return self.driver.execute_script('return window.%s' % ('%s_result' % cname))
sctiveFeeds = 'https://h5.qzone.qq.com/webapp/json/mqzone_feeds/getActiveFeeds?qzonetoken=%s&g_tk=%s' % (
qzonetoken, g_tk)
# 执行请求,并且返回结果
result = json.loads(self.sendRequest(sctiveFeeds, 'POST',
'res_type=%s&res_attach=%s&refresh_type=%s&format=%s&attach_info=%s' % (
res_type, res_attach, refresh_type, format, attach_info)))
由于数据量庞大所以返回可能会慢一点 现在开始造点赞的参数
opuin = self.driver.execute_script('return user.getUin()')
unikey = result['data']['vFeeds'][0]['comm']['orglikekey']
curkey = result['data']['vFeeds'][0]['comm']['curlikekey']
appid = result['data']['vFeeds'][0]['comm']['appid']
opr_type = 'like'
format = 'purejson'
liked = not 1 == result['data']['vFeeds'][0]['like']['isliked']
# 是否已经点赞,如果是那就不调用
if liked:
dolike = 'https://h5.qzone.qq.com/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_dolike_app?qzonetoken=%s&g_tk=%s' % (
qzonetoken, g_tk)
result = json.loads(self.sendRequest(dolike, 'POST',
'opuin=%s&unikey=%s&curkey=%s&appid=%s&opr_type=%s&format=%s' % (
opuin, unikey, curkey, appid, opr_type, format)))
qq全自动秒点赞 但是登陆验证码需要自己输入 这边js可能会导致phantomjs内存泄漏,所以我过两个小时会把 phantomjs 关闭
import json
import os
import pickle
import platform
import time
import urllib
from _sha1 import sha1
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import DesiredCapabilities, ActionChains
class qzone_dlewares(object):
# 浏览器请求头
headers = {'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.8',
'Cache-Control': 'max-age=0',
'User-Agent': 'Mozilla/5.0 (Linux; U; Android 2.3.6; zh-cn; GT-S5660 Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MicroMessenger/4.5.255',
'Connection': 'keep-alive', }
webdriverPath = 'E:\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe'
attachinfo = False
# 初始化浏览器
def __init__(self, userName='', password='', *args, **kwargs):
self.userName = userName
self.password = password
desired_capabilities = DesiredCapabilities.PHANTOMJS.copy()
for key, value in self.headers.items():
desired_capabilities['phantomjs.page.customHeaders.{}'.format(key)] = value
# 禁止加载图片
desired_capabilities["phantomjs.page.settings.loadImages"] = False
self.driver = webdriver.PhantomJS(executable_path=self.webdriverPath, desired_capabilities=desired_capabilities)
# 设置屏幕大小
self.driver.set_window_size(414, 736)
# 开始请求并且截图
def startQzoneRequest(self):
# 开始请求qzone
self.driver.get('https://mobile.qzone.qq.com')
# 截图保存到当前项目下
self.driver.save_screenshot('screenshot.png')
# 判断是否登录了
def isLogin(self):
try:
u = self.driver.find_element_by_xpath('//*[@id="u"]')
p = self.driver.find_element_by_xpath('//*[@id="p"]')
go = self.driver.find_element_by_xpath('//*[@id="go"]')
except NoSuchElementException:
return True
return False
def loginQzone(self):
u = self.driver.find_element_by_xpath('//*[@id="u"]')
p = self.driver.find_element_by_xpath('//*[@id="p"]')
go = self.driver.find_element_by_xpath('//*[@id="go"]')
# 清理账号和密码
u.clear()
p.click()
# 移动到账号框模仿键盘输入账号
action = ActionChains(self.driver)
action.move_to_element(u)
action.click(u)
# 模仿键盘输入账号
action.send_keys(self.userName)
# 移动到密码输入框
action.move_to_element(p)
action.click(p)
# 模仿键盘输入密码
action.send_keys(self.password)
# 点击登录
action.move_by_offset(go.location['x'], go.location['y'])
action.click(go)
# 执行登录
action.perform()
# 休息1秒保证能执行
time.sleep(1)
# 截图保存到当前项目下
self.driver.save_screenshot('screenshotLoginQzone.png')
def save_verify_code(self, element):
url = element.get_attribute('src')
fileName = element.get_attribute('id') + '.jpg'
urllib.request.urlretrieve(url, fileName)
# 校验码
def check_code(self):
# 先切换到默认的窗口
self.driver.switch_to.default_content()
iframe = None
try:
# 验证码
iframe = self.driver.find_element_by_xpath('//*[@id="new_vcode"]/iframe[2]')
except NoSuchElementException:
print('无需输入验证码')
else:
self.driver.switch_to.frame(iframe)
self.verify_code()
# 验证码
def verify_code(self):
que_code = self.driver.find_element_by_xpath('//*[@id="cap_que_img"]')
que_input = self.driver.find_element_by_xpath('//*[@id="cap_input"]')
que_but = self.driver.find_element_by_xpath('//*[@id="verify_btn"]')
# 保存验证码
self.save_verify_code(que_code)
verify_path = que_code.get_attribute('id') + '.jpg'
# 输入验证码
if (self.isWindows()):
os.startfile(verify_path)
else:
os.subprocess.call(["xdg-open", verify_path])
input_verify_code = input("验证码:")
# 模仿用户输入
action = ActionChains(self.driver)
action.move_to_element(que_input)
action.click()
action.send_keys(input_verify_code)
action.move_to_element(que_but)
action.click()
# 执行
action.perform()
def paresHtml(self):
time.sleep(1)
# 切换到默认的容器
self.driver.switch_to.default_content()
# 获取动态列表参数
qzonetoken = self.driver.execute_script('return window.shine0callback')
g_tk = self.driver.execute_script('return window.user.getToken()')
res_type = '0'
res_attach = self.attachinfo and self.attachinfo or self.driver.execute_script(
'return window.FrontPage.data.data.attachinfo')
refresh_type = '2'
attach_info = res_attach
format = 'json'
# 动态列表
sctiveFeeds = 'https://h5.qzone.qq.com/webapp/json/mqzone_feeds/getActiveFeeds?qzonetoken=%s&g_tk=%s' % (
qzonetoken, g_tk)
# 执行请求,并且返回结果
resultStr = next(self.sendRequest(sctiveFeeds, 'POST',
'res_type=%s&res_attach=%s&refresh_type=%s&format=%s&attach_info=%s' % (
res_type, res_attach, refresh_type, format, attach_info)))
result = json.loads(resultStr)
print(resultStr)
# 判断数据是否正确
if result['ret'] == 0 and result['code'] == 0:
self.attachinfo = result['data']['attachinfo']
for item in result['data']['vFeeds']:
self.paresLikeList(item, qzonetoken, g_tk)
else:
print(result['message'])
resultStr = None
result.clear()
def paresLikeList(self, item, qzonetoken, g_tk):
# 点赞参数
opuin = self.driver.execute_script('return user.getUin()')
unikey = item['comm']['orglikekey']
curkey = item['comm']['curlikekey']
appid = item['comm']['appid']
opr_type = 'like'
format = 'purejson'
likeresult = None
# 是否已经点赞,如果是那就不调用
liked = 'like' not in item or not 1 == item['like']['isliked']
if liked:
dolike = 'https://h5.qzone.qq.com/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_dolike_app?qzonetoken=%s&g_tk=%s' % (
qzonetoken, g_tk)
likeresult = json.loads(next(self.sendRequest(dolike, 'POST',
'opuin=%s&unikey=%s&curkey=%s&appid=%s&opr_type=%s&format=%s' % (
opuin, unikey, curkey, appid, opr_type, format))))
if (not likeresult == None) and likeresult['ret'] == 0:
content = ''
if 'summary' in item:
content = item['summary']['summary']
elif 'cell_summary' in item:
content = item['cell_summary']['summary']
elif 'original' in item:
content = item['original']['cell_summary']['summary']
else:
content = '未知内容'
print('点赞成功:%s %s' % (item['userinfo']['user']['nickname'], content))
item.clear()
def sendRequest(self, url, method, data):
cname = 'request_%d' % time.time()
self.driver.execute_script('window.%s = new XMLHttpRequest;' % cname)
self.driver.execute_script('window.%s.withCredentials=true;' % cname)
self.driver.execute_script(
'window.%s.open(\'%s\',\'%s\',false)' % (cname, method, url))
self.driver.execute_script(
'window.%s.onload = function (e){window.%s = window.%s.responseText;}' % (
cname, ('%s_result' % cname), cname))
self.driver.execute_script(
'window.%s.send("%s")' % (cname, data))
yield self.driver.execute_script('return window.%s' % ('%s_result' % cname))
# 内存回收
self.driver.execute_script(' window.%s = undefined;window.%s = undefined;' % (('%s_result' % cname), cname))
# 是 windows 系统
def isWindows(self):
sysstr = platform.system()
if (sysstr == "Windows"):
return True
return False
# 保存登录 cookies
def save_cookies(self):
with open(self.hashCode(), 'wb') as f:
obj = self.driver.get_cookies()
pickle.dump(obj, f)
f.close()
# 读取并设置 cookies
def load_cookies(self):
fileName = self.hashCode()
# 判断文件是否存在
if self.file_exists(fileName):
f = open(fileName, 'rb')
obj = pickle.load(file=f)
f.close()
# 循环设置 cookie
try:
for cookie in obj:
self.driver.add_cookie(cookie)
except Exception as e:
print(e)
def delete_cookies(self):
os.remove(self.hashCode())
# hasCode
def hashCode(self):
sha = sha1()
sha.update(b'qzone_cookies')
return sha.hexdigest()
# 判断文件是否存在
def file_exists(self, filename):
try:
with open(filename) as f:
return True
except IOError:
return False
# 退出浏览器
def __del__(self):
self.driver.quit()
def startQzoneBaselanding():
# 事先输入账号和密码
userName = '***'
password = '***'
oldTime = time.time()
browser = qzone_dlewares(userName=userName, password=password)
# 加载cookies
browser.load_cookies()
initTime = time.time()
# 打开浏览器并且截图
browser.startQzoneRequest()
requestTime = time.time()
# 判断是否登录
if (not browser.isLogin()):
# 模仿用户登录
browser.loginQzone()
# 检查code
browser.check_code()
currentTime = time.time()
# 解析动态
browser.paresHtml()
# 运行完成后再截图一次
browser.driver.save_screenshot('screenshotLoginQzoneSuccess.png')
# 保存cookies
browser.save_cookies()
print('开始时间 %f' % oldTime)
print('结束时间 %f' % currentTime)
print('初始化时间 %f' % (initTime - oldTime))
print('加载页面时间 %f' % (requestTime - initTime))
print('模仿操作时间 %f' % (currentTime - requestTime))
print('总运行时间 %f' % (currentTime - oldTime))
return browser
if __name__ == '__main__':
# 第一次查询
browser = startQzoneBaselanding()
starttime = time.time()
while True:
# 两小时刷新
currentTime = (time.time() - starttime) / 60 / 60
if currentTime >= 2:
browser.driver.quit()
browser.delete_cookies()
browser = startQzoneBaselanding()
continue
# 20秒刷新
time.sleep(20)
browser.paresHtml()
以上仅供参考学习。