Python3爬虫实战——QQ空间自动点赞程序(上)

(前一段时间刚学了Python,以为须要拿点东西来练手,因而就决定写个实用点的东西,也就是这条既能练手又能装逼的qq空间自动点赞的小爬虫)html

(好多不常联系的同窗都由于个人秒赞对我表示了他们的感动,搞得我都不敢说我是用程序自动点赞的)python

(然而,与其说是练手Python,其实写这个爬虫我学到的更多的知识是关于http请求的)
正则表达式

—————————————————————————————————————————————————————————————————算法

行吧,开车了。服务器

①探索:当咱们按下“赞”以后,Qzone的后台发生了什么PY交易?cookie

<1>首先,抓包。打开网页版的QQ空间(我已经用F12把各类推广的窗口强行关掉了,以及,两个号都是个人小号),再打开抓包工具(这里我用的抓包工具是Fiddler,百度一下就能找到下载资源了)。先清空Fiddler抓到的包,而后点“赞”,以后“取消赞”。app


Fiddler抓到的以下四个数据包dom



<2>而后,分析包。点击分析目标,选中右边的Inspector,会获得这个包的解析数据。(若是咱们只须要“赞”的功能的话,就只分析第一个数据包就够了。以后你能够尝试本身去看“取消赞”的包,其实跟“赞”作的工做是同样的,只不过post的url不同罢了)函数



在Headers的标签页中,咱们能够看出,点赞操做用的是POST方法,向“/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_dolike_app?g_tk=1589101394”的相对地址POST了这个包,就能实现“赞”的功能
工具

因而,目标很明确,想要实现“赞”,咱们就必须模拟这个包,使用python3的http模块中的http.client.HTTPConnection.request()来将这个包发送给服务器实现“赞”。(对应Python2的httplib模块的httplib.HTTPCOnnection.request()函数,其实我到后来才发现有个requests的模块很是好用,你们能够本身百度“python requests”,但下述代码仍是以python3的http模块来实现)

发送包的代码以下:

from http import client;
from urllib import parse;
httpClient=client.HTTPConnection('h5.qzone.qq.com');
#h5.qzone.qq.com是Host
httpClient.request("POST",url,parse.urlencode(body),headers);
#method是"POST"
#url是上述的"/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_dolike_app?g_tk=1589101394",其中g_tk在每次登陆空间后都不同。
#body和headers是字典型


那么问题就来了,url和数据包的内容是什么?怎么模拟?


②解答:Qzone点赞时,该POST的数据包的内容是什么?该向谁POST数据包?

<1>该POST的数据包内容之headers

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'
        }
headers['Cookie']='QZ_FE_WEBP_SUPPORT=*; cpu_performance_v8=*; _gdt_close_discover=*; __Q_w_s__QZN_TodoMsgCnt=*; blabla=*; zzpaneluin=*; zzpanelkey=; p_skey=5O9avpyb9nqMw7VP6P9BZ13X8vUUJup50PDFEACDgM8_; pt4_token=*; p_uin=*; pgv_pvi=*; pgv_pvid=*; ptui_loginuin=*; pt2gguin=*; RK=*; ptcz=*; skey*; pgv_si=*; pgv_info=ssid=*; ptisp=*; uin=*; qzone_check=*'
#参数值字符太多,我把cookie中的除了p_skey的参数值都用*代替了。
是的,这些就是headers的内容了,除了Cookie之外,不须要知道其余参数是干吗的,由于headers字典中其余的参数对全部用户实现这个爬虫都几乎没有影响。

因此,Cookie从哪里找呢?


鼠标三击Raw中的Cookie一行,便可选中整行,Ctrl+C复制,贴到代码中,去掉前缀,两侧加单引号,headers的内容就解决了。

必须使用颜色强调的是:个人这个爬虫暂没有实现自动登陆Qzone的能力,每次都要靠手动登陆,抓包获取Cookie,修改headers字典的“Cookie”来维持程序正常运行。但因为QQ空间的Cookie只要不断使用,有效时间将在12个小时以上,因此若是你想让程序长时间跑,更换Cookie也不会太频繁。(因此我称它为简单的爬虫)

<2>该POST的数据包内容之body

qqNumber='3236556749';
body={'qzreferrer':'http://user.qzone.qq.com/'+qNumber,
      'opuin':qNumber,
      'unikey':'',
      'curkey':'',
      'from':1,
      'appid':311,
      'typeid':0,
      'active':0,
      'fupdate':1
     }
body['unikey']='http://user.qzone.qq.com/***/mood/54a9a141c35a0b584d460300'
body['curkey']='http://user.qzone.qq.com/***/mood/54a9a141c35a0b584d460300'
#因为点赞对象是经常使用小号不想泄露,因此我就用***打个马赛克
params=urllib.urlencode(body)
一样,我把须要注意的参数单独拉出来赋值了。包括qqNumber,unikey,和curkey。
qqNumber如其名,就是点赞者的QQ,和Cookie同属于半固定类型的——在整个程序的执行过程当中保持静态,不会变化。

而unikey和curkey则是会根听说说的不一样而动态变化的。原创说说的unikey==curkey成立,而若是是转发而来的说说则unikey!=curkey为真。curkey的值取决于被转发者的说说原创时产生的unikey,unikey的值则是转发者转发时产生的。

固然,以下图,指定说说的unikey和curkey能够在在数据包中找到,可是在自动点赞程序中,unikey和curkey显然不是静态的!


因此?unikey和curkey在哪里找呢?这个问题我如今可能很难详细地讲清楚,须要卖个关子,等下一章或者下下章条件凑齐后才能讲明白。我只能简单地说——经过正则表达式过滤含有说说的html页面来得到unikey和curkey。


<3>该向谁POST数据包?(url中的可变参数g_tk)

def getGTK(ss):
    hash=5381
    for i in ss:
        hash+=(hash<<5)+ord(i);
    return (hash & 0x7fffffff);
#算法找自百度搜索"QQ空间 g_tk";
g_tk=getGTK(p_skey);
url="/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_dolike_app?g_tk="+str(g_tk);

g_tk的值取决于Cookie中的p_skey的参数值。能够说,它也是半固定的值——整个程序运行过程当中g_tk的值不会动态变化。可使用上述算法从Cookie中的p_skey计算而来,也能够以下找到:


一般我都是选择从Cookie中计算出来,避免重启爬虫的时候须要改太多参数了。一样,须要用到正则表达式来将目标p_skey从Cookie中过滤出来,而后计算。

p_skey=re.search('p_skey=([^;^\']*)',cookie).group(1);#正则过滤cookie获得p_skey
g_tk=getGTK(p_skey);
url="/proxy/domain/w.qzone.qq.com/cgi-bin/likes/internal_dolike_app?g_tk="+str(g_tk);

 
  

②至此,咱们已经具有写出“点赞类”的主要相关知识

from http import client 
from urllib import parse 
import re
import sys

class httpPOSTer:
    headers=None;
    body=None;
    qNum=None;
    url=None;
    cookie=None;
    url=None;
    
    def getGTK(self,ss):
        hash=5381
        for 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
qzoneP=httpPOSTer(cookie,qqNumber);#实例化一个点赞类
qzoneP.thumbs_up(unikey,curkey);#使用"thumbs_up()"方法,unikey和curkey的得到将在下一章或下下章讲述
#main process END


(详细讲的话,一章显然讲不彻底。我跟同窗说,我要写一篇0基础看了、都能看懂这个爬虫代码的教程,因此可能有点啰嗦……见谅)

(以后可能还会有一章,或者两章,讲述较多简单的细节,反正我如今是写不完了。凌晨四点了,好困……)