使用python+selenium爬取qq空间好友动态

使用python+selenium爬取qq空间好友动态

分析过程如下:

打开qq空间网址:https://qzone.qq.com/ ,内容如下:

要想用selenium登陆qq空间,必须点击账号密码登陆按钮然后再填写账号密码登陆。

1.PNG

点击账号密码按钮后跳转到如下页面:

2.PNG

以上过程实现代码:

# 这是你的chromedriver的对应版本文件
chrome_driver = r'E:\迅雷下载\chromedriver_win32\chromedriver.exe'
driver = webdriver.Chrome(executable_path=chrome_driver)

driver.get('https://qzone.qq.com/')

# driver.

# 切换网页框架
driver.switch_to.frame(driver.find_element_by_id('login_frame'))

# print(driver.page_source)

# 切换到账户密码输入界面
driver.find_element_by_id('switcher_plogin').click()

接下来就是输入账号、密码,点击登陆。

代码如下:

# 输入账号
driver.find_element_by_id('u').clear()
driver.find_element_by_id('u').send_keys('****') # 此处填写账号

# 输入密码
driver.find_element_by_id('p').clear()
driver.find_element_by_id('p').send_keys('****') # 此处填写密码

# 登陆账号
driver.find_element_by_id('login_button').click()
# 等待三秒让浏览器加载完
time.sleep(3)

登陆过后就进入了qq空间,但有可能不是好友动态页面,这是就需要用selenium来模拟点击跳转到好友动态页面:

3.PNG

代码如下:

driver.find_element_by_xpath('//*[@id="tab_menu_friend"]/div[3]').click()
# 休息3秒等待页面加载完
time.sleep(3)

这时我们就进入了qq空间好友动态页面,但是我发现好友动态是页面局部刷新加载出来的,所以要去查找动态加载文件。经过查找,我发现动态加载信息存放在feeds_3_html....文件下。

4.PNG

使用代码直接获取这个页面会报错,因为这个页面不仅需要登陆,而且他请求地址中的g_tk查询字符串还是通过加密构造的,其中有两个字段非常关键,一个是begintime,还有一个是加密得到的g_tk

5.PNG

begintime这个字段是你的动态请求中第一条动态的上一条动态的发布时间的时间戳。

g_tk这个字段是在jQuery中加密的字段。

jQuery中加密代码如下:

