Urllib库是Python中的一个功能强大用于操作URL,并在做爬虫的时候经常要用到的库。在Python2.x中分为Urllib库和Urllib2库,Python3.x之后都合并到Urllib库中,使用方法稍有不同,升级合并后,模块中的包的位置变化的地方较多。在此,列举一些常见的位置变动,方便之前用Python2.x的朋友在使用Python3.x的时候可以快速掌握。
Py2.X与Py3.X普遍的区别
属性 | Pytho2.x |
Python3.x |
请求模块 | ||
异常处理模块 | ||
url解析模块 |
||
url发送请求 | ||
将字典编码 | ||
Cookie | ||
构建请求参数 |
一、urllib.request模拟发送请求
urllib.request模块提供了最基本的构造HTTP请求的方法,利用它可以模拟浏览器的一个请求发起过程,同时它还带有处理授权验证(authenticaton)、重定向(redirection)、浏览器Cookies以及其他内容
#导入urllib.request模块
import urllib.request
#构建.request.urlopen发送请求
response = urllib.request.urlopen('https://www.python.org')
#输出网址中的内容编码为UTF-8
print(response.read().decode('utf-8'))
接下来看看它返回的到底是什么,利用type()方法输出响应的类型:
import urllib.request
response = urllib.request.urlopen('https://www.python.org')
#输出响应的类型
print(type(response))
输出结果如下:
<class 'http.client.HTTPResponse'>
可以发现它是一个HTTPResposne类型的对象,它主要包含read()、readinto()、getheader(name)、getheaders()、fileno()等方法,以及msg、version、status、reason、debuglevel、closed等属性
下面再通过一个实例来看看:
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-Clacks-Overhead', 'GNU Terry Pratchett'), ('Content-Length', '47397'), ('Accept-Ranges', 'bytes'), ('Date', 'Mon, 16 Aug 2019 09:57:31 GMT'), ('Via', '1.1 varnish'), ('Age', '2473'), ('Connection', 'close'), ('X-Served-By', 'cache-lcy1125-LCY'), ('X-Cache', 'HIT'), ('X-Cache-Hits', '23'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')]
nginx
可见前两个输出分别输出了响应的状态码和响应的头信息,最后一个输出通过调用getheader()
方法并传递一个参数Server
获取了响应头中的Server值结果是Nginx,意思是服务器是用Nginx搭建的
利用最基本的urlopen()方法,可以完成最基本的简单网页的GET请求抓取,如果想给链接传递一些参数,该怎么实现呢?
首先看一下urlopen()
函数的API:
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
可以发现除了第一个参数可以传递URL之外还可以传递其他内容比如data(附加数据)、timeout(超时时间)等
data参数是可选的,如果要添加该参数,并且如果它是字节流编码格式的内容,即bytes类型,则需要通过bytes()方法转化。另外,如果传递了这个参数,则它的请求方式就不再是GET方式,而是POST方式
import urllib.parse
import urllib.request
data = bytes(urllib.parse.urlencode({'wd': 'hello'}), encoding='utf8')
response = urllib.request.urlopen('https://www.baidu.com/post', data=data)
print(response.read())
这里我们传递了一个参数word,值是hello它需要被转码成
bytes
(字节流)类型。其中转字节流采用了bytes()
方法,参数需要是str
(字符串)类型,需要用urllib.parse模块里的urlencode()将参数字典转化为字符串指定编码格式为utf8
timeout
参数用于设置超时时间,单位为秒,意思就是如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常。如果不指定该参数,就会使用全局默认时间。它支持HTTP、HTTPS、FTP请求
import urllib.request
response = urllib.request.urlopen('http://httpbin.org/get', timeout=1)
print(response.read())
运行结果如下:
During handling of the above exception, another exception occurred:
Traceback (most recent call last): File "/var/py/python/urllibtest.py", line 4, in <module> response = urllib.request.urlopen('http://httpbin.org/get', timeout=1)
...
urllib.error.URLError: <urlopen error timed out>
二、处理异常
1、URLError
urllib.error模块定义了由request模块产生的异常。如果出现了问题
request
模块便会抛出error模块中定义的异常,它继承自OSError类是error异常模块的基类,由request模块生的异常都可以通过捕获这个类来处理
from urllib import request, error
try:
response = request.urlopen('http://mryu.com/index.htm')
except error.URLError as e:
print(e.reason)
我们打开一个不存在的页面,照理来说应该会报错,但是这时我们捕获了URLError这个异常,运行结果如下:
Not Found
程序没有直接报错,而是输出了如上内容,这样通过如上操作我们就可以避免程序异常终止,同时异常得到了有效处理
2. HTTPError
它是
URLError
的子类,专门用来处理HTTP请求错误,比如认证请求失败等。它有如下3个属性
code:返回HTTP状态码,比如404表示网页不存在,500表示服务器内部错误等。
reason:同父类一样,用于返回错误的原因。
headers:返回请求头
三、解析链接
前面说过,urllib库里还提供了
parse
这个模块它定义了处理URL的标准接口,例如实现URL各部分的抽取、合并以及链接转换。它支持如下协议的URL处理:file、ftp、gopher、hdl、http、https、imap、mailto、 mms、news、nntp、prospero、rsync、rtsp、rtspu、sftp、 sip、sips、snews、svn、svn+ssh、telnet和wais;下面介绍一下该模块中常用的方法来看一下它的便捷之处
urlparse()该方法可以实现URL的识别和分段,这里先用一个实例来看一下:
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')
再来看一下urlparse的API
urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)
可以看到返回结果是一个
ParseResult
类型的对象,它包含6部分:scheme
、netloc
、path
、params
、query
、fragment
解析时有特定的分隔符比如://前面的就是scheme
代表协议;第一个/前面便是netloc
即域名;分号;前面是params
代表参数
urlstring
:这是必填项,即待解析的URL
scheme
:它是默认的协议(比如http
或https
等)
from urllib.parse import urlparse
result = urlparse('www.baidu.com/index.html;user?id=5#comment', scheme='https')
print(result)
#运行结果:
ParseResult(scheme='https', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment')
可见scheme
参数只有在URL中不包含scheme
信息时才生效;a
llow_fragments
:即是否忽略fragment
。如果它被设置为False
,fragment
部分就会被忽略,它会被解析为path
、parameters
或者query
的一部分,而fragment
部分为空
from urllib.parse import urlparse
result = urlparse('http://www.baidu.com/index.html#comment', allow_fragments=False)
print(result)
运行结果:
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html#comment', params='', query='', fragment='')
浏览器的模拟
应用场景:有些网页为了防止别人恶意采集其信息所以进行了一些反爬虫的设置,而我们又想进行爬取。
解决方法:设置一些Headers
信息(User-Agent
),模拟成浏览器去访问这些网站
import urllib.request
import urllib.parse
#请求的url地址
url = 'http://www.baidu.com'
#自定义头部信息
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36'}
#构建request请求
request = urllib.request.Request(url, headers=header)
reponse = urllib.request.urlopen(request).read()
fhandle = open("./baidu.html", "wb")
fhandle.write(reponse)
fhandle.close()
代理服务器的设置
应用场景:使用同一个IP去爬取同一个网站上的网页,久了之后会被该网站服务器屏蔽。
解决方法:使用代理服务器在对方的网站上,显示的不是我们真实的IP地址而是代理服务器的IP地址
def use_proxy(proxy_addr,url):
import urllib.request
proxy=urllib.request.ProxyHandler({'http':proxy_addr})
opener=urllib.request.build_opener(proxy,urllib.request.HTTPHandler)
urllib.request.install_opener(opener)
data=urllib.request.urlopen(url).read().decode('utf8')
return data
proxy_addr='139.165.155:9999'
data=use_proxy(proxy_addr,'http://www.baidu.com')
print(len(data))
如果进行封装的话可以通过西刺代理来构建
# -*- coding:utf-8 -*-
import re
import os
import ssl
import lxml
import time
import random
import requests
from bs4 import BeautifulSoup
ssl._create_default_https_context = ssl._create_unverified_context
class ViedeoCrawler():
def get_ip_list(self):
url = 'http://www.xicidaili.com/nn/'
html = requests.get(url=url, headers=self.headers).text
soup = BeautifulSoup(html, 'lxml')
ips = soup.find(id='ip_list').find_all('tr')
ip_list = []
for i in range(1, len(ips)):
ip_info = ips[i]
tds = ip_info.find_all('td')
ip_list.append(tds[1].text + ':' + tds[2].text)
print(str(ip_list))
print("代理列表抓取成功")
return ip_list
def get_random_ip(self,ip_list):
print("正在设置随机代理...")
proxy_list = []
for ip in ip_list:
proxy_list.append('http://' + ip)
proxy_ip = random.choice(proxy_list)
proxies = {'http': proxy_ip}
print("代理设置成功",proxies)
return proxies
def run(self):
print("Start!")
start_time = time.time()
html = requests.get(self.url).text
ip_list = self.get_ip_list()
proxies = self.get_random_ip(ip_list)
if __name__=='__main__':
crawler = ViedeoCrawler()
crawler.run()
Cookie
的使用
应用场景:爬取的网页涉及登录信息。访问每一个互联网页面,都是通过HTTP
协议进行的,而HTTP
协议是一个无状态协议,所谓的无状态协议即无法维持会话之间的状态
import http.cookiejar
import urllib.request
filename = 'cookies.txt'
cookie = http.cookiejar.MozillaCookieJar()
cookie.load(filename, ignore_expires=True, ignore_discard=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')[500:1000])