• 隐藏侧边栏
  • 展开分类目录
  • 关注微信公众号
  • 我的GitHub
  • QQ:1753970025
Chen Jiehua

爬虫之模拟用户登录 

爬虫经常会碰到的一个问题就是获取目标网页的内容需要用户登录,而一贯的做法就是模拟用户登录,获取cookie后再抓取网页内容。不过,今天让我们来另辟蹊径……

原理

如果我们没有太多的时间精力来模拟用户登录(那啥,产品经理说:这个需求很简单,怎么实现我不管,明天上线!),又或者用户登录步骤过于复杂(各种加密、验证问题),我们有什么方法可以快速略过这一步而不影响后续网页内容抓取呢?没错,直接复用浏览器的Cookie!

我们在浏览器中访问网页后正常登录,浏览器保存的cookie显然是保存在本地文件中,只要我们读取该文件的内容附加到http请求上,一切就大功告成啦。

举个栗子

我们以爬取 优酷自频道 排行榜为例,在Ubuntu下使用Google Chrome为辅助。

直接打开上面的链接,发现自动跳转到了用户登录页面,使用优酷账号正常登陆后,在chrome的开发者面板即可看到相应的cookie信息。

Chrome Cookie

默认情况下,Ubuntu的Chrome Cookie存放在 /home/user/.config/google-chrome/Profile/Cookies ,文件格式为 SQLite,表结构大致为:

字段名称字段类型
creation_utcInteger primary key
host_keyText
nameText
valueText
pathText
expires_utcInteger
secureInteger
httponlyInteger
last_access_utcInteger
has_expiresInteger
persistentInteger
priorityInteger
encrypted_valueBlob
firstpartyonlyInteger

主要字段 host_key, name, value, path, encrypted_value. 我们简单看一下数据:

def get_cookie():
    cookie_path = os.path.join(os.getenv("HOME"), ".config/google-chrome/Default/Cookies")
    with sqlite3.connect(cookie_path) as conn:
        cur = conn.cursor()
        sql = "select host_key, name, value, path, encrypted_value from cookies where host_key = ?"
        cur.execute(sql, (".youku.com",))
        rows = cur.fetchall()
        print "total cookies: ", len(rows)
        for row in rows:
            print row

结果发现value为空,chrome cookie进行加密保存在 encrypted_value 字段:

total cookies:  7
(u'.youku.com', u'__ysuid', u'', u'/', <read-write buffer ptr 0x7fa32c4e1f10, size 35 at 0x7fa32c4e1ed0>)
(u'.youku.com', u'__yscnt', u'', u'/', <read-write buffer ptr 0x7fa329f44858, size 19 at 0x7fa329f44818>)
(u'.youku.com', u'__ayvstp', u'', u'/', <read-write buffer ptr 0x7fa329f44d28, size 19 at 0x7fa329f44ce8>)
(u'.youku.com', u'juid', u'', u'/', <read-write buffer ptr 0x7fa329f44d80, size 19 at 0x7fa329f44d40>)
(u'.youku.com', u'cna', u'', u'/', <read-write buffer ptr 0x7fa32c4e12e0, size 35 at 0x7fa32c4e12a0>)
(u'.youku.com', u'l', u'', u'/', <read-write buffer ptr 0x7fa329f5cca0, size 51 at 0x7fa329f5cc60>)
(u'.youku.com', u'isg', u'', u'/', <read-write buffer ptr 0x7fa32b7293a0, size 67 at 0x7fa32b729360>)

Cookie解密

参考:https://www.reddit.com/r/netsec/comments/39swuj/key_for_chromiums_encrypted_cookies_store_in/

Some more details from the source: Password is: “peanuts” Salt is: “saltysalt” The number of KDF iterations is: 1 Algorithm: AES-128-CBC [Edit] Clarify that no. of iterations is for the Key Derivation Function

因此我们可以进行解密:

from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2

def decrypt(encrypted_value):
    encrypted_value = encrypted_value[3:]
    salt = "saltysalt"
    length = 16
    my_pass = "peanuts"
    iterations = 1
    key = PBKDF2(my_pass, salt, length, iterations)
    iv = " " * 16
    cipher = AES.new(key, AES.MODE_CBC, IV=iv)
    decrypted = cipher.decrypt(encrypted_value)
    return decrypted


def clean(x):
    return x[:-ord(x[-1])].decode('utf8')

另外,在MacOS、Windows下Chrome cookie的加密方式稍有区别,具体可以参考 browsercookie源码。

Browsercookie

看了上面的实现过程,是不是觉得麻烦?OK,直接使用现成的第三方库 browsercookie,它已经把上面的cookie解密实现了:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import requests
import browsercookie


def main():
    cookiejar = browsercookie.chrome()
    url = "http://www.youku.com/u/channelRanking?channel=0&rank=0&pn=1&ajaxget=1"
    r = requests.get(url, cookies=cookiejar)
    r.raise_for_status()
    print r.text()


if __name__ == "__main__":
    main()

另外,browsercookie也可以直接读取Firefox的Cookie,使用方法类似Chrome:

cookiejar = browsercookie.firefox()

 

搞完收工,今晚是不是可以早点下班啦!

码字很辛苦,转载请注明来自ChenJiehua《爬虫之模拟用户登录》

评论