1 引入urllib库
import urllib.request
2 使用urllib发送GET请求百度
response = urllib.request.urlopen("http://www.baidu.com")
通过调用urllib的urlopen()方法,入参为要请求的url,返回值为response响应对象;
解析响应信息
# 打印响应信息
print(response.read().decode("UTF-8"))
# 获取状态码
print("状态码", response.status)
响应对象直接print是无法直观的看到响应信息的,尝试直接打印得到对象的地址值<http.client.HTTPResponse object at 0x000002C00C3D99B0>,需要通过http.client.HTTPResponse的read()方法进行解析,当然还要通过decode("UTF-8")指定编码;response.status可以获取当前响应的状态码200,500,404等...
3 使用urllib发送POST请求
# 使用字典设置请求内容
requestBody = {"hello": "urllib"}
# 使用urllib.parse对请求内容进行编码,作为body部分份
data = bytes(urllib.parse.urlencode(requestBody), encoding="UTF-8")
# 构造post请求
postResponse = urllib.request.urlopen("http://httpbin.org/post", data=data)
# 解析响应内容
print(postResponse.read().decode("UTF-8"))
其中http://httpbin.org/post是一个用于测试请求的一个测试url,使用urllib发送post请求和发送get请求的区别是对urlopen()方法的data进行赋值,无需声明,将自动转换为post请求,data就是post请求的requestBody部分,data需要使用urllib.parse对请求内容进行编码
响应信息为标准而熟悉的json串
{
"args": {},
"data": "",
"files": {},
"form": {
"hello": "urllib"
},
"headers": {
"Accept-Encoding": "identity",
"Content-Length": "12",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "Python-urllib/3.7",
"X-Amzn-Trace-Id": "Root=1-60168285-06da2c3216947a04714382f9"
},
"json": null,
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/post"
}
Process finished with exit code 0
这里需要留意下headers中的User-Agent的信息,现在我们什么都不设置的时候,他的值是Python-urllib/3.7,刚好是现在使用的python版本,后面会解释这个User-Agent
4 使用Request对应构造请求
# 请求url,请求体,请求方式
baseUrl = "http://httpbin.org/post"
requestBody = {"hello": "urllib"}
data = bytes(urllib.parse.urlencode(requestBody), encoding="UTF-8")
method = "POST"
# 将请求url,请求体,请求方式封装到request对象中
request = urllib.request.Request(url=baseUrl, data=data, method=method)
# 将request对应作为参数使用urlopen()方法发送请求
res = urllib.request.urlopen(request)
print(res.read().decode("UTF-8"))
urllib.request.Request对应可以设置url,请求体body,请求方式method还有我们常用的请求头(后面会专门讲),这样的请求方式更加灵活一些,当需要设置复杂参数构造请求的时候,这种方式比较适合
5 超时时间设置及超时异常处理
在真正的爬虫过程中,可能会因为网络或者目标服务器的原因,出现迟迟无法响应,所以需要设置超时时间,让爬虫更高效率的工作
# 提取解析响应的方法
def readResponse(response):
print(response.read().decode("UTF-8"))
# 发送get请求并设置超时时间
try:
getResponse = urllib.request.urlopen("http://www.baodu.com", timeout=0.01)
readResponse(getResponse)
except Exception as e:
print(e)
urlopen()方法中加入timeout = xx 便是设置超时时间,这里使用了0.01秒,必然会超时并抛出超时异常,使用try ... except ... 将异常信息打印
<urlopen error timed out>
Process finished with exit code 0
6 设置head请求头
爬虫实际应用中,会有爬虫使用者和服务端反扒之间的博弈,毕竟作为一个服务端每天如果被大量的爬虫所爬取,会对服务端产生压力,所以很多网站会有对应的反扒手段,比如最入门级别的就是检测User-Agent请求是否为真实的浏览器发出,下面以一个请求某瓣小例子进行展示
# 请求某瓣(真正的域名自己copy)
dbResponse = urllib.request.urlopen("http://www.xxxban.com")
# 调用上方封装好的解析响应的方法
readResponse(dbResponse)
响应响应信息
urllib.error.HTTPError: HTTP Error 418:
Process finished with exit code 1
可以看到服务端响应了我们错误信息418,我是茶壶;HTTP 418 I'm a teapot 客户端错误响应代码表示服务器拒绝冲泡咖啡,因为它是个茶壶。该错误是超文本咖啡壶控制协议的参考,和 1998 年愚人节的玩笑。意思就是服务端发现你不是一个正常浏览器,而是程序发起的请求
遇到这种情况我们就需要设置请求头,来伪装我们自己,达到欺骗服务端拿到正确响应的效果,因为需要设置请求头,所有这里就需要使用urllib.request.Request对应进行请求的构造:
# 通过设置user-agent请求头访问某瓣,url自行修正
baseUrl = "http://www.xxxban.com"
method = "GET"
# 这个user-agent 直接打开浏览器,F12后network里面cv
headers = {"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36"
}
# 将请求头,url封装到Request对象中
dbRequest = urllib.request.Request(url=baseUrl, headers=headers, method=method)
# 发送请求
dbRes = urllib.request.urlopen(dbRequest)
# 解析响应
readResponse(dbRes)
响应信息
状态码 200
<!DOCTYPE HTML>
<html lang="zh-cmn-Hans" class="ua-windows ua-webkit">
<head>
<meta charset="UTF-8">
...
Process finished with exit code 0
回到刚才的问题,一般情况下服务端是怎么区分浏览器请求和爬虫的请求的呢,正常情况下是通过请求头中User-Agent的值来确认的,浏览器会将自己的信息放在User-Agent里面告诉服务端自己是什么浏览器,能处理怎么样的报文,服务端会根据User-Agent中的信息返回正确格式的报文给浏览器
为什么我们一开始没有设置请求头的时候服务端能发现我们是一个爬虫程序呢,可以回去看一下之前第3个例子的User-Agent的值为Python-urllib/3.7,也就是说User-Agent的值在我们不设置的时候,会自动将我们的真实信息设置进去,而我们通过cv浏览器的User-Agent设置到请求头中,这时候服务端在User-Agent里面获取到的值是Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36,是一个标准的浏览器信息,则响应了正确的信息
网友评论