为了账号安全,请及时绑定邮箱和手机立即绑定

精通Python爬虫-02-初遇

精通Python爬虫-02-初遇

声明:

本系列文章原创于慕课网,作者秋名山车神,任何人不得以任何形式在不经作者允许的情况下,进行任何形式的印刷以及销售,转载需注明出处及此声明。

本系列文章更新至少每周一更,将涉及Python爬虫基础,Requests,Scrapy等主流爬虫技术。同时会介绍图片验证码,语音验证码的识别以及我自己设计的一个高并发可扩展易维护的集群爬虫架构。

对文章有任何问题请在下面留言,我会不定期的回复大家。

人非圣贤,如果文章有错别字请大家自行区分或指正出来,我将不定期修改错误的地方。

本系列能否持久更新下去离不开大家的支持与鼓励,以及对原创版权的尊重。

urllib简介

首先需要说明的是,本系列教程,全部采用Python3.5作为开发环境,因为我不想做一些影响Python3发展的事情,如非必要,请使用Python3。

urllib是Python提供的一个用来访问网络的库,在Python3中有了较大的改动,首先最明显的就是整合了urllib2和urllib,使用起来更加的明了简单。

第一个请求

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 引入urllib下的request模块
import urllib.request

# 使用urlopen发送请求,并获得响应
response = urllib.request.urlopen('http://www.imooc.com')
print(response.read())

urllib.request模块:包含了跟请求相关的方法。

urlopen:可以根据链接或Request对象来发送请求,并将结果返回为一个HTTPResponse对象。结果对象包含一个read方法,可以输出获取到的HTML代码。

使用Request对象

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib.request

# 创建一个Request对象
req = urllib.request.Request('http://www.imooc.com')
# 使用Request对象发送请求
response = urllib.request.urlopen(req)
print(response.read())

和上面有所不同的是,这里我们使用了Request方法来发送请求,这里或许看不出什么差异,我们将在下个案例中看到他们的区别。

发送POST请求

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib.request
import urllib.parse

post_value = {
    'keyword': '大数据',
    'pageCount': '1'
}

# 把dict对象转换为请求的url参数字符,并按照utf-8转换为字节
post_data = urllib.parse.urlencode(post_value).encode('utf-8')

# 发送携带参数的POST请求
req = urllib.request.Request('http://www.iimedia.cn/do_search.jsp', data=post_data)
response = urllib.request.urlopen(req)
print(response.read())

urllib.request.Request在Python3中,必须接受一个字节类型的data,这是和Python2不同,需要区分。和requests这种库不同,Python的urlopen方法,会根据有没有携带data参数来决定是发送GET请求还是POST请求。

再试试带参数的GET请求

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib.request
import urllib.parse

url = 'http://www.imooc.com/search/course'

get_value = {'words': 'Python', }
get_data = urllib.parse.urlencode(get_value)
# 拼装后的URL就是http://www.imooc.com/search/course?words=Python
req = urllib.request.Request(url  + '?' + get_data)
response = urllib.request.urlopen(req)
print(response.read())

欺骗的大师

我们来思考一个问题,那就是类似于腾讯,百度,乃至慕课网。愿意别人通过爬虫来获取网站上的一些数据吗?我想这种问题不用回答也知道结果,自然是不愿意。那么服务器是如何知道到底是什么在访问它呢?答案是通过请求的数据包中,一个叫做user-agent的参数来区分的,我们通过浏览器的开发者工具可以轻松的找到,如果不知道怎么使用的,可以参考我在慕课网的课程浏览器开发者工具使用技巧

我们只需要使用真正的浏览器访问一下网站,然后在开发者工具中找到这个参数,把里面的内容复制下来。

例如我的是:

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

可以看到,这里面包含了我的浏览器版本以及内核版本,系统的版本和位数等等。

这就像给我们的爬虫披上了一层伪装的外衣一样,可以悄无声息的渗透到我们想去的地方。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib.request

headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'}
url = 'http://www.imooc.com'
# 给headers参数赋值
req = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(req)
print(response.read())

通过urllib的源码,我们可以看到,urllib.request.Request方法接收的参数:

