Python使用Cookie字符串发起HTTP请求的几个方法(2)

前篇介绍了直接使用httplib发送Cookie,简洁直观。创建cookielib.CookieJar对象自动管理Cookie稍繁琐一些,但是一旦创建,可供urllib2创建opener,后续的所有cookie更新和过期删除都是自动处理的。

1. 解析Cookie Str,得到Cookie.SimpleCookie对象

我们得到的Request Header中,Cookie是这样的格式:

Cookie: key1=val1; key2=val2; key3=val3

第一步是解析它,得到Cookie.SimpleCookie对象。通过字符串构造即可: Cookie.SimpleCookie(cookie_str)

得到的是一个dict-like对象,它的每一个值都是一个Set-Cookie语句,上述例子解析后是:

from Cookie import SimpleCookie
>>> sc = SimpleCookie('key1=val1; key2=val2; key3=val3')
>>> for c,v in sc.items():
...     print c,v
...
key3 Set-Cookie: key3=val3
key2 Set-Cookie: key2=val2
key1 Set-Cookie: key1=val1

2. 用SimpleCookie创建cooklib.Cookie对象

上述SimpleCookie不能直接使用,因为一个完整的Cookie,还必须包括额外的字段,如:domain、path、expires等。第二步工作是创建cooklib.Cookie对象,直接将key, value传入cooklib.Cookie类的构造函数即可。

得到一系列cookielib.Cookie对象,便可以依次用它们来更新CookieJar了。

3. 完整的示例代码

import cookielib
import Cookie
import urllib2

def build_opener_with_cookie_str(cookie_str, domain, path='/'):
    simple_cookie = Cookie.SimpleCookie(cookie_str)    # Parse Cookie from str
    cookiejar = cookielib.CookieJar()    # No cookies stored yet

    for c in simple_cookie:
        cookie_item = cookielib.Cookie(
            version=0, name=c, value=str(simple_cookie[c].value),
                     port=None, port_specified=None,
                     domain=domain, domain_specified=None, domain_initial_dot=None,
                     path=path, path_specified=None,
                     secure=None,
                     expires=None,
                     discard=None,
                     comment=None,
                     comment_url=None,
                     rest=None,
                     rfc2109=False,
            )
        cookiejar.set_cookie(cookie_item)    # Apply each cookie_item to cookiejar
    return urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))    # Return opener


if __name__ == '__main__':
    cookie_str = 'tLargeScreenP=1; Authorization=Basic%20HereIsMySecret; subType=pcSub; TPLoginTimes=2'
    opener = build_opener_with_cookie_str(cookie_str, domain='192.168.1.253')
   
    html_doc = opener.open('http://192.168.1.253').read()
    import re
    print 'Open With Cookie:', re.search('(.*?)', html_doc, re.IGNORECASE).group(1)

    html_doc = urllib2.urlopen('http://192.168.1.253').read()
    print 'Open Without Cookie:', re.search('(.*?)', html_doc, re.IGNORECASE).group(1)

运行结果:

build-cookiejar-from-str

192.168.1.253是我房间的路由器,第一次使用带Cookie的Opener请求,第二次不带Cookie请求。

Python使用Cookie字符串发起HTTP请求的几个方法(1)

一般,我们HTTP抓包得到的Cookie位于Request Header中,格式如下:

Cookie: key1=val1; key2=val2; key3=val3

要使用该字符串发起HTTP请求,有几种方法:

  1. 使用httplib添加到headers中
  2. 使用cookielib创建CookieJar对象在urllib2中使用
  3. 使用requests模块

它们各有不足:

  1. httplib不会自动管理cookie(不会在后续的请求中自动添加、更新、删除过期)
  2. cookielib的处理过程稍复杂
  3. requests需要另安装

使用httplib直接添加cookie到header

如果不需要程序自己管理cookie,这个方法是极简单的。下面简单示例处理GET请求:

import httplib
import urlparse

def request(url, cookie=''):
    ret = urlparse.urlparse(url)    # Parse input URL
    if ret.scheme == 'http':
        conn = httplib.HTTPConnection(ret.netloc)
    elif ret.scheme == 'https':
        conn = httplib.HTTPSConnection(ret.netloc)
        
    url = ret.path
    if ret.query: url += '?' + ret.query
    if ret.fragment: url += '#' + ret.fragment
    if not url: url = '/'
    
    conn.request(method='GET', url=url , headers={'Cookie': cookie})
    return conn.getresponse()

if __name__ == '__main__':
    cookie_str = 'tLargeScreenP=1; Authorization=Basic%20HereIsMySecret; subType=pcSub; TPLoginTimes=2'
    url = 'http://192.168.1.253'
    html_doc = request(url, cookie_str).read()
    import re
    print 'With Auth:', re.search('(.*?)', html_doc, re.IGNORECASE).group(1)

    html_doc = request(url).read()
    print 'Without Auth:', re.search('(.*?)', html_doc, re.IGNORECASE).group(1)

request函数就是全部的内容,测试IP 192.168.1.253是我房间的无线路由器。第一次HTTP请求带Cookie,第二次不带Cookie,返回的标题不一样:

httplib-cookie

python获取路由器IP

