当前位置:   article > 正文

Python爬虫从菜鸟到高手--内置网络库urllib详解_python 发送http请求 post urllib

python 发送http请求 post urllib

urllib是Python3中内置的HTTP请求库,不需要单独安装,官方文档链接如下:
https://docs.python.org/3/library/urllib.html

从官方文档可以看出,urllib包含4个模块,如下图所示。

这4个模块的功能描述如下:

  • request:最基本的HTTP请求模块,可以用来发送HTTP请求,并接收服务端的响应数据。这个过程就像在浏览器地址栏输入URL,然后按Enter键一样。

  • error:异常处理模块,如果出现请求错误,我们可以捕获这些异常,然后根据实际情况,或者进行重试,或者直接忽略,或进行其他操作。

  • parse:工具模块,提供了很多处理URL的API,如拆分、解析、合并等。

  • robotparser:主要用来识别网站的robots.txt文件,然后判断哪些网站可以抓取,哪些网站不可以抓取。

本文会介绍这4个模块的核心用法。

1. 发送HTTP GET请求与获得响应

urllib最基本的一个应用就是向服务端发送HTTP请求,然后接收服务端返回的响应数据。这个功能只需要通过urlopen函数就可以搞定。例如,下面的代码向百度发送HTTP GET请求,然后输出服务端的响应结果。

import urllib.request
response=urllib.request.urlopen('https://baidu.com')
# 将服务端的响应数据用utf-8解码
print(response.read().decode('utf-8'))
  • 1
  • 2
  • 3
  • 4

运行结果如下图所示:

我们可以看到,使用urllib与服务端交互是非常容易的,除了import语句外,真正与业务有关的代码只有2行,就完成了整个与服务端交互的过程。其实这个过程已经完成了爬虫的第一步,就是从服务端获取HTML代码,然后就可以利用各种分析库对HTML代码进行解析,提取出我们感兴趣的URL、文本、图像等。
其实urlopen函数返回的是一个对象,而read是这个对象的一个方法,可以利用type方法输出这个对象的类型,当我们知道了对象类型后,就可以很容易知道这个对象中有哪些API,然后调用它们。

import urllib.request
response=urllib.request.urlopen('https://baidu.com')
print(type(response))
  • 1
  • 2
  • 3

这段代码会输出如下的结果:

<class 'http.client.HTTPResponse'>
  • 1

现在我们了解到,urlopen函数返回的是HTTPResponse类型的对象,主要包含read、getheader、getheaders等方法,以及msg、version、status、debuglevel、closed等属性。

下面的例子编写一个程序,使用urllib访问京东主页,并通过HTTPResponse对象读取响应数据。

import urllib.request
# 向京东商城发送HTTP GET请求,urlopen函数即可以使用http,也可以使用https
response=urllib.request.urlopen('https://www.jd.com')
# 输出urlopen函数返回值的数据类型
print('response的类型:',type(response))
# 输出响应状态码、响应消息和HTTP版本
print('status:',response.status,' msg:',response.msg,' version:', response.version)
# 输出所有的响应头信息
print('headers:',response.getheaders())
# 输出名为Content-Type的响应头信息
print('headers.Content-Type',response.getheader('Content-Type'))
# 输出京东商城首页所有的HTML代码(经过utf-8解码)
print(response.read().decode('utf-8'))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

运行结果如下图所示。

2. 发送HTTP POST请求与获得响应

urlopen函数默认情况下发送的是HTTP GET请求,如果要发送HTTP POST请求,需要使用data命名参数,该参数是bytes类型,需要用bytes类将字符串形式的数据转换为bytes类型。

编写一个程序,使用urlopen函数向http://httpbin.org/post发送HTTP POST请求,并将返回结果输出到终端。

import urllib.request
# 将表单数据转换为bytes类型,用utf-8编码
data=bytes(urllib.parse.urlencode({'name':'Bill','age':30}),encoding='utf-8')
# 提交HTTP POST请求
response=urllib.request.urlopen('http://httpbin.org/post',data=data)
# 输出响应数据
print(response.read().decode('utf-8'))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这段代码中一开始提供了一个字典形式的表单数据,然后使用urlencode方法将字典类型的表单转换为字符串形式的表单,接下来将字符串形式的表单按utf-8编码转换为bytes类型,这就是要传给urlopen函数的data命名参数的值,要注意,一旦指定了data命名参数,urlopen函数就会向服务端提交HTTP POST请求,这里并不需要显式指定要提交的是POST请求。

