(发完上一篇博客之后,观察了一个星期发现阅读量迟迟突破不了50大关,蓝瘦香菇+心疼自己T.T,于是果然又找到了各种拖延的理由,刚才登博客的时候突然发现有人评论期待我的下篇,立马精神振奋!开始敲字。)

——————————————————————————————————————————————————————————————————————

那么,再次发车。

上一次我们已经实现了“点赞”,却是缺少了“自动”,这一篇,就讲解如何实现“自动“点赞程序。

①探索:在浏览器地址栏输入网址并回车、或点击刷新后,浏览器与Qzone又发生了什么交易~

<1>与上一篇一样,先抓包。演员又是它们,我的两个小号。登录QQ空间后,打开抓包工具,刷新页面,Fiddler里哗啦啦地闪出一排数据包。然而,只有一个主角,那就是我们“刷新”操作时候发出去的数据包。

(浏览器访问空间.png)

(数据包们.png)

<2>照例,分析包。可见,在我们刷新页面后,浏览器向user.qzone.qq.com的相对路径/3236556749使用HTTP请求中的GET方法。(GET和POST的区分百度一搜一大把,我也有点晕。只是单纯地认为GET就是向服务器发个只有headers、body为空的数据包过去,包里含有Cookie,索要/3236556749的内容。而POST则是发包含了非空headers和非空body的数据包,body中含有了该次操作的内容,服务器解读body并实现对应的操作。)

(数据包的headers内容.png)

于是,现在目标就是用Python3的代码实现GET方法了。

代码如下:

<pre name="code" class="python">from http import client;
from urllib import parse;headers = {'Host': 'user.qzone.qq.com','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3','Accept-Encoding': 'gzip, deflate','Cookie': '','Connection': 'keep-alive','Upgrade-Insecure-Requests': 1,'Content-Type': 'application/x-www-form-urlencoded'}
headers['Cookie']='*************';#(自己抓)
httpClient=client.HTTPConnection('user.qzone.qq.com') #host是user.qzone.qq.com
httpClient.request("GET","/3236556749",parse.urlencode({}),headers);#输出获得的内容
response=httpClient.getresponse();
print(response.status);#状态,成功的话是200;常见的错误403表示权限不足,404表示页面不存在。
print(response.reason);#原因,成功的话是ok;失败的话,各种原因
html=response.read();
print(html);#获得的数据包内容

与上一章的POST方法类似,使用的是同一个函数。只不过Host改了,method为“GET”,url固定为你的QQ号,body为空,headers仅有Host不一样。

然后,你看到了什么?