self, url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None

data和headers参数,如果我们不指定,默认都是为空的。

异常的处理

异常的处理,对于任何一个语言任何一个程序都是非常重要的,它意味着我们的程序在遇到错误以后需要如何处理。那么同样的,为了我们的爬虫不轻易的就遇到异常停止,我们需要对一些异常进行操作。

URLError

这是urllib内的一个异常类,正如其名,通常在遇到URL链接有问题时,会抛出这种异常,比如下面的代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib.request
import urllib.error

# 注意这里的URL地址,我们故意写成htt
urllib.request.urlopen('htt://www.imooc.com')

运行上面的代码,得到一个错误:

urllib.error.URLError: <urlopen error unknown url type: htt>

那么我们如何捕获这个异常,在遇到这种问题时,不要让我们的程序崩溃呢?

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib.request
# 所有urllib异常的类,在Python3中都移植到了urllib.error里面
import urllib.error

try:
    urllib.request.urlopen('htt://www.imooc.com')
except urllib.error.URLError as e:
    print(e.reason)

运行上面的程序,我们会得到一个输出信息:

unknown url type: htt

可以看到,这个内容,就是前面我们输出的

urllib.error.URLError: <urlopen error unknown url type: htt>

尖括号里面的内容,信息被存储在URLError对象里的reason属性,它就说明了导致异常的原因。

上面是我们协议写错,这种情况在真实的爬虫环境中,几乎不会遇到,但是我们常遇到的就是URL访问地址不可达,什么情况会导致这种现象发生?比如有一个网站里面有一个链接指向一个地址,但是那个地址的域名到期了,域名所有者没有续费,那这个域名现在就属于一个不可访问的地址了,我们来模拟这种情况,来看一下会报出什么异常信息。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib.request
import urllib.error

try:
    # 这个域名显然不会有人注册,不排除有人看了教程以后注册这个域名...
    urllib.request.urlopen('http://www.imooc1imooc2imooc3.com')
except urllib.error.URLError as e:
    print(e.reason)

上面的代码运行会输出以下信息:

[Errno 11001] getaddrinfo failed

此处我们明显的发现,多了一个中括号,里面有一个Errno是11001。像这样的错误码还有很多,大家带Errno可以在网上搜索,也可以根据后面的错误信息自己去判断。

HTTPError

刚才我们说的都是URL上的一些错误,那么我们在HTTP请求的时候一定也会遇到很多的问题,比如常见的我们访问的页面不存在。需要注意的是,页面不存在不代表我们的请求没有发到对方的服务器,而是发到对方的服务器以后,对方的服务器没有找到我们要访问的网页,给我们返回一个错误码。这和上面的根本都找不到对方的服务器,有本质上的区别。

那么我们来看一下不捕获异常访问一个不存在的页面会怎样:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib.request

# 显然慕课网目前没有cheshen.html这个页面
urllib.request.urlopen('http://www.imooc.com/cheshen.html')

运行以后,得到我们期望的异常:

urllib.error.HTTPError: HTTP Error 404: Not Found

其中HTTP Error 就是错误编号,这里是404,冒号后面是错误原因,意思是没有找到。

这些编号有很多,大家可以参考百度百科给出的资料:HTTP Error

相比URLError,HTTPError更容易产生,也更容易让我们的程序变得脆弱不堪,所以没啥说的,捕获异常:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib.request
import urllib.error

try:
    urllib.request.urlopen('http://www.imooc.com/cheshen.html')
except urllib.error.HTTPError as e:
    print(e.reason)
    print(e.code)

运行以后输出了:

Not Found
404

不用我说,大家也知道reason代表错误信息,code代表错误代码。

而我们如果修改上面的代码:

print(type(e.code))

就会看到

<class 'int'>

这说明code是一个int类型的数值,我们可以在代码中进行比较来判断我们是遇到了哪种错误,以决定接下来我们的爬虫如何工作。

上一篇:精通Python爬虫-01-不断前行的蜘蛛

下一篇:精通Python爬虫-03-狩猎大师

点击查看更多内容
50人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消