Crawler

一次爬虫的记录与分享

0x00准备

安装selenium库,使用urllib库,re库

0x01实现

selenium模拟登录

emmm,起因是为了实现自动在qzone发送说说,然后到github上去翻了翻,发现轮子都已经失效了(大概没人喜欢做这么无聊的事吧
登录qzone: i.qq.com,查看到qzone的登录是利用js实现的

并没有想去分析qzone的js算法,决定使用selenium模拟登录qzone,不难操作,然后取得当前页面,也就是登录完成的页面的cookie,(注意!用sleep来等待大量数据加载完成,许多操作不能达到预计效果可能是因为网络不佳造成的)

除了cookie,还有当前页面的源代码也要保存下来(笑

分析说说发送过程

在首页随便发送一条说说,截获一条post请求

分析结构,发现请求地址很长

1
https://user.qzone.qq.com/proxy/domain/taotao.qzone.qq.com/cgi-bin/emotion_cgi_publish_v6?g_tk=91245527&qzonetoken=010dee36790ae077dbba7521e3e0838b6d9ef25271a9260c8040cd2c2e976f58f614e3ab913c683dfb5c90

前面一部分是固定的(除非人为的话,后面有两个参数g_tk和qzonetoken,这是两个为了加密而设置的参数,通过js来获得,qzonetoken的函数源码没有找到,查询网络上的也是一些过时的(算法早就改变了,于是我从主页的源码中扣下来,在最底下,用正则来截取

g_tk无法在源码中找到,于是刷新主页,查到在一个interface_mini.js?max_age=60628的js文件里有定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
QZONE.FrontPage.getACSRFToken = function(url) {
url = QZFL.util.URI(url);
var skey;
if (url) {
if (url.host && url.host.indexOf("qzone.qq.com") > 0) {
skey = QZFL.cookie.get("p_skey");
} else {
if (url.host && url.host.indexOf("qq.com") > 0) {
skey = QZFL.cookie.get("skey");
}
}
}
if (!skey) {
try {
skey = parent.QZFL.cookie.get("p_skey") || "";
} catch (err) {
skey = QZFL.cookie.get("p_skey") || "";
}
}
if (!skey) {
skey = QZFL.cookie.get("skey") || QZFL.cookie.get("rv2");
}
var hash = 5381;
for (var i = 0, len = skey.length;i < len;++i) {
hash += (hash << 5) + skey.charAt(i).charCodeAt();
}
return hash & 2147483647;
};

简单分析一下,用python来重写了g_tk的计算函数(这种操作会因为腾×更新算法而失效,而腾×的更新恰好是比较快的2333

urllib发送post请求

没啥好说的,构造post请求头,主体,发送请求

源码地址:https://github.com/void0red/code/blob/master/qzone.py

0x03后记

最滑稽的是,这个爬虫并没有实现,运行结果是Bad Request,233333

已经解决,问题在于没有计算请求头中Content-Length的值(笑

总的来说,程序可用性不高:

  1. 登录是用selenium实现的,加载会占用大量内存与cpu,不适宜在小型设备(例如树莓派)上运行
  2. 两个加密函数有时效性,qzonetoken还好,g_tk的计算就显得有些鸡肋,一旦腾×更改其中的某段代码中的某个参数值,该爬虫就得重新编写(事实上他也是这么做的,可以考虑用正则把js扣出来,放在本地运行,这样程序大概能健壮一些
  3. 程序容错率不高,一旦有环节出错,整个过程无法实现

可喜的是,终于成功爬了qzone,2333

void0red 2017.10.13