Python爬虫(一):urllib学习

使用urllib

urllib库是Python内置的请求库,可以用来模拟发送请求,它包含四个模块:

  • request:它是最基本的HTTP请求模块,可以用来模拟发送请求
  • error:异常处理模块,保证程序不会意外终止
  • parse:一个工具模块,提供了许多Url的处理方法
  • robotparser:主要用来识别网站的robots.txt文件,用的较少

urlopen()

urllib.request模块提供了最基本的HTTP请求的方法,同时它还带有处理授权验证、重定向、浏览器Cookies等功能。

使用urlopen()方法,API如下:

1
2
def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
*, cafile=None, capath=None, cadefault=False, context=None)
1
2
3
4
import urllib.request

response=urllib.request.urlopen('https://www.python.org')
print(response.read().decode('utf8'))

我们使用type方法查看响应的类型

1
2
3
4
import urllib.request

response=urllib.request.urlopen('https://www.python.org')
print(type(response))
<class 'http.client.HTTPResponse'>

可以看出返回的是一个HTTPResponse,我们可以这些方法和属性查看一些信息。

1
2
3
4
5
import urllib.request

response=urllib.request.urlopen('https://www.python.org')
print(response.status)
print(response.getheaders())
200
[('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'DENY'), ('Via', '1.1 vegur'), ('Via', '1.1 varnish'), ('Content-Length', '48510'), ('Accept-Ranges', 'bytes'), ('Date', 'Sat, 10 Aug 2019 02:56:05 GMT'), ('Via', '1.1 varnish'), ('Age', '3403'), ('Connection', 'close'), ('X-Served-By', 'cache-iad2138-IAD, cache-lax8627-LAX'), ('X-Cache', 'HIT, HIT'), ('X-Cache-Hits', '2, 360'), ('X-Timer', 'S1565405765.160733,VS0,VE0'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')]

urlopen的data参数:
data参数是可选的,如果添加该参数,若它是字节流编码格式的内容,则需要通过bytes()方法转换,请求方式变为POST方式。

1
2
3
4
5
6
import urllib.parse
import urllib.request

data=bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf8')
response=urllib.request.urlopen('http://httpbin.org/post',data=data)
print(response.read())
b'{\n  "args": {}, \n  "data": "", \n  "files": {}, \n  "form": {\n    "word": "hello"\n  }, \n  "headers": {\n    "Accept-Encoding": "identity", \n    "Content-Length": "10", \n    "Content-Type": "application/x-www-form-urlencoded", \n    "Host": "httpbin.org", \n    "User-Agent": "Python-urllib/3.7"\n  }, \n  "json": null, \n  "origin": "112.26.77.217, 112.26.77.217", \n  "url": "https://httpbin.org/post"\n}\n'

可以看到上述的form表单中的word:hello,即我们请求的数据。

urllib的time参数:用于设置超时时间

1
2
3
4
import urllib.request

response=urllib.request.urlopen('http://httpbin.org/get',timeout=0.1)
print(response.read())

最下面一行的:URLError,我们设置的超时时间为0.1s,超过时间后,抛出该异常。

因此可以通过设置这个超时时间来控制一个网页如果长时间未响应,就跳过它的抓取。我们通过try except语句来实现。

1
2
3
4
5
6
7
8
9
import socket
import urllib.request
import urllib.error

try:
response=urllib.request.urlopen('http://httpbin.org/get',timeout=0.1)
except urllib.error.URLError as e:
if isinstance(e.reason,socket.timeout):
print('Time out')
Time out

Request

我们利用urlopen()方法可以实现最基本请求的发起,但这几个参数并不足以构建一个完整的请求。如果在请求中加入Headers等信息,我们可以利用更强大的Request类构建。

1
2
3
4
5
import urllib.request

request=urllib.request.Request('https://www.python.org')
response=urllib.request.urlopen(request)
print(response.read().decode('utf8'))

查看一下Request的构造方法:

1
2
3
def __init__(self, url, data=None, headers={},
origin_req_host=None, unverifiable=False,
method=None):