本例将HTTP POST请求提交给了http://httpbin.org/post,这是一个用于测试HTTP POST请求的网址,如果请求成功,服务端会将HTTP POST请求信息原封不动地返回给客户端。

运行结果如下图所示。

3. 错误处理:URLError

URLError类属于urllib库的error模块,该类从OSError类继承,是error模块中的异常基类,由request模块产生的异常都可以通过URLError来捕捉。

URLError类有一个reason属性,可以通过这个属性获得错误的原因。

下面的例子向不同服务器发送多个请求,并用URLError类捕捉发生的异常。

from urllib import request,error
try:
    response = request.urlopen('http://www.jd123.com/test.html')
except error.URLError as e:
    # Bad Request
    print(e.reason)    
try:
    response = request.urlopen('https://geekori.com/abc.html')
except error.URLError as e:
    # Not Found
    print(e.reason)
try:
    response = request.urlopen('https://geekori123.com/abc.html')
except error.URLError as e:
    # [Errno 8] nodename nor servname provided, or not known
    print(e.reason)
try:
    response = request.urlopen('https://bbbccc.com',timeout=2)
except error.URLError as e:
    # timed out
    print(e.reason)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

程序运行结果如下图所示。

从输出的异常原因可以看出,前两个异常分别是Bad Request和Not Found,响应状态码分别是400和404。其实这两个异常原因都是因为URL的域名是存在的,只是指定的资源不存在,服务端遇到这种情况有可能返回400或404。第3个错误原因是因为域名geekori123.com不存在,DNS解析错误。最后一个错误原因是因为域名和资源都存在,只是服务器在超时时间内没有响应客户端,所以抛出了timeout异常。

4. HTTPError

HTTPError是URLError的子类,专门用于处理HTTP请求错误,比如400、404错误。该类有如下3个属性:

  • code:返回HTTP状态码,如404、400等表示服务端资源不存在,500表示服务器内部错误。

  • reason:用于返回错误原因。

  • headers:返回请求头。

    HTTPError也并不是什么错误都能捕捉,例如time out异常无法用HTTPError捕捉,必须使用URLError捕捉。由于HTTPError是URLError的子类,所以通常的做法是在try…except语句中先捕捉HTTPError异常,如果抛出的不是HTTPError异常,再用URLError进行捕捉。所以URLError起到了兜底的作用。

5. 拆分与合并URL(urlparse与urlunparse)

urlparse函数用于拆分URL,也就是将URL分解成不同的部分,下面先看一个例子。

result = urlparse('https://search.jd.com/Searchprint;hello?keyword=Python从菜鸟到高手&enc=utf-8#comment')
print(type(result),result)
  • 1
  • 2

在上面的代码中使用urlparse函数解析了一个URL,并将解析结果类型以及解析结果输出,运行结果如下:

<class 'urllib.parse.ParseResult'> 
ParseResult(scheme='https', netloc='search.jd.com', path='/Searchprint', params='hello', query='keyword=Python从菜鸟到高手&enc=utf-8', fragment='comment')
  • 1
  • 2

可以看到,返回结果是一个ParseResult类型的对象,包含6部分,分别是scheme、netloc、path、params、query和fragment。根据拆分结果,可以得出一个完整的URL的通用格式如下:

scheme://netloc/path;params?query#fragment
  • 1

只要符合这个规则的URL,都可以被urlparse函数解析,urlparse函数除了可以传递url参数外,还可以传递另外两个参数。urlparse函数的定义如下:

urlparse(url, scheme='', allow_fragment=True)
  • 1

