Python2 urllib模块和urllib2模块的区别

urllib 和urllib2都是接受URL请求的相关模块,但是urllib2可以接受一个Request类的实例来设置URL请求的headers;urllib仅可以接受URL,这意味着,你不可以伪装你的User Agent字符串等。

urllib提供urlencode方法用来GET查询字符串的产生,而urllib2没有。这是为何urllib常和urllib2一起使用的原因。

urllib.urlretrieve函数以及urllib.quote等一系列quote和unquote功能没有被加入urllib2中,因此有时也需要urllib的辅助。

quote(),unquote(),quote_plus(),unquote_plus(),urlencode() ,pathname2url(),url2pathname()

urllib中还提供了一些辅助方法,用于对url进行编码、解码。url中是不能出现一些特殊的符号的,有些符号有特殊的用途。我们知道以get方式提交数据的时候,会在url中添加key=value这样的字符串,所以在value中是不允许有'=',因此要对其进行编码;与此同时服务器接收到这些参数的时候,要进行解码,还原成原始的数据。这个时候,这些辅助方法会很有用:

urllib.quote(string[, safe]):对字符串进行编码。参数safe指定了不需要编码的字符;
urllib.unquote(string) :对字符串进行解码;

urllib.quote_plus(string [ , safe ] ) :与urllib.quote类似,但这个方法用'+'来替换' ',而quote用'%20'来代替' '
urllib.unquote_plus(string ) :对字符串进行解码;

urllib.urlencode(query[, doseq]):将dict或者包含两个元素的元组列表转换成url参数。例如 字典{'name': 'dark-bull', 'age': 200}将被转换"name=dark-bull&age=200"

urllib.pathname2url(path):将本地路径转换成url路径;
urllib.url2pathname(path):将url路径转换成本地路径;

Python3 urllib

python3对urllib和urllib2进行了重构,拆分成了urllib.request,urllib.response, urllib.parse, urllib.error等几个子模块,这样的架构从逻辑和结构上说更加合理。urllib库无需安装,python3自带。python 3.x中将urllib库和urilib2库合并成了urllib库。

更多python 2 python 3版本的urllib区别,参考:python2&3.html#urllib

urllib2.urlopen() 变成了 urllib.request.urlopen()

urllib2.Request() 变成了 urllib.request.Request()

python2中的 cookielib 改为 http.cookiejar
import http.cookiejar 代替 import cookielib

urljoin 现在对应的函数是 urllib.parse.urljoin
import urllib.parse,urllib.request
data = 'name = ~a+3'

data1 = urllib.parse.quote(data)
print(data1)  # result: name%20%3D%20%7Ea%2B3
print(urllib.parse.unquote(data1))  # result: name = ~a+3

data2 = urllib.parse.quote_plus(data)
print(data2)  # result: name+%3D+%7Ea%2B3
print(urllib.parse.unquote_plus(data2))  # result: name = ~a+3

data3 = urllib.parse.urlencode({'name': 'dark-bull', 'age': 200})
print(data3)  # result: age=200&name=dark-bull

data4 = urllib.request.pathname2url(r'd:/a/b/c/23.php')
print(data4)  # result: ///D|/a/b/c/23.php
print(urllib.request.url2pathname(data4))  # result: D:/a/b/c/23.php

urllib + urllib2 的 POST GET 写法

import urllib
response = urllib.urlopen('http://127.0.0.1/')
the_page = response.read()

POST:
import urllib
import urllib2

url = 'http://www.someserver.com/cgi-bin/register.cgi'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'# 将user_agent写入头信息
values = {'name' : 'who','password':'123456'}
headers = { 'User-Agent' : user_agent }
data = urllib.urlencode(values)
request = urllib2.Request(url, data, headers)
response = urllib2.urlopen(request)
the_page = response.read()

GET:
import urllib
import urllib2

url = 'http://www.baidu.com/s'
values = {'wd':'D_in'}
data = urllib.urlencode(values)
geturl = url + "?" + data
request = urllib2.Request(geturl)
response = urllib2.urlopen(request)
print response.read()
response.close()

#post data可以直接送字符串,特别是有特殊符号时
post data = '{"stockIds":[30],"startDate":"2015-05-15T09:05:34.501Z","endDate":"2020-05-15T09:05:34.501Z","granularities":["q"],"expressionCaculateTypes":["t"]}'

获取远程文件

# python2
import urllib
urllib.urlretrieve(url, filename)

# python3
import urllib.request
urllib.request.urlretrieve(url, filename)

默认的是FileCookieJar没有实现save函数。
而MozillaCookieJar或LWPCookieJar都已经实现了。
所以可以用MozillaCookieJar或LWPCookieJar,去自动实现cookie的save。
建议用LWPCookieJar,其保存的cookie,易于人类阅读

FileCookieJar(filename)
创建FileCookieJar实例,检索cookie信息并将信息存储到文件中,filename是文件名。
MozillaCookieJar(filename)
创建与Mozilla cookies.txt文件兼容的FileCookieJar实例。
LWPCookieJar(filename)
创建与libwww-perl Set-Cookie3文件兼容的FileCookieJar实例。