getACSRFToken:function(url) {
    url = QZFL.util.URI(url);
    var skey;
    if (url) {
      if (url.host && url.host.indexOf("qzone.qq.com") > 0) {
        try {
          skey = QZONE.FP._t.QZFL.cookie.get("p_skey");
        } catch (err) {
          skey = QZFL.cookie.get("p_skey");
        }
      } else {
        if (url.host && url.host.indexOf("qq.com") > 0) {
          skey = QZFL.cookie.get("skey");
        }
      }
    }
    if (!skey) {
      skey = QZFL.cookie.get("p_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.charCodeAt(i);
    }
    return hash & 2147483647;

为了获取g_tk,首先是要获取登陆过后的cookies

代码如下:

# 获取cookie为字典形式
cookie_dict = {i['name']: i['value'] for i in driver.get_cookies()}
# 把cookie转化为字符串形式:name1=value1; name2=value2;
cookie_str = ''
for key, value in cookie_dict.items():
    cookie_str += key + '=' + value + '; '

用python实现的加密代码如下:

# -*- coding: UTF-8 -*-
import re


class GetGTK(object):
    def __init__(self, cookiestr):
        self.cookieStr = cookiestr
        self.p_skey = None
        self.skey = None
        self.rv2 = None

    def getNewGTK(self):
        skey = self.p_skey or self.skey or self.rv2
        hash = 5381
        for i in range(0, len(skey)):
            hash += (hash << 5) + ord(skey[i])
        return hash & 2147483647

    def handler(self):
        if re.search(r'p_skey=(?P<p_skey>[^;]*)', self.cookieStr):
            self.p_skey = re.search(r'p_skey=(?P<p_skey>[^;]*)', self.cookieStr).group('p_skey')
        else:
            self.p_skey = None
        if re.search(r'skey=(?P<skey>[^;]*)', self.cookieStr):
            self.skey = re.search(r'skey=(?P<skey>[^;]*)', self.cookieStr).group('skey')
        else:
            self.skey = None
        if re.search(r'rv2=(?P<rv2>[^;]*)', self.cookieStr):
            self.rv2 = re.search(r'rv2=(?P<rv2>[^;]*)', self.cookieStr).group('rv2')
        else:
            self.rv2 = None

    def run(self):
        self.handler()
        return self.getNewGTK()


if __name__ == '__main__':
    cookiestr = "cookies" # 这是你的登陆后的cookie 
    getGTK = GetGTK(cookiestr)
    g_tk = getGTK.run()
    print(g_tk)

获取begintime

代码如下:

basetime = driver.find_elements_by_xpath('//*[@id="feed_friend_list"]//li[@class="f-single f-s-s"]').pop().get_attribute(
    'id').split('_')[4]

获取begintime可以直接在id里面获取,id中包含了发布动态的时间戳。

6.PNG

有了begintimeg_tk后,我们就可以组装url了,然后就可以用requests加上cookies信息请求url,就可以获取到空间好友动态了。

# 构造url
url = 'https://user.qzone.qq.com/proxy/domain/ic2.qzone.qq.com/cgi-bin/feeds/feeds3_html_more?uin=1392853401&begintime={}&g_tk={}'.format(begintime, g_tk)

# 发起请求
res = requests.get(base_url, cookies=cookie_dict)
print(res.content.decode())

获取的结果如下

7 .PNG

再在浏览器中请求这个url,得到结果如下

8.PNG

发现用代码抓取的空间动态信息正确,接下来就是用一般的数据处理方法来清洗数据(xpath,re,或者beautifulsoup),要注意的是构造下一个请求的begintime要用到上一个请求结果中最后一条消息的发布时间的时间戳。例如下图中最后一个动态的发布时间戳为1563797364

9.PNG

下一个Ajax请求的begintime就是1563797364

10

这样就可以构造连续的请求来获取空间好友动态消息。

最后附上源代码:

from selenium import webdriver
import time
import requests
# 导入密钥构造类
from get_g_tk import GetGTK
from lxml import etree
import demjson
import pymongo

myclient = pymongo.MongoClient('mongodb://localhost:27017/')
mydb = myclient['QQDongTaiInfo']
mycollection = mydb['QQDongTaiInfo']


class GetQQDongTaiInfo(object):
    chrome_driver = r'E:\迅雷下载\chromedriver_win32\chromedriver.exe'

    def __init__(self, username, password):
        self.driver = webdriver.Chrome(executable_path=GetQQDongTaiInfo.chrome_driver)
        self.cookies = {}
        self.username = username
        self.password = password
        self.base_url = 'https://user.qzone.qq.com/proxy/domain/ic2.qzone.qq.com/cgi-bin/feeds/feeds3_html_more?uin={}&begintime={}&g_tk={}'
        # g_tk为jquery中加密的字段,用登陆的cookie信息进行加密
        self.g_tk = None
        self.begintime = None

    def login_qq_zone(self):
        self.driver.get('https://qzone.qq.com/')

        # 切换网页框架
        self.driver.switch_to.frame(self.driver.find_element_by_id('login_frame'))

        # 切换到账户密码输入界面
        self.driver.find_element_by_id('switcher_plogin').click()

        # 输入账号
        self.driver.find_element_by_id('u').clear()
        self.driver.find_element_by_id('u').send_keys(self.username)

        # 输入密码
        self.driver.find_element_by_id('p').clear()
        self.driver.find_element_by_id('p').send_keys(self.password)

        # 登陆账号
        self.driver.find_element_by_id('login_button').click()
        time.sleep(3)
        self.driver.find_element_by_xpath('//*[@id="tab_menu_friend"]/div[3]').click()
        time.sleep(3)
        self.cookies = {i['name']: i['value'] for i in self.driver.get_cookies()}

    def get_static_html_info(self):
        page_source = self.driver.page_source
        self.begintime = self.driver.find_elements_by_xpath(
            '//*[@id="feed_friend_list"]//li[@class="f-single f-s-s"]').pop().get_attribute(
            'id').split('_')[4]
        html = etree.HTML(page_source)
        # 获取静态网页中的动态消息
        dongtai_contents = html.xpath('//li[@class="f-single f-s-s"]')
        # print(dongtai_content)
        single_info = dict()
        for temp in dongtai_contents:
            # 动态内容
            single_info['content'] = temp.xpath(".//div[starts-with(@id,'feed_')]/div[@class='f-info']/text()")
            # print(single_info['content'])
            # 动态发布者名称
            single_info['publisher_name'] = temp.xpath(".//a[contains(@class,'f-name')]/text()")
            # 动态发布时间戳
            single_info['push_date'] = temp.xpath(".//*[starts-with(@id,'hex_')]/i/@data-abstime")
            # 动态浏览次数
            single_info['view_count'] = temp.xpath(".//a[contains(@class,'state qz_feed_plugin')]/text()")
            # 动态评论
            single_info['comments-content'] = temp.xpath(".//div[@class='comments-content']//text()")
            # 点赞次数
            # print(temp.xpath(".//span[@class='f-like-cnt']/text()"))
            single_info['like'] = temp.xpath(".//span[@class='f-like-cnt']/text()")
            # print(single_info)
            self.save_to_mongdb(single_info)
        self.cookies = {i['name']: i['value'] for i in self.driver.get_cookies()}
        cookie_str = ''
        for key, value in self.cookies.items():
            cookie_str += key + '=' + value + '; '
        self.g_tk = GetGTK(cookie_str).run()

    def get_dynamic_info(self):

        requests_url = self.base_url.format(self.username, self.begintime, self.g_tk)
        print(requests_url)
        res = requests.get(requests_url, cookies=self.cookies).content.decode()
        res_dict = demjson.decode(res[10: -3])
        # 如果没有请求到正确数据,再次发出请求
        try:
            res_datas = res_dict['data']['data']
        except KeyError:
            self.get_dynamic_info()
            return None
      
        res_datas = [temp for temp in res_datas if isinstance(temp, dict)]
        # res_datas_len = len(res_datas)
        for temp in res_datas:
            single_info = dict()
            html = etree.HTML(temp['html'])
            # 动态内容
            single_info['content'] = html.xpath("//div[@class='f-info']/text()")
            # print(single_info['content'])
            # 动态发布者名称
            single_info['publisher_name'] = temp['nickname']
            # 动态发布时间戳
            single_info['push_date'] = temp['abstime']
            # 动态浏览次数
            single_info['view_count'] = html.xpath("//a[@class='state qz_feed_plugin']/text()")
            # 动态评论
            single_info['comments-content'] = html.xpath("//div[@class='comments-content']//text()")
            # 点赞次数
            # print(temp.xpath(".//span[@class='f-like-cnt']/text()"))
            single_info['like'] = html.xpath(".//span[@class='f-like-cnt']/text()")
            # print(single_info)
            self.save_to_mongdb(single_info)
            if temp == res_datas[-1]:
                self.begintime = single_info['push_date']
                self.get_dynamic_info()

    def save_to_mongdb(self, single_info):
        if mycollection.find({'push_date': single_info['push_date']}).count() == 0:
            mycollection.insert_one(single_info.copy())
            print('插入成功')
        else:
            print('插入失败')

    def run(self):
        self.login_qq_zone()
        self.get_static_html_info()
        self.get_dynamic_info()


if __name__ == "__main__":
    username = '***' # qq账号
    password = '***' # qq密码
    Demo = GetQQDongTaiInfo(username, password)
    Demo.run()

结果保存在了mongdb数据库中,结果如下:

12.PNG

以上就是用selenium+python获取qq空间好友动态的全部流程,谢谢浏览。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342