b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xb4YOo\xdb\xc8\x15?\xab\xc0~\x871\x03H$BQ\x96\xd3\xd8\x8edz\xb1Mb4@\xb6[7^\xa0mj\x08\x149&\x19S\x1c\x9a3\xb2\x1c\xc7\x06\……………(省略n长字符)……………'

这是什么——一串byte类型的'\x1f\x8b'开头的莫名其妙的字符。当时我各种百度,各种懵逼,用了各种在线转码工具,UTF-8转码,Unicode转码,ASCII转码,16进制转十进制再转码……依旧懵逼……

也忘了是什么原因让我找到了它的正确理解方式——它是使用gzip格式压缩的一个压缩包!所以是byte类型。

所以,为了得到正确的html文件,我们要解压这个数据包。

②实现:GZIP解压GET到的数据包,获取Qzone的HTML文件实现“刷新”

代码如下:

import gzip#①
html=gzip.decompress(html).decode("utf-8");#解压get到的html文件并解码为utf-8或者unicode格式#②
non_bmp_map=dict.fromkeys(range(0x10000,sys.maxunicode+1),0xfffd);#将编码为0x10000到最大unicode编码之外的编码替换为0xfffd
print(html.translate(non_bmp_map));#令得到的html实现上述过滤

理论上,标注为①的解压解码完成后,就可以print(html)了,至少现在是这样的。

但是在我实现这个代码的时候,并没有这么容易——因为emoji超脱三界之外无量天之外的存在!

如果在获得的html文件中含有emoji的话,不通过标注为②的过滤步骤,使用print()会报"UnicodeDecodeError"的错,因为print无法print出emoji!

然而注意到了没,近期Qzone发说说后,emoji表情变得不清真了?

原因如下:

(Qzone内emoji处的html代码.png)

是的,近期qq空间优化了网页端的显示,使用url代替了原本的emoji,而如果不通过url优化emoji显示的话,下面就是电脑网页端使用emoji的车祸现场(找不到原版的QQ空间的emoji截图了,反正更惨):

(百度知道内的emoji处的html代码.png)

会发现,emoji在html中仍旧以图形的方式出现,这显然不是print()可以打印出来的,所以当爬虫抓取有emoji的页面时,想要使用print进行debuge时,步骤②的过滤还是必要的。

现在,将html变量print出来之后,是不是已经看到了"user.qzone.qq.com/(你的qq号)"页面上的所有html标签和内容了~

此时,可以写出“页面刷新”类了:

<pre name="code" class="python">from http import client
from urllib import parse
import gzip
import sys
<pre>class httpGETer:httpClient=None;content=None;headers=None;status=None;qNum=None;cookie=None;def __init__(self,coo,qqNumber):self.cookie=coo;self.qNum=qqNumber;self.headers={'Host':'user.qzone.qq.com','User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0','Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language':'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3','Accept-Encoding':'gzip, deflate','Cookie':'','Connection':'keep-alive','Upgrade-Insecure-Requests':1,'Content-Type':'application/x-www-form-urlencoded'            }self.headers['Cookie']=coo;self.refresh();def refresh(self,):try:self.httpClient=client.HTTPConnection('user.qzone.qq.com');#我也忘了当时为啥要用这么浪费资源的写法了,“每次刷新重连一次”self.httpClient.request('GET','/'+self.qNum,parse.urlencode({}),self.headers)temp=self.httpClient.getresponse()self.status=temp.statusself.content=gzip.decompress(temp.read()).decode("utf-8").translate(dict.fromkeys(range(0x10000,sys.maxunicode+1),0xfffd))except Exception as e:if(self.content!=None):print("Error happended in the class httpGETer"+time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())));print(e)self.content=None;finally:if self.httpClient:self.httpClient.close();def getHtml(self,):if(self.content):return self.content;else:return "Fail to get HTML data";
#使用这个类的代码如下:
#main process BEGIN
<span>cookie=<span class="string">'(输入区)'</span><span>;</span><span class="comment">#自己输入cookie</span><span></span>
qqNumber=<span class="string">'(输入区)'</span><span>;</span><span class="comment">#自己输入qq</span><span>
qzoneG=httpGETer(cookie,qqNumber);#实例化一个页面刷新类
qzoneG.refresh();#刷新
print(qzoneG.getHtml());#输出html内容
#main process END

现在,我们已经实现了获取页面html内容功能。

还差什么。

还差上一篇留下来的问题没有解决——获得页面中每一条说说的unikey和curkey!

③实现:使用正则表达式过滤页面内容抓取unikey和curkey。

其实也就四行代码:

html=qzoneG.getHtml();
temp=re.search('data-unikey="(http[^"]*)"[^d]*data-curkey="([^"]*)"[^d]*data-clicklog=("like")[^h]*href="javascript:;"',html);
unikey=group(1);
curkey=group(2);

(完结!撒花~~~~~~~~~               才怪-.-!)

这个正则表达式我费了那么大周折才搞对的怎么可能不把这个逼装完整。

所以,从页面哪里找到unikey和curkey

步骤1:通过Fiddler找到点赞的数据包中的unikey一个确切的值

(通过Fiddler获得点赞请求包中的unikey和curkey的值.png)

步骤2:通过浏览器中按F12,然后CTRL+F,粘贴unikey的值,找到含有unikey的位置。

(FireFox反向搜索unikey位置.png)

(Edge反向搜索unikey.png)

是的,就是这么坑。

HTTP请求发送的数据包的headers中包含了你所使用的浏览器的信息,就有可能出现不同浏览器获得不同的DOM的情况。

所以请注意,想要“点赞类”和“页面刷新类”共用一个Cookie的话,请尽量保证headers中的浏览器信息是同一个浏览器~(最好是headers除了Host,其他的都一样)

步骤3:写出对应的正则表达式

正则表达式……网上很多教程,我只推荐这个在线测试的网址——http://tool.oschina.net/regex/

然后我再解释一下,为何我的正则表达式长得那么坑。

<pre name="code" class="python">temp=re.search('data-unikey="(http[^"]*)"[^d]*data-curkey="([^"]*)"[^d]*data-clicklog=("like")[^h]*href="javascript:;"',html);

re模块——是regex(正则表达式)的缩写。

search(表达式,对象)函数——返回对象被过滤后的值,匹配到一个就停止。

这些都很容易搜到。我只讲两个跟Qzone有关的内容:

第一个,是一个坑爹的参数——data-clicklog

我原本的正则表达式是不包括href="javascript;"这个内容的,理论上,当我成功点赞一条说说后刷新页面获得新的DOM树,data-clicklog的值会从"like"变为“cancellike”。而事实确实如此。

但是!我之后分析了一下点赞后的页面,发现这条正则仍能抓到同一个unikey和curkey。导致重复发送n次同一个数据包,且只能点赞第一个人。(之前有同学就抱怨说Qzone一直重复提醒她我点赞的消息)。

说这么多,我只想强调——“如果unikey和curkey在多次运行后没有改变,不是刷新类的错,是正则表达式”、“正则表达式限制条件尽量多一点,否则抓不准”

第二个,是unikey和curkey的取值

通常unikey和curkey都是“http://”开头的一个连接。然而,上一篇我们说过,curkey是原作者发表说说时候产生的,但如果转发者分享的是一个连接呢?分享的是一个推广呢?分享的是一首音乐呢?原作者不是用户,所以curkey存在不是“http://”开头的情况。因此我正则表达式中data-curkey=“([^"]*)”里面没加http开头的限制。

④完结!撒花~~

我把全部代码贴出来了,有问题还请评论,这段时间我会不定期查看阅读量的T.T:

from http import client
from urllib import parse
import re
import time
import gzip
import sysclass httpGETer:httpClient=None;content=None;headers=None;status=None;qNum=None;cookie=None;def __init__(self,coo,qqNumber):self.cookie=coo;self.qNum=qqNumber;self.headers={'Host':'user.qzone.qq.com','User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0','Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language':'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3','Accept-Encoding':'gzip, deflate','Cookie':'','Connection':'keep-alive','Upgrade-Insecure-Requests':1,'Content-Type':'application/x-www-form-urlencoded'            }self.headers['Cookie']=coo;self.refresh();def refresh(self,):try:self.httpClient=client.HTTPConnection('user.qzone.qq.com');self.httpClient.request('GET','/'+self.qNum,parse.urlencode({}),self.headers)temp=self.httpClient.getresponse()self.status=temp.statusself.content=gzip.decompress(temp.read()).decode("utf-8").translate(dict.fromkeys(range(0x10000,sys.maxunicode+1),0xfffd))except Exception as e:if(self.content!=None):print("Error happended in the class httpGETer"+time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())));print(e)self.content=None;finally:if self.httpClient:self.httpClient.close();def getHtml(self,):if(self.content):return self.content;else:return "Fail to get HTML data";class httpPOSTer:headers=None;body=None;qNum=None;url=None;cookie=None;url=None;def getGTK(self,ss):hash=5381for i in ss:hash+=(hash<<5)+ord(i);return (hash & 0x7fffffff);def __init__(self,coo,qqNumber):self.cookie=coo;self.qNum=qqNumber;p_skey=re.search('p_skey=([^;^\']*)',self.cookie).group(1);g_tk=self.getGTK(p_skey);self.url="/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_dolike_app?g_tk="+str(g_tk);self.headers={'Host':'h5.qzone.qq.com','User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0','Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language':'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3','Accept-Encoding':'gzip, deflate','Cookie':'','Connection':'keep-alive','Upgrade-Insecure-Requests':1,'Content-Type':'application/x-www-form-urlencoded'}self.headers['Cookie']=coo;self.body={'qzreferrer':'http://user.qzone.qq.com/'+qqNumber,'opuin':qqNumber,'unikey':'',#mark'curkey':'',#mark'from':1,'appid':311,'typeid':0,'active':0,'fupdate':1}def thumbs_up(self,unikey,curkey):self.body['unikey']=unikey;self.body['curkey']=curkey;print("url="+self.url);httpClient=client.HTTPConnection('h5.qzone.qq.com');httpClient.request("POST",self.url,parse.urlencode(self.body),self.headers);response=httpClient.getresponse();print("thumbs_up "+str(response.status)+" "+response.reason);httpClient.close();#main process begin
cookie='(输入)';#输入Cookie
qqNumber='(输入)';#输入qq号
qzoneG=httpGETer(cookie,qqNumber)#实例化一个页面刷新类
qzoneP=httpPOSTer(cookie,qqNumber)#实例化一个点赞类
flag=1;#避免重复输出的标记,影响不大
while(True):qzoneG.refresh();html=qzoneG.getHtml();temp=re.search('data-unikey="(http[^"]*)"[^d]*data-curkey="([^"]*)"[^d]*data-clicklog=("like")[^h]*href="javascript:;"',html);#正则自己写咯if(temp!=None):print(temp.group(0))unikey=temp.group(1);curkey=temp.group(2);qzoneP.thumbs_up(unikey,curkey);flag=1;if(flag==1):print("sleeping...    from:"+time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) );#输出最该次点赞的时间flag=0;time.sleep(1);#1秒延迟,可以在好友发完说说后的一秒左右完成点赞。也可以腾出CPU资源
#main process end
(文章排版很丑我还是有自知之明的……所以我尽量把重点句子都棕色标出来了……不然我自己都没有看下去的欲望……)
(之后我会尝试着实现自动登录获取Cookie,否则频繁更换Cookie真的好枯燥。)
(撒花~)

Python3爬虫实践——QQ空间自动点赞程序(下)相关推荐

  1. PHP爬虫之QQ空间自动点赞--更换cookie版

    QQ空间自动点赞网上一搜一大把,但是关于php的还是停留在用以前的3Gqq登陆方式获取sid之后再点赞的.而现在貌似3Gqq没法用了, 网上也没有关于最新的.实现QQ空间自动点赞已经很久了,一直没有发 ...

  2. 基于selenium的QQ空间自动点赞功能(2020年7月版)

    准备工作 简介: selenium是一个基于java开发的自动化浏览器处理器,它更像是浏览器驱动的代理. 配置: 它本身没有配置浏览器,因此需要配合本机上面安装的浏览器驱动一同使用.例如:Firefo ...

  3. 24行代码简单实现qq空间自动点赞

    什么是Auto.js? Auto.js是基于JavaScript语言运行在Android平台上的工具.它依赖于无障碍服务. 它可以做什么? 解放双手,让手机自动打游戏.自动签到.自动领红包等等等等 它 ...

  4. QQ空间自动点赞脚本

    先放上代码: 在浏览器开发者工具里面的控制台(console)里面粘贴这段代码,然后回车就可以了,前提是qq空间的页面要一直开着,在个人中心可以运行. var clicklog = function( ...

  5. JS实例操作QQ空间自动点赞方法

    做开法的小伙伴都知道我们在查找网络资源时,通常会通过 使用谷歌浏览器的 F12 对页面元素进行操作,且可以查看一些网络资源,当然火狐浏览器也有这种功能,不是IE就好了. 打开QQ空间 按下 F12 , ...

  6. [转] JS实例操作QQ空间自动点赞方法

    做开法的小伙伴都知道我们在查找网络资源时,通常会通过 使用谷歌浏览器的 F12 对页面元素进行操作,且可以查看一些网络资源,当然火狐浏览器也有这种功能,不是IE就好了. 打开QQ空间 按下 F12 , ...

  7. C#源码QQ空间自动点赞神器,无需密码直接点头像登录,可加自动功能评论转发等。

    使用C#开发的:最新版V5.7下载https://pan.baidu.com/s/1ZOG2KLV8z0pXNXjCRfMMhw 可以直接看见页面操作结果. 功能: 1.朋友发表说说,第一时间点赞. ...

  8. 利用浏览器JS代码实现QQ空间自动点赞

    我不喜欢我的时间用来看说说,因为我的朋友和同学总是在说话,要求关注点赞.虽然我不喜欢看空间,但是有时候也确实有这个需求.而且有时候碍于一些原因不得不去帮别人点赞,但是自己又没时间或者不想去翻空间的时候 ...

  9. QQ空间自动点赞js脚本

    这是很久前写的脚本了,在浏览器打开QQ空间,并在控制台输入代码就可 时间间隔最好开大点,不然容易被暂时冻结账号 1 function autoLike() 2 { 3 var list=documen ...

最新文章

  1. C# Task的用法
  2. Boost:验证atomic <T>具有来自T的隐式转换构造函数
  3. java后台两个表关联查询_简单界面+JAVA后台+数据库实现页面对数据库的两张关联表操作...
  4. 守卫者的挑战(guard)
  5. ASP.NET MVC5+EF6+EasyUI 后台管理系统(40)-精准在线人数统计实现-【过滤器+Cache】...
  6. CrossOver如何删除容器软件的安装包
  7. 阶段1 语言基础+高级_1-3-Java语言高级_08-JDK8新特性_第1节 常用函数接口_4_使用Lambda优化日志案例...
  8. [Canvas]空战游戏进阶 增加发射子弹 敌机中弹爆炸功能
  9. [Fondar]手机屏幕驱动板HDMI接口USB触摸使用步骤
  10. Linux就这个范儿 第10章 生死与共的兄弟
  11. 魔兽地图编辑器使用自定义图标的方法
  12. 代理模式实例与解析--实例一:论坛权限控制代理
  13. 用C语言程序求两个正整数的最大公约数
  14. 锤子手机Android auto,分享锤子手机Smartisan OS的几个使用小技巧,效率更高
  15. Centos jenkins 插件安装失败
  16. gos-log高性能大日志检索中台
  17. 福建厦门双十计算机竞赛,22人夺一等奖!恭喜厦门一中、双十中学、实验中学...
  18. Jetson Nano部署YOLOv5与Tensorrtx加速——(自己走一遍全过程记录)
  19. 形式语言与自动机理论的发展
  20. ESP32超详细学习记录:NTP同步时间

热门文章

  1. 卖锁小老板的创业之路,如何从入不敷出到月入30万的方案分享
  2. 小程序连接 域名不合法
  3. 计算机发展历史分为几代,cpu发展历程经历了几代?每一代计算机包括哪些型号的芯片...
  4. 时间序列预测:指数平滑法及python实现
  5. Android系统的由来
  6. 2022年11月前端学习笔记
  7. UR5机械臂正向运动学
  8. 【数学】三角函数性质及公式
  9. 《文明IV》主题歌获2011年格莱美奖
  10. 64位linux下安装ps模拟器ePSxe