另外,补充一下urllib2的方法:

1、geturl():

这个返回获取的真实的URL,这个很有用,因为urlopen(或者opener对象使用的)或许会有重定向。获取的URL或许跟请求URL不同。
URL重定向(URL redirection,或称网址重定向或网域名称转址),是指当使用者浏览某个网址时,将他导向到另一个网址的技术。常用在把一串很长的网站网址,转成较短的网址。因为当要传播某网站的网址时,常常因为网址太长,不好记忆;又有可能因为换了网路的免费网页空间,网址又必须要变更,不知情的使用者还以为网站关闭了。这时就可以用网路上的转址服务了。这个技术使一个网页是可借由不同的统一资源定位符(URL)连结。

>>> import urllib2
>>> url = "http://www.baidu.com"
>>> req = urllib2.Request(url)
>>> response = urllib2.urlopen(req)
>>> response.geturl()
'http://www.baidu.com'
>>> print response.info()
Date: Fri, 28 Mar 2014 03:30:01 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: Close
Vary: Accept-Encoding
Set-Cookie: BAIDUID=AF7C001FCA87716A52B353C500FC45DB:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: H_PS_PSSID=1466_5225_5288_5723_4261_4759_5659; path=/; domain=.baidu.com
P3P: CP=" OTI DSP COR IVA OUR IND COM "
Expires: Fri, 28 Mar 2014 03:29:06 GMT
Cache-Control: private
Server: BWS/1.1
BDPAGETYPE: 1
BDQID: 0xea1372bf0001780d
BDUSERID: 0

2、默认情况下会针对 HTTP 3XX 返回码自动进行 redirect 动作(URL重定向),无需人工配置。要检测是否发生了 redirect 动作,只要检查一下 Response 的 URL 和 Request 的 URL 是否一致就可以了。

import urllib2
my_url = 'http://www.google.cn'
response = urllib2.urlopen(my_url)
redirected = response.geturl() == my_url
print redirected

my_url = 'http://rrurl.cn/b1UZuP'
response = urllib2.urlopen(my_url)
redirected = response.geturl() == my_url
print redirected

3、Debug Log
使用 urllib2 时,可以通过下面的方法把 debug Log 打开,这样收发包的内容就会在屏幕上打印出来,方便调试,有时可以省去抓包的工作

import urllib2
httpHandler = urllib2.HTTPHandler(debuglevel=1)
httpsHandler = urllib2.HTTPSHandler(debuglevel=1)
opener = urllib2.build_opener(httpHandler, httpsHandler)
urllib2.install_opener(opener)
response = urllib2.urlopen('http://www.google.com')

requests

GET:

import requests

def get_page():
    url = 'http://cip.cc/'
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36',
    }
    params = {
        'query': word
    }
    response = requests.get(url, headers=headers, params=params)
    return response

response = get_page()
print(response.text)
print(response.status_code)

POST:

import requests
import json

def get_page():
    url = 'https://www.xxx.com/api'
    headers = {
        'Content-Type': "application/json",
        # 'Content-Type': "application/x-www-form-urlencoded",
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36',
    }
    data = {
        'page': '1',
        'keywords': '2'
    }
    response = requests.post(url=url, headers=headers, data=json.dumps(data))
    # response = requests.post(url=url, headers=headers, data=data)
    return response

print(response.text)
print(response.status_code)

response_json = json.loads(response.text)
response_data = (response_json['data'])

requests.session

在进行接口测试的时候,我们会调用多个接口发出多个请求,在这些请求中有时候需要保持一些共用的数据,例如cookies信息。

1、requests库的session对象能够帮我们跨请求保持某些参数,也会在同一个session实例发出的所有请求之间保持cookies。

s = requests.session()
req_param = '{"belongId": "300001312","userName": "alitestss003","password":"pxkj88","captcha":"pxpx","captchaKey":"59675w1v8kdbpxv"}'
res = s.post('http://test.e.fanxiaojian.cn/metis-in-web/auth/login', json=json.loads(req_param))
# res1 = s.get("http://test.e.fanxiaojian.cn/eos--web/analysis/briefing")
print(res.cookies.values())   获取登陆的所有session

2、requests库的session对象还能为我们提供请求方法的缺省数据,通过设置session对象的属性来实现
eg:

# 创建一个session对象
s = requests.Session()

# 设置session对象的auth属性,用来作为请求的默认参数
s.auth = ('user', 'pass')

# 设置session的headers属性,通过update方法,将其余请求方法中的headers属性合并起来作为最终的请求方法的headers
s.headers.update({'x-test': 'true'})

# 发送请求,这里没有设置auth会默认使用session对象的auth属性,这里的headers属性会与session对象的headers属性合并
r = s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})

上面的请求数据等于:{'Authorization': 'Basic dXNlcjpwYXNz', 'x-test': 'false'}

# 查看发送请求的请求头
r.request.headers      #打印响应中请求的所有header数据

