一、urllib 简介

urllib 是 Python3 中自带的 HTTP 请求库,无需复杂的安装过程即可正常使用,十分适合爬虫入门

urllib 中包含四个模块,分别是

  • request:请求处理模块
  • parse:URL 处理模块
  • error:异常处理模块
  • robotparser:robots.txt 解析模块

以下我们将会分别讲解 urllib 中各模块的使用方法,但是由于篇幅问题,本文只会涉及模块中比较常用的内容

详细内容可以参考官方文档:https://docs.python.org/3.7/library/urllib.html

二、urllib 使用

在开始讲解前,先给大家提供一个用于测试的网站,http://www.httpbin.org/

这个网站可以在页面上返回所发送 请求 的相关信息,十分适合练习使用

好了,下面正式开始!

1、request 模块

request 模块是 urllib 中最重要的一个模块,一般用于 发送请求和接收响应

(1)urlopen 方法

urllib.request.urlopen()

urlopen 方法无疑是 request 模块中最常用的方法之一,常见的参数说明如下:

  • url:必填,字符串,指定目标网站的 URL

  • data:指定表单数据

    该参数默认为 None,此时 urllib 使用 GET 方法 发送请求

    当给参数赋值后,urllib 使用 POST 方法 发送请求,并在该参数中携带表单信息(bytes 类型)

  • timeout:可选参数,用来指定等待时间,若超过指定时间还没获得响应,则抛出一个异常

该方法始终返回一个 HTTPResponse 对象,HTTPResponse 对象常见的属性和方法如下:

  • geturl():返回 URL
  • getcode():返回状态码
  • getheaders():返回全部响应头信息
  • getheader(header):返回指定响应头信息
  • read():返回响应体(bytes 类型),通常需要使用 decode('utf-8') 将其转化为 str 类型

例子1:发送 GET 请求

>>> import urllib.request
>>> url = 'http://www.httpbin.org/get'
>>> response = urllib.request.urlopen(url)
>>> type(response)
# <class 'http.client.HTTPResponse'>
>>> response.geturl()
# 'http://www.httpbin.org/get'
>>> response.getcode()
# 200
>>> response.getheaders()
# [('Connection', 'close'), ('Server', 'gunicorn/19.9.0'), ('Date', 'Sat, 11 Aug 2018 01:39:14 GMT'), ('Content-Type', 'application/json'), ('Content-Length', '243'), ('Access-Control-Allow-Origin', '*'), ('Access-Control-Allow-Credentials', 'true'), ('Via', '1.1 vegur')]
>>> response.getheader('Connection')    
# 'close'
>>> print(response.read().decode('utf-8'))
# {
#   "args": {}, 
#   "headers": {
#     "Accept-Encoding": "identity", 
#     "Host": "www.httpbin.org", 
#     "User-Agent": "Python-urllib/3.7"
#   }, 
#   "origin": "183.6.159.80, 183.6.159.80", 
#   "url": "https://www.httpbin.org/get"
# }

例子2:发送 POST 请求

urllib.parse.urlencode():进行 URL 编码,实际上是将 dict 类型数据转化成 str 类型数据

encode('utf-8'):将 str 类型数据转化成 bytes 类型数据

>>> import urllib.request
>>> import urllib.parse
>>> url = 'http://www.httpbin.org/post'
>>> params = {
    'from':'AUTO',
    'to':'AUTO'
}
>>> data = urllib.parse.urlencode(params).encode('utf-8')
>>> response = urllib.request.urlopen(url=url,data=data)
>>> html =  response.read().decode('utf-8')
>>> print(html)
# {
#   "args": {}, 
#   "data": "", 
#   "files": {}, 
#   "form": { # 这是我们设置的表单数据
#     "from": "AUTO", 
#     "to": "AUTO"
#   }, 
#   "headers": {
#     "Accept-Encoding": "identity", 
#     "Connection": "close", 
#     "Content-Length": "17", 
#     "Content-Type": "application/x-www-form-urlencoded", 
#     "Host": "www.httpbin.org", 
#     "User-Agent": "Python-urllib/3.6"
#   }, 
#   "json": null, 
#   "origin": "116.16.107.180", 
#   "url": "http://www.httpbin.org/post"
# }

(2)Request 对象

实际上,我们还可以给 urllib.request.open() 方法传入一个 Request 对象作为参数

为什么还需要使用 Request 对象呢?因为在上面的参数中我们无法指定 请求头部,而它对于爬虫而言又十分重要

很多网站可能会首先检查请求头部中的 USER-AGENT 字段来判断该请求是否由网络爬虫程序发起

但是通过修改请求头部中的 USER_AGENT 字段,我们可以将爬虫程序伪装成浏览器,轻松绕过这一层检查

这里提供一个查找常用的 USER-AGENT 的网站:

urllib.request.Request()

参数说明如下:

  • url:指定目标网站的 URL
  • data:发送 POST 请求时提交的表单数据,默认为 None
  • headers:发送请求时附加的请求头部,默认为 {}
  • origin_req_host:请求方的 host 名称或者 IP 地址,默认为 None
  • unverifiable:请求方的请求无法验证,默认为 False
  • method:指定请求方法,默认为 None
>>> import urllib.request
>>> url = 'http://www.httpbin.org/headers'
>>> headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
>>> req = urllib.request.Request(url, headers=headers, method='GET')
>>> response = urllib.request.urlopen(req)
>>> html = response.read().decode('utf-8')
>>> print(html)
# {
#   "headers": {
#     "Accept-Encoding": "identity", 
#     "Connection": "close", 
#     "Host": "www.httpbin.org", 
#     "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" 这是我们设置的 User-Agent
#   }
# }

