首先Urllib是python内置的HTTP请求库。
包括以下模块:
- urllib.request 请求模块;
- urllib.error 异常处理模块;
- urllib.parse url解析模块;
- urllib.robotparser robots.txt解析模块。
urllib常规发送请求方式
import urllib.parse import urllib.request data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf8') response = urllib.request.urlopen('https://httpbin.org/post', data=data) print(response.read())
运行结果:
b'{ "args": {}, "data": "", "files": {}, "form": { "word": "hello" }, "headers": { "Accept-Encoding": "identity", "Connection": "close", "Content-Length": "10", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "Python-urllib/3.6" }, "json": null, "origin": "47.74.11.227", "url": "https://httpbin.org/post" } '
需要post数据按指定编码格式编码,在使用urlopen时传递data参数即发送post请求,默认不加就是get请求。
设置超时时间:
import urllib.request response = urllib.request.urlopen('http://httpbin.org/get', timeout=1) print(response.read())
b'{ "args": {}, "headers": { "Accept-Encoding": "identity", "Connection": "close", "Host": "httpbin.org", "User-Agent": "Python-urllib/3.6" }, "origin": "36.57.169.42", "url": "https://httpbin.org/get" } '
设置超时时间主要是防止爬取某些页面速度极慢,爬的时间过长造成假死状态,从而影响我们的爬虫性能,所以会用到我们的超时时间。
urlopen打开的是什么类型的数据呢,或者是是什么对象。
import urllib.request response = urllib.request.urlopen('https://www.python.org') print(type(response))
<class 'http.client.HTTPResponse'>
这个对象可以read()方法读取。那么我们都可以读取到那些东西呢。
import urllib.request response = urllib.request.urlopen('https://www.python.org') print(response.status) print(response.getheaders()) print(response.getheader('Server'))
#运行结果 #获取状态码 200 #获取请求头的所有信息 [('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'SAMEORIGIN'), ('x-xss-protection', '1; mode=block'), ('X-Clacks-Overhead', 'GNU Terry Pratchett'), ('Via', '1.1 varnish'), ('Fastly-Debug-Digest', 'a63ab819df3b185a89db37a59e39f0dd85cf8ee71f54bbb42fae41670ae56fd2'), ('Content-Length', '48907'), ('Accept-Ranges', 'bytes'), ('Date', 'Wed, 31 Jan 2018 11:39:52 GMT'), ('Via', '1.1 varnish'), ('Age', '2396'), ('Connection', 'close'), ('X-Served-By', 'cache-iad2150-IAD, cache-hnd18723-HND'), ('X-Cache', 'HIT, HIT'), ('X-Cache-Hits', '6, 11'), ('X-Timer', 'S1517398792.207858,VS0,VE0'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')] #获取服务器 nginx
#获取整个页面的信息 import urllib.request response = urllib.request.urlopen('https://www.python.org') print(response.read().decode('utf-8'))
在一般情况下我们都需要对请求对象进行改装,加上请求头等信息,这时候我们就要自己定制request对象,然后给他加buff。
from urllib import request, parse url = 'https://httpbin.org/post' headers = { 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)', 'Host': 'httpbin.org' } dict = { 'name': 'Germey' } data = bytes(parse.urlencode(dict), encoding='utf8') #自定义构造request包含了自定义请求头 req = request.Request(url=url, data=data, headers=headers, method='POST') response = request.urlopen(req) print(response.read().decode('utf-8'))
{ "args": {}, "data": "", "files": {}, "form": { "name": "Germey" }, "headers": { "Accept-Encoding": "identity", "Connection": "close", "Content-Length": "11", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)" }, "json": null, "origin": "36.57.169.42", "url": "https://httpbin.org/post" }
另外一种加请求头的方式
from urllib import request, parse url = 'https://httpbin.org/post' dict = { 'name': 'Germey' } data = bytes(parse.urlencode(dict), encoding='utf8') req = request.Request(url=url, data=data, method='POST') req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)') response = request.urlopen(req) print(response.read().decode('utf-8'))
结果与上一种请求一定是一模一样的。上面完成了最基本的http请求,如果我们想要完成更高级的http请求,使用代理或者带上cookie值等,那么我们需要使用handler方法,handler有很多种。
import urllib.request # 构建一个HTTPHandler处理器对象,支持处理HTTP的请求 #http_handler = urllib2.HTTPHandler() # 在HTTPHandler增加参数"debuglevel=1"将会自动打开Debug log 模式, # 程序在执行的时候会打印收发包的信息 http_handler = urllib.request.HTTPHandler(debuglevel=1) # 调用build_opener()方法构建一个自定义的opener对象,参数是构建的处理器对象 opener = urllib.request.build_opener(http_handler) request = urllib.request.Request("http://www.baidu.com/") response = opener.open(request) print(response.read().decode('utf8'))
这里我们定制了一个自己的opener,构建一个Handler处理器对象,参数是一个字典类型,包括代理类型和代理服务器IP+PROT。
import urllib.request # 代理开关,表示是否启用代理 proxyswitch = True # proxyswitch = False # 构建一个Handler处理器对象,参数是一个字典类型,包括代理类型和代理服务器IP+PROT httpproxy_handler = urllib.request.ProxyHandler({"http" : "121.41.175.199:80"}) # 构建了一个没有代理的处理器对象 nullproxy_handler = urllib.request.ProxyHandler({}) if proxyswitch: opener = urllib.request.build_opener(httpproxy_handler) else: opener = urllib.request.build_opener(nullproxy_handler) # 构建了一个全局的opener,之后所有的请求都可以用urlopen()方式去发送,也附带Handler的功能 urllib.request.install_opener(opener) request = urllib.request.Request("http://www.baidu.com/") response = urllib.request.urlopen(request) #print response.read().decode("gbk") print(response.read().decode('utf8'))
保存cookie的handler
import http.cookiejar, urllib.request cookie = http.cookiejar.CookieJar() handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open('http://www.baidu.com') for item in cookie: print(item.name+"="+item.value)
#将cookie依次打印出来 BAIDUID=185CCA6964D4660F71AC56C5FD40F293:FG=1 BIDUPSID=185CCA6964D4660F71AC56C5FD40F293 H_PS_PSSID=25641_1460_24565_21125_18559 PSTM=1517404650 BDSVRTM=0 BD_HOME=0
cookie保存成文本文件
import http.cookiejar, urllib.request filename = "cookie.txt" cookie = http.cookiejar.MozillaCookieJar(filename) handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open('http://www.baidu.com') cookie.save(ignore_discard=True, ignore_expires=True)
将本地的cookie读取出来然后使用本地文本里的cookie去访问网址:
import http.cookiejar, urllib.request cookie = http.cookiejar.LWPCookieJar() cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True) handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open('http://www.baidu.com') print(response.read().decode('utf-8'))
urlparse将网址分割成多个部分(也可以说是解析)。
from urllib.parse import urlparse result = urlparse('http://www.baidu.com/index.html;user?id=5#comment') print(type(result), result)
解析的结果:
<class 'urllib.parse.ParseResult'> ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')
#另一种解析 from urllib.parse import urlparse result = urlparse('www.baidu.com/index.html;user?id=5#comment', scheme='https') print(result)
并且url里的scheme优先级更高。
from urllib.parse import urlparse #url里有scheme,然后我们又传入一次,结果是什么呢 result = urlparse('http://www.baidu.com/index.html;user?id=5#comment', scheme='https') print(result)
#url win ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')
#不显示什么字段 from urllib.parse import urlparse result = urlparse('http://www.baidu.com/index.html;user?id=5#comment', allow_fragments=False) print(result)
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5#comment', fragment='')
from urllib.parse import urlparse result = urlparse('http://www.baidu.com/index.html#comment', allow_fragments=False) print(result)
#实际上,这个字段位false那么就减少了一次split,数据还是会存在上一级,但是path之前字段是必须有的 ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html#comment', params='', query='', fragment='')
#反向解析 from urllib.parse import urlunparse data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment'] print(urlunparse(data)) #解析结果还原url http://www.baidu.com/index.html;user?a=6#comment
url拼接
from urllib.parse import urljoin print(urljoin('http://www.baidu.com', 'FAQ.html')) print(urljoin('http://www.baidu.com', 'https://cuiqingcai.com/FAQ.html')) print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html')) print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html?question=2')) print(urljoin('http://www.baidu.com?wd=abc', 'https://cuiqingcai.com/index.php')) print(urljoin('http://www.baidu.com', '?category=2#comment')) print(urljoin('www.baidu.com', '?category=2#comment')) print(urljoin('www.baidu.com#comment', '?category=2'))
from urllib.parse import urlencode params = { 'name': 'germey', 'age': 22 } base_url = 'http://www.baidu.com?' url = base_url + urlencode(params) print(url)
http://www.baidu.com?name=germey&age=22
url编码常用于汉字在url中传输时使用。以上就是urllib库常用的操作了。
随着深入的爬虫学习,后面要接触功能强大的Requests库,Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用。
Requests 允许你发送纯天然,植物饲养的 HTTP/1.1 请求,无需手工劳动。你不需要手动为 URL 添加查询字串,也不需要对 POST 数据进行表单编码。Keep-alive 和 HTTP 连接池的功能是 100% 自动化的,一切动力都来自于根植在 Requests 内部的 urllib3。
官方文档地址:Requests: 让 HTTP 服务人类。