爬虫之模拟用户登录
目录
爬虫经常会碰到的一个问题就是获取目标网页的内容需要用户登录,而一贯的做法就是模拟用户登录,获取cookie后再抓取网页内容。不过,今天让我们来另辟蹊径……
原理
如果我们没有太多的时间精力来模拟用户登录(那啥,产品经理说:这个需求很简单,怎么实现我不管,明天上线!),又或者用户登录步骤过于复杂(各种加密、验证问题),我们有什么方法可以快速略过这一步而不影响后续网页内容抓取呢?没错,直接复用浏览器的Cookie!
我们在浏览器中访问网页后正常登录,浏览器保存的cookie显然是保存在本地文件中,只要我们读取该文件的内容附加到http请求上,一切就大功告成啦。
举个栗子
我们以爬取 优酷自频道 排行榜为例,在Ubuntu下使用Google Chrome为辅助。
直接打开上面的链接,发现自动跳转到了用户登录页面,使用优酷账号正常登陆后,在chrome的开发者面板即可看到相应的cookie信息。
Chrome Cookie
默认情况下,Ubuntu的Chrome Cookie存放在 /home/user/.config/google-chrome/Profile/Cookies ,文件格式为 SQLite,表结构大致为:
字段名称 | 字段类型 |
creation_utc | Integer primary key |
host_key | Text |
name | Text |
value | Text |
path | Text |
expires_utc | Integer |
secure | Integer |
httponly | Integer |
last_access_utc | Integer |
has_expires | Integer |
persistent | Integer |
priority | Integer |
encrypted_value | Blob |
firstpartyonly | Integer |
主要字段 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源码。
看了上面的实现过程,是不是觉得麻烦?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()
搞完收工,今晚是不是可以早点下班啦!
评论