同样第二个参数data如果要传,则必须要tytes(字节流)类型的,如果是字典,可以先用urllib.parse模块里的urlencode()编码。

第三个参数headers是一个字典,它就是请求头,最常用的方法就是修改User-Agent来伪装浏览器。比如伪装Chrome,设置为:

1
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36

第四个参数origin_req_host为请求方的host名称或者ip地址,第六个为请求方法,比如GET,POST等

下面我们传入多个参数来看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from urllib import request,parse

url='http://httpbin.org/post'
headers={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
'HOST':'httpbin.org'
}
dict={
'name':'Jack'
}
data=bytes(parse.urlencode(dict),encoding='UTF8')
req=request.Request(url=url,data=data,headers=headers,method='POST')
response=request.urlopen(req)
print(response.read())
b'{\n  "args": {}, \n  "data": "", \n  "files": {}, \n  "form": {\n    "name": "Jack"\n  }, \n  "headers": {\n    "Accept-Encoding": "identity", \n    "Content-Length": "9", \n    "Content-Type": "application/x-www-form-urlencoded", \n    "Host": "httpbin.org", \n    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"\n  }, \n  "json": null, \n  "origin": "112.26.77.217, 112.26.77.217", \n  "url": "https://httpbin.org/post"\n}\n'

观察结果,我们已经成功设置了headers,data数据了。

headers也可以使用add_header()方法来添加,更加方便。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from urllib import request,parse

# method,headers,data

url='http://httpbin.org/post'
dict={
'name':'Jack'
}
data=bytes(parse.urlencode(dict),encoding='UTF8')
req=request.Request(url=url,data=data)
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36')
req.add_header('HOST','httpbin.org')

response=request.urlopen(req)
print(response.read())
b'{\n  "args": {}, \n  "data": "", \n  "files": {}, \n  "form": {\n    "name": "Jack"\n  }, \n  "headers": {\n    "Accept-Encoding": "identity", \n    "Content-Length": "9", \n    "Content-Type": "application/x-www-form-urlencoded", \n    "Host": "httpbin.org", \n    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"\n  }, \n  "json": null, \n  "origin": "112.26.77.217, 112.26.77.217", \n  "url": "https://httpbin.org/post"\n}\n'

异常处理

我们了解了请求的发送过程,但是在网络不好的情况下出现了异常怎么办?这时候如果不处理异常,程序很可能因为报错而终止,所以异常的处理是非常必要的。

urllib的eroor模块定义了由request模块产生的异常。

URLError

ERLERROR类来自于urllib库中的error模块,它有一个属性reason,即返回错误的原因。

1
2
3
4
5
6
from urllib import error,request

try:
response=request.urlopen('https://www.zhyong.cn/11')
except error.URLError as e:
print(e.reason)
Not Found

程序没有直接报错,而是输出了如上内容,可以避免程序的异常终止。

HTTPError

它是URLError的子类,专门处理HTTP的请求错误,它有三个属性:

  • code:返回HTTP状态码,比如404表示页面不存在,500表示服务器内部错误
  • reason:同父类一样,用于返回错误的原因
  • headers:返回请求头

因为URLError是HTTPError的父类,所以可以先选择捕获子类的异常,再去捕获父类的错误。

1
2
3
4
5
6
7
8
9
10
from urllib import error,request

try:
response=request.urlopen('https://www.zhyong.cn/11')
except error.HTTPError as e:
print(e.code,e.reason,e.headers,sep='\n')
except error.URLError as e:
print(e.reason)
else:
print('Request Successfully')
404
Not Found
Server: nginx
Date: Sat, 10 Aug 2019 06:28:46 GMT
Content-Type: text/html
Content-Length: 146
Connection: close

有时候reason属性返回的不一定是字符串,也可能是一个对象。

1
2
3
4
5
6
7
8
9
import socket
from urllib import error,request

try:
response=request.urlopen('https://www.zhyong.cn',timeout=0.01)
except error.URLError as e:
print(type(e.reason))
if isinstance(e.reason,socket.timeout):
print('TIME OUT')
<class 'socket.timeout'>
TIME OUT
坚持原创技术分享,您的支持将鼓励我继续创作!
0%