urllib 是一个用来处理网络请求的python标准库,它包含4个模块。
urllib.requests 请求模块,用于发起网络请求
urllib.parse 解析模块,用于解析URL
urllib.error 异常处理模块,用于处理request引起的异常
urllib.robotparse 用于解析robots.txt文件
一. request模块
第一个参数时url 第二个参数时data 第三个参数是超时时间 !没有请求头headers参数
import re
import json
import urllib3
from urllib import request
url = "http://httpbin.org/post"
resp = request.urlopen(url=url, data=b"name=YeLan&age=20")
print(resp.read().decode()) # 读取响应返回的数据,并且响应的数据只能读取一次
print(readline()) # 读取一行,可以用循环读取全部
print(resp.info()) # 获取响应头信息
print(resp.geturl()) # 获取访问的url
print(resp.getcode()) # 返回状态码
# urlopen默认会发送get请求,当传入data参数时,则会发起POST请求。data参数是字节类型、类文件对象或可迭代对象。
# data参数传输的是bytes类型的数据(一般以键值对的形式),以form表单的形式返回
Request对象
urlopen()方法不能添加请求头的参数headers因此需要更强大的Request对象
1.请求头添加
通过urllib发送的请求会有一个默认的Headers: “User-Agent”:“Python-urllib/3.6”,指明请求是由urllib发送的。所以遇到一些验证User-Agent的网站时,需要我们自定义Headers把自己伪装起来。
from urllib import request
url = 'http://www.jianshu.com'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/73.0.3683.86 Safari/537.36",
"User-Agent": "Python-urllib/3.6" 默认的UA
}
referer = {"Referer": "http://httpbin.org"}
# 如果请求头需要携带referer、content-type等,只需要将其更新至请求头即可
headers.update(referer)
req = request.Request(url, headers=headers) # 实例
resp = request.urlopen(req) # 这里的urlopen() 接收一个Request对象也是可以的
Handler不同子类处理不同的需求
ProxyHandler 用于设置代理,默认代理为空
HTTPCookieProcessor 用于处理cookies
HTTPRedirectHandler 用于处理重定向
HTTPBasicAuthHandler 用于管理认证,若一个连接打开时需要认证,那么可以用他来解决认证问题
2.cookie操作
urlopen() 不支持代理、Cookie、https认证(auth); 因此我们需要借助底层的 Handler 处理器对象
from urllib import request
from http import cookiejar
# 1.创建一个cookie对象
cookie = cookiejar.CookieJar()
# 2.创建一个cookie处理器
handler = request.HTTPCookieProcessor(cookie)
# 3.创建Opener对象, 将handler绑定到该对象
opener = request.build_opener(handler)
# 4.使用这个opener来发送请求(其实就是携带cookie)
url = 'http://www.baidu.com'
resp = opener.open(url)
print(resp.info()) # 第一次请求时返回的响应头包含set-cookie
resp = opener.open(url)
print(resp.info()) # 第二次请求时返回的响应头不包含set-cookie;因为第二次请求时已经绑定了上一次请求的cookie了, 相当于浏览器已经认识你了
resp = urlopen(url) # 而用urlopen()无论请求多少次,每次响应的头部都会有set-cookie,说明每次请求都没有携带上次的cookie
print(resp.info) # 与opener.open()方法对比
3.设置代理
理解: 原理其实都是通过opener的open()方法发起请求
from urllib.error import URLError
url = 'http://httpbin.org/ip'
# print(request.urlopen(url).read().decode()) # 该测试网址可以返回我们本机的网址
# {"origin": "123.169.20.111, 123.169.20.111"}
# 1.代理地址
proxy = {
'http': '221.6.32.206:41816',
'https': '221.6.32.206:41816'
}
# 2.创建代理处理器
proxy_handler = request.ProxyHandler(proxy)
# 3.创建opener对象 # 实质都是通过opener发起请求
opener = request.build_opener(proxies)
# 4.发送请求(如果代理ip被封,请求会出现异常)
try:
resp = opener.open(url)
print(resp.read().decode()) # 返回代理ip
except URLError as e:
print('请求错误的原因:%s' % e.reason)
二、parse模块
parse模块是一个工具模块,提供了需要对url处理的方法,用于解析url。
parse模块常用方法: quote unquote urlencode parse_qs
url中只能包含ascii字符,在实际操作过程中,get请求通过url传递的参数中会有大量的特殊字符,例如汉字,那么就需要进行url编码。
from urllib import parse, error, robotparser
result = parse.quote("ie=UTF-8&wd=爬虫") # quote()只接受str类型 将url进行编码
print(result)
print(parse.unquote("%E7%88%AC%E8%99%AB")) # unquote 对url进行解码(反编码)
params = {
"wd": "爬虫",
"username": "YeLan"
}
url = "https://www.baidu.com/s?"
print(url + parse.urlencode(params)) # urlencode()接受字典;对url[url参数]进行编码
param = parse.urlencode(params)
print(parse.parse_qs(param)) # parse_qs()接收字典; 对url进行解码
print(parse.parse_qs('wd=%E7%88%AC%E8%99%AB'))
url = urljoin('https:', '//ke.qq.com/course') # 该方法用来拼接url
===> url = "https://ke.qq.com/course"
三、urllib.error模块
error模块主要负责处理异常,如果请求出现错误,我们可以用error模块进行处理
主要包含URLError和HTTPError
URLError:是error异常模块的基类,由request模块产生的异常都可以用这个类来处理
该异常常用属性: reason 错误的原因
HTTPError:是URLError的子类,主要包含三个属性:
Code:请求的状态码
reason:错误的原因
headers:响应的报头
四、urllib.robotparse模块
该模块的 RobotFileparser(url='www.baidu.com/robot.txt')用来解析robot文件(了解就行)
Robots协议(也称为爬虫协议、机器人协议等)的全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。
https://www.baidu.com/robots.txt
https://www.taobao.com/robots.txt
robots.txt文件是一个文本文件, robots.txt是一个协议,而不是一个命令.
urllib3 第三方HTTP库
安装: pip install urllib3
urllib3.disable_warnings() 禁用警告信息
一、基本使用
import urllib3 url = 'http://image.baidu.com' headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) \ Chrome/73.0.3683.86 Safari/537.36", "Referer":"https://www.baidu.com" } # 实例一个PoolManager对象构造请求。该对象处理了连接池和线程安全的所有细节,所以我们不用自行处理 http = urllib3.PoolManager() # 1. 发送get请求 resp = http.request('GET', url, headers=headers) print(resp.data.decode("utf-8")) # 解码为文本html # 响应属性 print(resp.data) # 返回的是二进制数据 print(resp.status) # 响应状态码 print(resp.headers) # 响应头 print(resp.getheaders()) # 方法:获取响应头部信息 print(resp.info()) # 方法:响应头部信息 # 2. 发送post请求 form_data = { 'username': 'yelan', 'password': 'wwxxxx' } resp = http.request('post', 'http://httpbin.org/post', fields=form_data) # fields参数就是post请求提交的数据(form表单) # 3. 大文件下载 # 修改可选参数preload_content=False 以流的形式下载 http = urllib3.PoolManager(headers=headers) # 创建该对象时,就指定headers后续请求都会携带请求头 resp = http.request('get', http://httpbin.org/bytes/102400, preload_content=False) for chunk in resp.stream(128): # 每次读取128字节 print(chunk) # 4. 文件上传(注意上传文件是二进制数据,使用body参数接收,与提交数据区分) with open(r"../img/img_1.jpg","rb") as fb: binary_data = fb.read() headers.update({'Content-Type':'image/jpeg'}) # 上传文件|携带json等需要指定类型 response = http.request("POST", 'http://httpbin.org/post', body=binary_data, headers=headers) print(json.loads(response.data.decode("utf-8")))
request参数:
method: <str> 请求方法 url: <str> 请求url headers <dict> 请求头 fields <dict> 发送get请求时该参数就是url的参数, 而当发送post请求时,该参数就是提交的数据(如form表单) body <bytes> 发送post请求携带的二进制数据;如请求时携带json数据或者上传文件。 preload_content <bool> 默认为True, 修改为False时以流的形式接收数据。
import urllib3 # 若我们会发送多个请求,那么给每个请求都去写headers=headers这样太重复了;所以可以在创建该对象时,就指定headers,则后续请求都会携带请求头。 http = urllib3.PoolManager(headers=headers) # 1.如果我们GET请求要发送URL参数, 那么怎么发送呢? 那么给fields参数传入包含了URL参数数据的字典即可. resp = http.request("GET", url="https://httpbin.org/get", fields={"name": "jack", "age": "18"}) print(resp.data.decode()) # 2.若POST请求也要携带URL参数, 怎么办呢? 也是fields? 不是!要通过构造URL才发送URL参数.而fields参数是以字典的形式携带的表单数据 url = "https://httpbin.org/post?name=jack&age=18" resp = http.request("POST", url=url, fields=form_data) print(resp.data.decode()) # str类型的键值对即json数据类型 print(json.loads(resp.data.decode())) # json转python字典
http代理
url = 'http://httpbin.org/get' pro_host = "http://221.6.32.206:41816" # 1. 创建一个代理对象 proxy = urllib3.ProxyManager(proxy_url=pro_host, headers=headers, proxy_headers=None) # 2. 基于该代理对象发送请求 resp = proxy.request("get", url=url, headers=headers) print(resp.data.decode())
对于post和put请求,如果需要查询参数,需要通过url编码将参数编码成正确格式然后拼接到url中
import urllib3 from urllib import parse args = { "user": "YeLan", "key": "value", } form_data = { "user": "yelan", "pwd": "123456" } args_encoded = parse.urlencode(args) url = 'http://httpbin.org/post?' + args_encoded http = urllib3.PoolManager(headers=headers) resp = http.request("POST", url) print(json.loads(resp.data.decode())["args"]) # 返回的字典是无序的 # Form data :对于put和post请求,需要提供字典类型的参数field来传递form表单数据。 resp = http.request("POST", url=url, fields=form_data) print(json.loads(resp.data.decode())["form"])
请求时携带json数据; 通过body参数携带二进制的json数据,并指定请求头的content_type
data = { "user":"YeLan", "key":"value", "age":"22" } json_data_encoded = json.dumps(data).encode("utf-8") # 将dict转化为json数据再编码 content_type = { 'Content-Type': 'application/json' } headers = header.update(content_type) # 更新头部信息 # JSON 当我们需要发送json数据时,我们需要在request中传入编码后的二进制数据类型的body参数,并指定Content—Type的请求头 url = "http://httpbin.org/post" http = urllib3.PoolManager() resp = http.request("POST", url=url, body=json_data_encoded, headers=headers) print(json.loads(resp.data.decode()))
爬虫开发流程
1.找到目标数据
2.分析请求流程
3.构造http请求
4.提取清洗数据
5.数据持久化