urllib的用法

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:password@61.135.217.7: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 的模拟登录就太容易了,甚至可以根据你的鼠标活动的特征准确地判断出是不是真人在操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342