之前测试某个路由器漏洞时,写了一小段python脚本查找路由器IP。

实际上可以认为是端口扫描,程序只是粗略地检查是否开放了80端口。

import socket
import threading

routers = []
lock = threading.Lock()

def search_routers(): 
    routers = []
    local_ips = socket.gethostbyname_ex(socket.gethostname())[2]    # get local IP
    all_threads = []
    for ip in local_ips:
        for i in range(1, 255):
            array = ip.split('.')
            array[3] = str(i)
            new_ip = '.'.join(array)
            t = threading.Thread(target=check_ip, args=(new_ip,) )
            t.start()
            all_threads.append(t)
    for t in all_threads:
        t.join()

def check_ip(new_ip):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)
    result = s.connect_ex((new_ip, 80))
    s.close()
    if result == 0:
        lock.acquire()
        print new_ip.ljust(15), ' port 80 is open'
        routers.append((new_ip, 80))
        lock.release()

print 'Searching for routers, please wait...'
search_routers()

timeout可以设置成1秒或2秒。local_ips是获取多块网卡上绑定的IP,比如我的IP地址是192.168.1.4和192.168.56.1。而代码所做的事情就是扫描 [192.168.1.1 ~ 192.168.1.254]  [192.168.56.1 ~ 192.168.56.254] 有哪些IP开放80端口。

在我的PC上运行结果:

get-router-ips

其中192.168.1.1和192.168.1.253是我的无线modem和无线路由器。

一个python的组合题目

帮同学做了道题,附上自己的解答。

他的题目是:

有a,b两序列,大小相同,序列中元素均为正整数,无序。

要求实现:通过交换a,b中的元素,使得 [序列a所有元素的和] 与 [序列b所有元素的和] 之间的差值最小。

这是一个组合问题,我们需要从一个列表里取出一半的元素,然后让这些元素的和最接近原始和的一半。

对于排列组合,python提供了高效迭代器,位于itertools模块。 可以直接暴力枚举。

#coding:utf-8
import itertools

def swap_item(a, b):
    lst = a + b    # 合并两个列表
    min_diff = sum(lst)
    half_sum = min_diff / 2    # 原始和的一半

    for item in itertools.permutations(lst, len(lst)/2):
        diff = abs(half_sum - sum(item))
        if diff < min_diff:
            min_diff = diff
            result = list(item)
            if diff == 0: break    # 如果正好是原始和的一半,即最佳方案
    in_lst = []
    out_lst = []
    for item in set(a + result):
        a_num = a.count(item)
        result_num = result.count(item)
        if a_num < result_num:    # 列表A添加该元素
            in_lst.append(item * (result_num - a_num))
        elif a_num > result_num:    # 列表A移除该元素
            out_lst.append(item * (a_num - result_num))
            
    print 'A ----->'.ljust(10), '<----- B'.rjust(9)
    for x,y in zip(out_lst, in_lst):
        print str(x).ljust(10), str(y).rjust(9)
    print '\nFinal result is', result

a = [19, 90, 1, 2, 3, 52, 11]
b = [6, 66, 21, 45, 32, 17, 43]
swap_item(a, b)

输出结果:
swap-list-items

西工大的猫 v1.3

“西工大的猫” 是在xiaoxia的 sogou代理脚本 基础上加了个壳子,

方便用户一键设置浏览器HTTP代理, 现在每天有几千人使用。

教育网的同学可以用它来免费上网、浏览器提速,国外的同学可以用它浏览内地视频网站等。

 

昨天用python、Tkinter、winIco简单实现了这个工具,

ballon tip是自己用Toplevel实现的,3.5秒内会自动淡出直至消失。  popup menu和ballon的位置只是粗略计算,最终显示效果不是特别理想。

 

源代码      Windows可执行文件

在VPS上给自己的人人网账号刷”人品”

人人网新出了一个增加用户粘性的类似积分制度,叫”人品”。

每半个小时刷新一下人人网,就可以得到1RP,而得到的RP,可以兑换一些小礼品。

这类似于挂机, 如果你已经有VPS,写一段python脚本跑起来就可以全天刷了。

#encoding:utf-8
import httplib
import time
import datetime

headers = {
    'Cookie': '你的Cookie',
    'Referer': '自行修改Referer',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) \
    Chrome/31.0.1650.48 Safari/537.36'
}

while True:
    conn = httplib.HTTPConnection('www.renren.com')
    conn.request(method='GET', url='/你的人人网ID', headers=headers)
    html_doc = conn.getresponse().read().decode('utf-8')
    
    with open('renpin.log', 'a') as outFile:
        if html_doc.find(u'class="logout">退出') > 0:
            outFile.write('OK ' + str(datetime.datetime.now()) + '\n' )
        else:
            outFile.write('Fail ' +  str(datetime.datetime.now()) + '\n' )
    conn.close()
    time.sleep(600)

获取人人网的cookie,可以直接在Chrome浏览器Ctrl + Shift + J打开控制台,刷新当前页面,在Network选项卡查看对应的URL条目即可。

把这段代码保存到renpin.py,放到VPS上某个文件夹,然后执行

python renpin.py &

就可以全天24小时,每隔10分钟自动刷新人人了。 程序还会在当前路径下写入日志到renpin.log。