urllib的用法
什么是Urllib?
* urllib.request 请求模块
* urllib.error 异常处理模块
* urllib.parse url解析模块
* urllib.robotparser robots.txt解析模块
Handler处理器 和 自定义Opener
opener是 urllib.request.OpenerDirector 的实例,我们之前一直都在使用的urlopen,它是一个特殊的opener(也就是模块帮我们构建好的)。
但是基本的urlopen()方法不支持代理、cookie等其他的HTTP/HTTPS高级功能。所以要支持这些功能:
使用相关的 Handler处理器 来创建特定功能的处理器对象;
然后通过 urllib.request.build_opener()方法使用这些处理器对象,创建自定义opener对象;
使用自定义的opener对象,调用open()方法发送请求。
如果程序里所有的请求都使用自定义的opener,可以使用urllib.request.install_opener() 将自定义的 opener 对象 定义为 全局opener,表示如果之后凡是调用urlopen,都将使用这个opener(根据自己的需求来选择)
简单的自定义opener()
import urllib.request
# 构建一个HTTPHandler 处理器对象,支持处理HTTP请求
http_handler = urllib.request.HTTPHandler()
# 构建一个HTTPHandler 处理器对象,支持处理HTTPS请求
# http_handler = urllib.request.HTTPSHandler()
# 调用urllib.request.build_opener()方法,创建支持处理HTTP请求的opener对象
opener = urllib.request.build_opener(http_handler)
# 构建 Request请求
request = urllib.request.Request("http://www.baidu.com/")
# 调用自定义opener对象的open()方法,发送request请求
response = opener.open(request)
# 获取服务器响应内容
print (response.read().decode())
这种方式发送请求得到的结果,和使用urllib.request.urlopen()发送HTTP/HTTPS请求得到的结果是一样的。
如果在 HTTPHandler()增加 debuglevel=1参数,还会将 Debug Log 打开,这样程序在执行的时候,会把收包和发包的报头在屏幕上自动打印出来,方便调试,有时可以省去抓包的工作。
仅需要修改的代码部分:
构建一个HTTPHandler 处理器对象,支持
处理HTTP请求,同时开启Debug Log,debuglevel 值默认 0
http_handler = urllib.request.HTTPHandler(debuglevel=1)
构建一个HTTPHSandler 处理器对象,支持
处理HTTPS请求,同时开启Debug Log,debuglevel 值默认 0
https_handler = urllib.request.HTTPSHandler(debuglevel=1)
我们在做爬虫的过程中经常会遇到这样的情况,最初爬虫正常运行,正常抓取数据,一切看起来都是那么美好,然而一杯茶的功夫可能就会出现错误,比如403 Forbidden,这时候打开网页一看,可能会看到“您的IP访问频率太高”这样的提示。出现这种现象的原因是网站采取了一些反爬虫措施。比如,服务器会检测某个IP在单位时间内的请求次数,如果超过了这个阈值,就会直接拒绝服务,返回一些错误信息,这种情况可以称为封IP。
既然服务器检测的是某个IP单位时间的请求次数,那么借助某种方式来伪装我们的IP,让服务器识别不出是由我们本机发起的请求,不就可以成功防止封IP了吗?
一种有效的方式就是使用代理,后面会详细说明代理的用法。在这之前,需要先了解下代理的基本原理,它是怎样实现IP伪装的呢?
基本原理: 代理实际上指的就是代理服务器,英文叫作proxy server,它的功能是代理网络用户去取得网络信息。形象地说,它是网络信息的中转站。在我们正常请求一个网站时,其实是发送了请求给Web服务器,Web服务器把响应传回给我们。如果设置了代理服务器,实际上就是在本机和服务器之间搭建了一个桥,此时本机不是直接向Web服务器发起请求,而是向代理服务器发出请求,请求会发送给代理服务器,然后由代理服务器再发送给Web服务器,接着由代理服务器再把Web服务器返回的响应转发给本机。这样我们同样可以正常访问网页,但这个过程中Web服务器识别出的真实IP就不再是我们本机的IP了,就成功实现了IP伪装,这就是代理的基本原理
web_proxy
代理的作用: 那么,代理有什么作用呢?我们可以简单列举如下。
1.突破自身IP访问限制,访问一些平时不能访问的站点。
2.访问一些单位或团体内部资源:比如使用教育网内地址段免费代理服务器,就可以用于对教育网开放的各类FTP下载上传,以及各类资料查询共享等服务。
3.提高访问速度:通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲区中,当其他用户再访问相同的信息时,则直接由缓冲区中取出信息,传给用户,以提高访问速度。
4.隐藏真实IP:上网者也可以通过这种方法隐藏自己的IP,免受攻击。对于爬虫来说,我们用代理就是为了隐藏自身IP,防止自身的IP被封锁。
使用代理IP,这是爬虫/反爬虫的第二大招,对于爬虫来说,由于爬虫爬取速度过快,在爬取过程中可能遇到同一个IP访问过于频繁的问题,此时网站就会让我们输入验证码登录或者直接封锁IP,这样会给爬取带来极大的不便。通常这种反爬虫手段也是最好用的。
很多网站会检测某一段时间某个IP的访问次数(通过流量统计,系统日志等),如果访问次数多的不像正常人,它会禁止这个IP的访问。
所以我们可以设置一些代理服务器,每隔一段时间换一个代理,就算IP被禁止,依然可以换个IP继续爬取。
常见的代理分类:
1.根据协议划分:
FTP代理服务器:主要用于访问FTP服务器,一般有上传、下载的功能以及缓存的功能,端口号一般为21,2121等。
HTTP代理服务器:主要用于访问网页,一般有内容过滤和缓存的功能,端口号一般为80、8080、3128等
SSL/TLS代理:主要能用于访问加密的网站,一般有SSL或者TLS加密功能
SOCKS代理:只是单纯的用于传输数据包,不关心具体的协议用法,速度快、有缓存功能,端口号一般为1080
2.根据匿名内容划分:
高度匿名代理:会将数据包原封不动的转发,在服务器看来就好像真的是一个普通的用户短在访问,而记录的IP就是代理服务器的IP
普通匿名代理:会在数据包上做一些改动,服务端上有可能发现这个是代理服务器,也有一定的几率追查到客户端的真实IP.
透明代理:不但改动了数据包,还会告诉服务器客户端的真实IP,这种代理除了用缓存技术提高浏览器速度。能用内容过滤提高安全性之外,并没有其他作用。
使用代理IP 这是爬虫/反爬虫的第二大招,通常也是最好用的。
很多网站会检测某一段时间某个IP的访问次数(通过流量统计,系统日志等),如果访问次数多的不像正常人,它会禁止这个IP的访问。
所以我们可以设置一些代理服务器,每隔一段时间换一个代理,就算IP被禁止,依然可以换个IP继续爬取。
ProxyHandler处理器用来设置代理
urllib.request中通过ProxyHandler来设置使用代理服务器,下面代码说明如何使用自定义opener来使用代理:
from urllib import request,error
#构建支持代理的handler
proxy = {
'http':'61.138.33.20:808',
'https':'120.69.82.110:44693',
}
proxy_handler = request.ProxyHandler(
proxies=proxy
)
# 构建一个私密代理Handler,需要加上私密代理账户的用户名和密码
# authproxy = {
# "http" :"username:[email protected]:80"
#}
# authproxy_handler=urllib.request.ProxyHandler(
# proxies=authproxy
#)
#根据proxy_handler实例化一个opener对象
opener = request.build_opener(proxy_handler)
url = 'http://www.baidu.com/'
# 使用https://httpbin.org/get接口验证使用了代理
# url = 'https://httpbin.org/get'
try:
response = opener.open(url,timeout=5)
print(response.status)
except error.HTTPError as err:
print(err.reason)
except error.URLError as err:
print(err.reason)
# 1. 如果按照上面代码,只有使用opener.open()方法发送
请求才使用自定义的代理,而urlopen()则不使用自定义代理。
response = opener.open(request)
# 2. 将自定义的opener设置为全局的opener,之后所有的,不管是
opener.open()还是urlopen() 发送请求,都将使用自定义代理。
# request.install_opener(opener)
# response = urlopen(request)
免费的开放代理获取基本没有成本,我们可以在一些代理网站上收集这些免费代理,测试后如果可以用,就把它收集起来用在爬虫上面,但是这些免费的代理不稳定,并且可用率不高。
免费短期代理网站举例:
西刺免费代理IP:http://www.xicidaili.com/
快代理免费代理:https://www.kuaidaili.com/free/inha/
如果代理IP足够多,就可以像随机获取User-Agent一样,随机选择一个代理去访问网站。
import urllib.request
import random
proxy_list = [
{"https" : "124.88.67.81:80"},
{"https" : "124.88.67.81:80"},
{"https" : "124.88.67.81:80"},
{"https" : "124.88.67.81:80"},
{"https" : "124.88.67.81:80"}
]
# 随机选择一个代理
proxy = random.choice(proxy_list)
# 使用选代理构建代理处理器对象
proxy_handler = request.ProxyHandler(
proxies=proxy
)
#根据proxy_handler实例化一个opener对象
opener = request.build_opener(proxy_handler)
url = 'http://www.baidu.com/'
try:
response = opener.open(url,timeout=5)
print(response.status)
except error.HTTPError as err:
print(err.reason)
except error.URLError as err:
print(err.reason)
但是,这些免费开放代理一般会有很多人都在使用,而且代理有寿命短,速度慢,匿名度不高,HTTP/HTTPS支持不稳定等缺点(免费没好货)。
所以,专业爬虫工程师或爬虫公司会使用高品质的代理,这些代理通常需要找专门的代理供应商购买。
另外:我们也可以自己爬取免费的代理网站,获取免费代理,然后收集可用的代理,存放在数据库中,使用的视乎直接从数据库中获取.
Cookie Cookie 是指某些网站服务器为了辨别用户身份和进行Session跟踪,而储存在用户浏览器上的文本文件,Cookie可以保持登录信息到用户下次与服务器的会话。
Cookie原理 HTTP是无状态的面向连接的协议, 为了保持连接状态, 引入了Cookie机制 Cookie是http消息头中的一种属性,包括:
Cookie名字(Name)
Cookie的值(Value)
Cookie的过期时间(Expires/Max-Age)
Cookie作用路径(Path)
Cookie所在域名(Domain),
使用Cookie进行安全连接(Secure),
指的是该cookies是否能仅能够被安全协议传输。
sameSite是否是同一个站点
前两个参数是Cookie应用的必要条件,另外,还包括Cookie大小(Size,不同浏览器对Cookie个数及大小限制是有差异的)。
那么,我们怎样利用Cookies保持状态呢?
当客户端第一次请求服务器时,服务器会返回一个请求头中带有Set-Cookie字段的响应给客户端,用来标记是哪一个用户,客户端浏览器会把Cookies保存起来。当浏览器下一次再请求该网站时,浏览器会把此Cookies放到请求头一起提交给服务器,Cookies携带了会话ID信息,服务器检查该Cookies即可找到对应的会话是什么,然后再判断会话来以此来辨认用户状态。
在成功登录某个网站时,服务器会告诉客户端设置哪些Cookies信息,在后续访问页面时客户端会把Cookies发送给服务器,服务器再找到对应的会话加以判断。如果会话中的某些设置登录状态的变量是有效的,那就证明用户处于登录状态,此时返回登录之后才可以查看的网页内容,浏览器再进行解析便可以看到了。
反之,如果传给服务器的Cookies是无效的,或者会话已经过期了,我们将不能继续访问页面,此时可能会收到错误的响应或者跳转到登录页面重新登录。
所以,Cookies和会话需要配合,一个处于客户端,一个处于服务端,二者共同协作,就实现了登录会话控制。
Cookie由变量名和值组成,根据 Netscape公司的规定,Cookie格式如下:
Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
Cookie应用
Cookies在爬虫方面最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续。
观察豆瓣的登录信息:
0F9AAB1B-F733-471A-9447-9AE6C6636417
获取到一个有登录信息的Cookie模拟登陆
# -*- coding:utf-8 -*-
import urllib.request
url = 'https://www.douban.com/people/175417123/'
根据刚才的登录信息来构建一个已经登录过的用户的headers信息
headers = {
'User-Agent':' Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:59.0) Gecko/20100101 Firefox/59.0',
'Host':'www.renren.com',
'Cookie':'anonymid=jgoj4xlw-3izsk4; depovince=BJ; jebecookies=62d94404-de1f-450a-919b-a2d9f4c8b811|||||; _r01_=1; JSESSIONID=abchsGLNgne0L8_wz2Emw; ick_login=cf54f2dc-8b0b-417a-96b2-32d4051f7236; jebe_key=02cb19ad-2966-4641-8828-217160ca67a0%7Cba6f6d6ec917200a4e17a85dbfe33a4a%7C1525230975024%7C1%7C1525230982574; t=87a502d75601f8e8c0c6e0f79c7c07c14; societyguester=87a502d75601f8e8c0c6e0f79c7c07c14; id=965706174; xnsid=e1264d85; ver=7.0; loginfrom=null; wp_fold=0',
}
2. 通过headers里的报头信息(主要是Cookie信息),构建Request对象
request = urllib.request.Request(url, headers=headers)
3. 直接豆瓣个人主页(主要是Cookie信息)
,判断这是一个已经登录的用户,并返回相应的页面
response = urllib.request.urlopen(request)
4. 打印响应内容
print (response.read().decode())
那么我们通过上面的代码可以看到其实是实现了获取Cookie,通过Cookie来请求网页数据的 但是这样很明显不够灵活,也不够复杂,我们有没有方便一点的做法呢?
cookiejar库 和 HTTPCookieProcessor处理器
在Python处理Cookie,一般是通过cookiejar模块和 urllib模块的HTTPCookieProcessor处理器类一起使用。
cookiejar模块:主要作用是提供用于存储cookie的对象
HTTPCookieProcessor处理器:主要作用是处理这些cookie对象,并构建handler对象。
cookiejar 库
该模块主要的对象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。其实大多数情况下,我们只用CookieJar(),如果需要和本地文件交互,就用 MozillaCookjar()
CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。
MozillaCookieJar (filename,delayload=None,policy=None):从FileCookieJar派生而来,创建与Mozilla浏览器 cookies.txt兼容的FileCookieJar实例。
CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。
我们来做几个案例:
案例一
1)获取Cookie,并保存到CookieJar()对象中:
# urllib_cookiejar_test1.py
import urllib
from http import cookiejar
# 构建一个CookieJar对象实例来保存cookie
cookiejar = cookiejar.CookieJar()
# 使用HTTPCookieProcessor()来创建cookie处理器对象,参数为CookieJar()对象
handler=urllib.request.HTTPCookieProcessor(cookiejar)
# 通过 build_opener() 来构建opener
opener = urllib.request.build_opener(handler)
# 4. 以get方法访问页面,访问之后会自动保存cookie到cookiejar中
opener.open("http://www.baidu.com")
## 可以按标准格式将保存的Cookie打印出来
cookieStr = ""
for item in cookiejar:
cookieStr = cookieStr + item.name + "=" + item.value + ";"
## 舍去最后一位的分号
print (cookieStr[:-1])
我们使用以上方法将Cookie保存到cookiejar对象中,
我们使用以上方法将Cookie保存到cookiejar对象中,然后打印出了cookie中的值,也就是访问百度首页的Cookie值。
案例2: CookieJar() 利用cookiejar和post模拟登录豆瓣网
import http.cookiejar as cookiejar
from urllib import parse,request
#1.构造一个CookieJar对象实例来保存cookie
cookie = cookiejar.CookieJar()
# 2.使用HTTPCookieProcessor()创建cookie处理器对象,
# 参数为CookieJar()对象
cookie_handler = request.HTTPCookieProcessor(cookie)
#3.通过build_opener()来构建opener
opener = request.build_opener(cookie_handler)
#4.addheaders接受一个列表,里面每一个元素都是一个headers信息的元组
#opener将会附带header信息
opener.addheaders = [
('User-Agent','Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:59.0) Gecko/20100101 Firefox/59.0'),
]
#5.需要登录账号和密码
data = {
'source': 'index_nav',
'form_email': '18518753265',
'form_password': 'ljh123456',
}
#6. 通过urlencode()转码
postdata = parse.urlencode(data).encode('utf-8')
#7. 构建Request请求对象,包含需要发送的用户名和密码
request = request.Request("https://www.douban.com/accounts/login", data = postdata)
# 8. 通过opener发送这个请求,并获取登录后的Cookie值,
opener.open(request)
# 9. opener包含用户登录后的Cookie值,可以直接访问那些登录后才可以访问的页面
response = opener.open("https://www.douban.com/people/175417123/")
#这里为了测试不添加cookie时访问改界面的效果
#headers = {
# 'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:59.0) Gecko/20100101 #Firefox/59.0',
#}
# request = request.Request('https://www.douban.com/people/175417123/',headers=headers)
# response = request.urlopen(request)
# 10. 打印响应内容
#打印结果查看是否访问成功
print(response.code)
html = response.read().decode('utf-8')
# print(html)
with open('douban_login.html','w') as f:
f.write(html)
运行结果如下获取到了个人主页的html页面信息 DeepinScrot-0811
使用MozillaCookjar()保存和使用cookie
案例3: 使用MozillaCookjar()访问网站,并且把获得的cookie保存在创建的cookie文件中
322A6571-C12A-4DDA-BCDC-FEA2FA9E6971 (1)
屏幕快照 2018-05-03 上午8.24.59
使用MozillaCookjar()从文件中获取cookies,作为请求的一部分去访问
F8DCAD22-BDA3-4F1F-9371-A012762F78D5 (1)
模拟登录要注意几点:
登录一般都会先有一个HTTP GET,用于拉取一些信息及获得Cookie,然后再HTTP POST登录。
HTTP POST登录的链接有可能是动态的,从GET返回的信息中获取。
password 有些是明文发送,有些是加密后发送。有些网站甚至采用动态加密的,同时包括了很多其他数据的加密信息,只能通过查看JS源码获得加密算法,再去破解加密,非常困难。
大多数网站的登录整体流程是类似的,可能有些细节不一样,所以不能保证其他网站登录成功.
这个测试案例中,为了想让大家快速理解知识点,我们使用的人人网登录接口是人人网改版前的隐藏接口,登录比较方便,目的为了测试cookies的使用。
当然,我们也可以直接发送账号密码到登录界面模拟登录,但是当网页采用JavaScript动态技术以后,想封锁基于 HttpClient 的模拟登录就太容易了,甚至可以根据你的鼠标活动的特征准确地判断出是不是真人在操作。