res3 = s.get("http://pre.n.cn/irs-web/sso/login",cookies = cookie)
print(res3.request.headers.get("Cookie").split("IRSSID=")[-1])
print(type(res3.request.headers.get("Cookie").split("IRSSID=")[-1]))
print(res3.request._cookies)

访问Https时不受信任SSL证书问题

# 问题:(python2: urllib2)
URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)>

# 问题:(python3: urllib.request)
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)>

# 解决方法:
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

# 问题:(requests)
SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:661)

# 解决方法:
request = requests.get(url)
request = requests.get(url, verify=False)

SSLV3 警报握手失败

# 问题:(urllib2)
URLError: <urlopen error [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:661)>

# 解决方法:
import ssl
ssl._DEFAULT_CIPHERS = 'ALL'

# 问题:(requests)
SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:661)

# 解决方法:
import requests.packages.urllib3.util.ssl_
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'ALL'

SSL文件:

# urllib2
"C:\Python27\Lib\ssl.py"

# requests
"C:\Python27\Lib\site-packages\urllib3\util\ssl_.py"

问题:(警告)

Warning (from warnings module):
  File "C:\Python27\lib\site-packages\urllib3\connectionpool.py", line 852
    InsecureRequestWarning)
InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings

# 解决方法:
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

向服务器请求gzip压缩数据格式,并解压缩数据

headers = {"Accept-Encoding": "gzip, deflate"}

# python2

import urllib2, httplib
request = urllib2.Request(jsonpUrl,None,values)
response = urllib2.urlopen(request)
jsonpPage = response.read()
response.close()

#解压缩
import StringIO, gzip
compressedstream = StringIO.StringIO(jsonpPage)
gziper = gzip.GzipFile(fileobj=compressedstream)
jsonpPage = gziper.read()
return jsonpPage

# python3

import urllib.request as request
r = request.Request(url,None,self.headers )
response = request.urlopen(r)
page = response.read()

#print(page[0:3])   # b'\x1f\x8b\x08'   即为gzip压缩

#解压缩

# 方式1:
import gzip
page = gzip.decompress(page)

# 方式2:
import gzip
from io import BytesIO

buff = BytesIO(page)
f = gzip.GzipFile(fileobj=buff)
page = f.read()

详解python requests中的post请求的参数问题

1.http请求中Form Data和Request Playload的区别:

Ajax post请求中常用的两种参数形式:form data 和 request payload

get请求的时候,我们的参数直接反映在url里面,为key1=value1&key2=value2形式,如果是post请求,那么表单参数是在请求体中,也是以key1=value1&key2=value2的形式在请求体中。通过chrome的开发者工具可以看到如下

a.Form Data类型:

这种情况有两种处理方式,一个是把这个post请求变成get请求,即把请求参数通过"?key1=value1&key2=value2"拼接在url当中,然后以get方式请求就可以了:response = requests.get(url,headers=headers),其中url为拼接的url

另一种是仍然用post请求,将参数放在data中:response = requests.post(url,headers=headers,data=data),其中url为post url.

注意上图中红圈内的内容,这里请求头中的Content-Type为application/x-www-form-urlencoded,点击Form Data旁边的view source,变成了key1=value1&key2=value2形式,如下图,这时你可以将这部分内容加到post的Request URL后面使它变成一个get请求,就可以获取到数据了。

b.Request Payload类型

同样在chrome浏览器开发者工具中可以看到它们不同的地方,Content-Type为application/json,表明需要传的是一个json类型的对象,点击view source,如下图

自动变成了json类型,这时必须发post请求,将这json对象传入才可获取数据。形如:

response = requests.post(url, headers=headers, data=json.dumps(data))
response = requests.post(url, headers=headers, json=data)

相比普通的post,这个post参数中的data一定要序列化才行。

requests 代理

import requests

def get_page():

    url = 'http://cip.cc/'
    headers = {
        #"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"
        "User-Agent": "curl/7.55.1"
    }
    params = {
    }
    response = requests.get(url, headers=headers, params=params, proxies={"http": "106.54.128.253:999"})
    return response

response = get_page()
print(response.text)

快代理:
https://www.kuaidaili.com/free/inha/

厂商名称 地址
66代理 http://www.66ip.cn/
西刺代理 https://www.xicidaili.com
全网代理 http://www.goubanjia.com
云代理 http://www.ip3366.net
IP海 http://www.iphai.com
快代理 https://www.kuaidaili.com
免费代理IP库 http://ip.jiangxianli.com
小幻代理 https://ip.ihuan.me/

注意:如果请求的ip是https类型的,但代理的ip是只支持http的,那么还是使用本机的ip,
如果请求的ip是http类型的,那么代理的ip一定要是http的,前面不能写成https,否则使用本机IP地址

python requests 的编码问题

通常情况下:
response = requests.get("http://www.xxx.com")
response_text = response.text

response.text    返回的是Unicode型的数据,文本数据用
response.content 返回的是bytes型的数据,图片等数据用

有部分网页是utf-8编码,但text却是乱码,解决办法:
response = requests.get("http://www.xxx.com")
response.encoding = 'utf-8'
response_text = response.text