我们可以看到,urlparse函数有3个参数,它们的含义如下:

  • url:必填参数,待解析的URL。

  • scheme:可选参数,如果url没有带协议(https、http、ftp等),那么scheme参数的值就会作为默认协议。该参数默认值为空字符串。

  • allow_fragments:可选参数,表示是否忽略fragment部分。该参数值如果为False,表示忽略fragment部分,默认参数值是True。

    既然有用于拆分URL的urlparse函数,那么就会有将各个部分合并成一个完整URL的urlunparse函数。该函数接收一个可迭代的对象,对象中的元素个数必须是6,否则会抛出参数数量不足或过多的错误。

    下面的例子使用urlparse函数拆分了一个URL,并输出拆分后的各个部分,以及使用urlunparse函数合并不同的部分,组成一个完整的URL。

from urllib.parse import urlparse,urlunparse
# 拆分URL
result = urlparse('https://search.jd.com/Searchprint;hello?keyword=Python从菜鸟到高手&enc=utf-8#comment')
print('scheme:',result.scheme)
print('netloc:',result.netloc)
print('path:',result.path)
print('params:',result.params)
print('query:',result.query)
print('fragment:',result.fragment)
print('-----------------')
# 拆分URL,指定默认的scheme,并且忽略fragment部分
result = urlparse('search.jd.com/Searchprint;hello?keyword=Python从菜鸟到高手&enc=utf-8#comment',scheme='ftp',allow_fragments=False)
print('scheme:',result.scheme)
print('fragment:',result.fragment)
print('----------------')
# 合并不同部分,组成一个完整的URL
data = ['https','search.jd.com','Searchprint','world','keyword=Python从菜鸟到高手&enc=utf-8','comment']
print(urlunparse(data))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

程序运行结果如下图所示。

6. Robots协议简介

Robots协议也称作爬虫协议、机器人协议,它的全名是网络爬虫排除标准(Robots Exclusing Protocol),用来告诉爬虫和搜索引擎哪些页面可以抓取,哪些不可以抓取。该协议的内容通常放在一个名为robots.txt的文本文件中,该文件一般位于网站的根目录下。

注意,robots.txt文件中的内容只是告诉爬虫应该抓取什么,不应该抓取什么,但并不是通过技术手段阻止爬虫抓取那些被禁止的资源,而只是通知爬虫而已。尽管编写爬虫可以不遵循robots.txt文件中的规则,但作为一只有道德、有文化、有纪律的爬虫,应该尽量遵循robots.txt文件描述的规则。

当爬虫访问一个网站时,首先会检查这个网址根目录下是否存在robots.txt文件,如果存在,爬虫就会根据该文件中定义的抓取范围来抓取Web资源。如果这个文件并不存在,爬虫就会抓取这个网站所有可直接访问的页面。

下面来看一个robots.txt文件的例子:

User-agent:*
Disallow:/
Allow:/test/
  • 1
  • 2
  • 3

这个抓取规则首先告诉爬虫对所有的爬虫有效,而且除了test目录外的任何资源都不允许抓取。如果将这个robots.txt文件放在某个网站的根目录,那么搜索引擎的爬虫就会只抓取test目录下的资源,我们会发现搜索引擎中再也查不到其他目录下的资源了。

上面的User-agent描述了爬虫的名字,这里将其设置为*,则表示对所有的爬虫有效,我们还可以特指某些爬虫,如下面的设置明确指定百度爬虫。

User-agent:BaiduSpider
  • 1

robots.txt文件中有2个重要的授权指令:Disallow和Allow,前者表示禁止抓取,后者表示允许抓取。也就是说,Disallow是黑名单,Allow是白名单。例如,下面是一些Robots协议的例子。

(1)禁止所有爬虫抓取网站所有的资源

User-agent:*
Disallow:/
  • 1
  • 2

(2)禁止所有爬虫抓取网站/private和/person目录中的资源

User-agent: *

Disallow: /private/


Disallow:/person/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(3)只禁止百度爬虫抓取网站资源

User-agent:BaiduSpider
Disallow:/
  • 1
  • 2

很多搜索引擎的爬虫都有特定的名称,下表列出了一些常用的爬虫名称。

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述
若有侵权,请联系删除
本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/367438
推荐阅读
相关标签
  

闽ICP备14008679号