(3) 使用 Cookie

什么是 Cookie?

Cookie 是指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据

① 获取 Cookie

>>> import urllib.request
>>> import http.cookiejar
>>> cookie = http.cookiejar.CookieJar()
>>> cookie_handler = urllib.request.HTTPCookieProcessor(cookie)
>>> opener = urllib.request.build_opener(cookie_handler)
>>> response = opener.open('http://www.baidu.com')
>>> for item in cookie:
    	print(item.name + '=' + item.value)
        
        
# BAIDUID=486AED46E7F22C0A7A16D9FE6E627846:FG=1
# BDRCVFR[RbWYmTxDkZm]=mk3SLVN4HKm
# BIDUPSID=486AED46E7F22C0A7A16D9FE6E627846
# H_PS_PSSID=1464_21106_26920
# PSTM=1533990197
# BDSVRTM=0
# BD_HOME=0
# delPer=0

② 使用 Cookie

>>> import urllib.request
>>> import http.cookiejar
>>> # 将 Cookie 保存到文件
>>> cookie = http.cookiejar.MozillaCookieJar('cookie.txt')
>>> cookie_handler = urllib.request.HTTPCookieProcessor(cookie)
>>> opener = urllib.request.build_opener(cookie_handler)
>>> response = opener.open('http://www.baidu.com')
>>> cookie.save(ignore_discard=True,ignore_expires=True)
>>> # 从文件读取 Cookie 并添加到请求中
>>> cookie = http.cookiejar.MozillaCookieJar()
>>> cookie = cookie.load('cookie.txt',ignore_discard=True,ignore_expires=True)
>>> cookie_handler = urllib.request.HTTPCookieProcessor(cookie)
>>> opener = urllib.request.build_opener(cookie_handler)
>>> response = opener.open('http://www.baidu.com')
>>> # 此时已经得到带有 Cookie 请求返回的响应

(4)使用代理

对于某些网站,如果同一个 IP 短时间内发送大量请求,则可能会将该 IP 判定为爬虫,进而对该 IP 进行封禁

所以我们有必要使用随机的 IP 地址来绕开这一层检查,这里提供几个查找免费的 IP 地址的网站:

注意,免费的代理 IP 基本上十分不稳定,而且还可能随时更新,所以最好自己写一个爬虫去维护

>>> import urllib.request
>>> import random
>>> ip_list = [
    {'http':'61.135.217.7:80'},
    {'http':'182.88.161.204:8123'}
]
>>> proxy_handler = urllib.request.ProxyHandler(random.choice(ip_list))
>>> opener = urllib.request.build_opener(proxy_handler)
>>> response = opener.open('http://www.httpbin.org/ip')
>>> print(response.read().decode('utf-8'))
# {
#   "origin": "61.135.217.7"
# }

2、parse 模块

parse 模块一般可以用于处理 URL

(1)quote 方法

当你在 URL 中使用中文时,你会发现程序会出现莫名其妙的错误

>>> import urllib.request
>>> url = 'https://www.baidu.com/s?wd=爬虫'
>>> response = urllib.request.urlopen(url)
# UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-11: ordinal not in range(128)

这时,quote 方法就可以派上用场了,它使用转义字符替换特殊字符,从而将上面的 URL 处理成合法的 URL

>>> import urllib.parse
>>> url = 'https://www.baidu.com/s?wd=' + urllib.parse.quote('爬虫')
>>> url
# 'https://www.baidu.com/s?wd=%E7%88%AC%E8%99%AB'

(2)urlencode 方法

urlencode 方法在上面的文章中曾经用到过,不知道大家还有没有印象,这里我们再重新回顾一遍

简单来说,urlencode 方法就是将 dict 类型数据转化为符合 URL 标准的 str 类型数据,请看演示:

>>> import urllib.parse
>>> params = {
    'from':'AUTO',
    'to':'AUTO'
}
>>> data = urllib.parse.urlencode(params)
>>> data
# 'from=AUTO&to=AUTO'

(3)urlparse 方法

urlparse 方法用于解析 URL,返回一个 ParseResult 对象

该对象可以认为是一个六元组,对应 URL 的一般结构:scheme://netloc/path;parameters?query#fragment

>>> import urllib.parse
>>> url = 'http://www.example.com:80/python.html?page=1&kw=urllib'
>>> url_after = urllib.parse.urlparse(url)
>>> url_after
# ParseResult(scheme='http', netloc='www.example.com:80', path='/python.html', params='', query='page=1', fragment='urllib')
>>> url_after.port
# 80

3、error 模块

error 模块一般用于进行异常处理,其中包含两个重要的类:URLErrorHTTPError

注意,HTTPError 是 URLError 的子类,所以捕获异常时一般要先处理 HTTPError,常用的格式如下:

>>> import urllib.request
>>> import urllib.error
>>> import socket
>>> try:
	response = urllib.request.urlopen('http://www.httpbin.org/get', timeout=0.1)
except urllib.error.HTTPError as e:
	print("Error Code: ", e.code)
	print("Error Reason: ", e.reason)
except urllib.error.URLError as e:
	if isinstance(e.reason, socket.timeout):
		print('Time out')
else:
	print('Request Successfully')
    
# Time out

【参考资料】

【爬虫系列相关文章】