Python网络爬虫5 - 爬取QQ空间相册
自毕业后,就再也没有用过QQ,QQ空间里记录的是些并不精彩的青葱岁月,但好歹也是份回忆,近日想着学以致用,用Python把QQ空间相册的所有照片爬取下来,以作备份。
分析QQ空间
登录QQ空间
爬取第一步,分析站点,首先需要知道如何登录QQ空间。最初想法是用requests库配置登录请求,模拟登录,但是不久便放弃了这一思路,请看下图↓
根据登录按钮绑定的监听事件可以追踪到该按钮的点击事件如下:
账号加密是必然的,但这一堆堆的代码真心不好解析,有耐心的勇士尽情一试!
在排除这种登录方法后,选择selenium模拟用户登录不失为省时省力的方法,而且我们只是需要通过selenium完成登录,获取到Cookies和后面讲述的g_tk参数后,就可以停用了,所以效率并不太低。
分析空间相册
登录以后,页面会跳转至 https://user.qzone.qq.com/{QQ_NUMBER}, 这时把鼠标移到导航栏你会发现,所有的导航栏链接都是javascript:; 😳。没错就是这么坑,一切都是暗箱操作。
当然这并不难处理,使用调试工具捕获点击后产生的请求,然后过滤出正确的请求包即可。因为网络包非常多,那么怎么过滤呢,猜想相册数据的API必然会返回个列表list,尝试过滤list然后逐个排除,最后定位到请求包。下面是通过fcg_list过滤后的数据包,列表信息以jsonp格式返回,稍作处理即可当做json格式来读取(后面有讲)。
从Headers和Response可以分别获取到两组重要信息:
request获取相册列表所需的请求信息,包括请求链接和参数response数据包包含的所有相册的信息,是每个相册所含照片对应的请求包参数的数据来源
先看请求包:
1 | |
其中hostUin, uin都是QQ号,g_tk是必须的且每次重新登录都会更新(后面有讲如何获取),其它有些参数不是必须的,我尝试后整理出如下请求参数:
1 | |
接下来看jsonp格式的跨域响应包:
1 | |
shine0_Callback是请求包的callbackFun参数决定的,如果没这个参数,响应包会以_Callback作为默认名,当然这都不重要。所有相册信息以json格式存入albumListModeSort中,上面仅截取了一个相册的信息。
相册信息中,name代表相册名称,id作为唯一标识可用于请求该相册内的照片信息,而pre仅仅是一个预览缩略图的链接,无关紧要。
分析单个相册
与获取相册信息类似,进入某一相册,使用cgi_list过滤数据包,找到该相册的照片信息
同样的道理,根据数据包可以获取照片列表信息的请求包和响应信息,先看请求:
1 | |
其中有几个关键参数:
g_tk- 与相册列表参数一致topicId- 与相册列表参数中的id一致pageStart- 本次请求照片的起始编号pageNum- 本次请求的照片数量
为了一次性获取所有照片,可以将
pageStart设为0,pageNum设为所有相册所含照片的最大值。
同样可以对上面的参数进行简化,在相册列表请求参数的基础上添加topicId,pageStart和pageNum三个参数即可。
下面来看返回的照片列表信息:
1 | |
返回的照片信息都存于photoList, 上面同样只截取了一张照片的信息,后面一部分返回的是当前相册的一些基本信息。totalInAlbum, totalInPage存储了当前相册总共包含的照片数及本次返回的照片数。而我们需要下载的图片链接则是url!
OK, 到此,所有请求和响应数据都分析清楚了,接下来便是coding的时候了。
确定爬取方案
- 创建
qqzone类,初始化用户信息 - 使用
Selenium模拟登录 - 获取
Cookies和g_tk - 使用
requests获取相册列表信息 - 遍历相册,获取照片列表信息并下载照片
创建qqzone类
1 | |
模拟登录
1 | |
此处需要注意的是:
- 使用
selenium需要安装对应的webdriver - 可以通过
webdriver.Chrome()指定浏览器位置,否则默认从环境变量定义的路径查找 - 如果电脑打开浏览器较慢,可能需要在
driver.get后sleep几秒
获取 Cookies
使用selenium获取Cookies非常方便
1 | |
获取 g_tk
获取g_tk最开始可以说是本爬虫最大的难点,因为从网页中根本找不到直接写明的数值,只有各种函数调用。为此我全局搜索,发现好多地方都有其获取方式。
最后选择了其中一处,通过selenium执行脚本的功能成功获取到了g_tk!
1 | |
到此,selenium的使命就完成了,剩下的将通过requests来完成。
初始化 request.Session
接下来需要逐步生成请求然后获取数据。但是为方便起见,这里使用会话的方式请求数据,配置好cookie和headers,省的每次请求都设置一遍。
1 | |
请求相册信息
获取相册信息,需要先封装好请求参数,然后通过session.get爬取数据,再通过正则匹配以json格式读取jsonp数据,最后解析所需的name和id。
1 | |
其中的参数组合来自下面的函数_get_query_for_request函数。
1 | |
其中的jsonp解析函数如下,主体部分就是一个正则匹配,非常简单。
1 | |
解析并下载照片
获取相册列表后,逐个请求照片列表信息,进而逐一下载
1 | |
下载图片也是通过request,记得设置超时时间。
1 | |
爬取测试
- 爬取过程
- 爬取结果
写在最后
- 如果将请求参数中的
format由jsonp改成json,则可以直接获取json数据 - 本用例并未使用多进程或多线程,所以速率不算快,还有待优化的地方
- 该爬虫已存放至开源项目Github capturer,欢迎交流






