了解了爬虫的基本原理后,接下来我们就可以爬取网页内容。网页其实是由HTML代码和JS、CSS等组成的。urllib是python提供的HTTP请求库,它有许多模块供我们爬取使用。
urllib.request
首先使用urlopen打开一个url,可以获取页面的源代码。
import urllib.request
response=urllib.request.urlopen("https://www.baidu.com/")
print(response.read())
结果如下所示:
urlopen函数的为
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None)
其中url为访问的网址;data为访问时需要传输的数据;timeout为超时时间,当超过此时间服务器未响应时即为访问失败;cafile和capath为HTTP请求指定一组受信的CA证书,在此先不管它。除了第一个参数之外,后四个参数都可以不设置。
除此之外,urlopen函数可以直接接受一个Request对象:
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False)
同样地,只有第一个参数不可省略。因此上述代码可以换成:
import urllib.request
request=urllib.request.Request("https://www.baidu.com/")
response=urllib.request.urlopen(request)
print(response.read())
然而有的网页是动态网页,需要根据传的数据做出相应的响应。HTTP请求传送数据时有POST和GET两种方式。GET方式是直接以链接形式访问,链接中包含了所有的参数,可以直观地查看自己提交的内容,但是当数据中包含密码时就涉及到了安全问题。POST相反,它不会在网址上显示所有的参数。
下面展示这两种数据传送的用法。
POST:
import urllib.request
import urllib.parse
params = urllib.parse.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query?%s" % params)
print(f.read().decode('utf-8'))
GET:
import urllib.request
import urllib.parse
params = urllib.parse.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
params = params.encode('utf-8')
f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query", params)
print(f.read().decode('utf-8'))
request headers
在前面讲到构建Request对象时可以设置headers。那么headers是干什么用的呢。
下面是我使用Chrome浏览器访问百度的一个request headers。
可以看到它包含但不仅限于这些内容:
- Accept:浏览器端可以接受的媒体类型。
- Accept-Encoding:浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate)
- Accept-Language:浏览器申明自己接收的语言。
- Host:请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的。
- Referer:告诉服务器我是从哪个页面链接过来的。
- User-Agent:告诉HTTP服务器, 客户端使用的操作系统和浏览器的名称和版本。
在使用python进行爬虫的时候,可以会遇到一些网站的反爬虫措施。一般服务器会根据User-Agent的值来判断请求是否从浏览器发出。如果没有对headers进行设置,User-Agent会声明自己为python脚本,对于有反爬虫措施的服务器,它就会拒绝爬虫程序访问。而通过设置headers就可以伪装成浏览器进行访问。
下面为设置headers的例子。
import urllib.request
def parse(url):
headers = {'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.8',
'Cache-Control': 'max-age=0',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36',
'Connection': 'keep-alive',
'Referer': 'http://www.baidu.com/'
}
req = urllib.request.Request(url, None, headers)
response = urllib.request.urlopen(req)
page_source = (response.read())
return page_source
设置代理
一些网站的反爬虫措施会检测一段时间内某个ip的访问次数,如果访问频率过高,它会禁止此ip的访问。所以我们可以设置一个代理服务器,隔一定的时间更换一个代理。
Python3中提供了ProxyHandler设置代理服务器,废话不多说,我们还是通过一个例子来看看如何使用代理。
import urllib.request
proxy_handler = urllib.request.ProxyHandler({"http" : "117.87.176.131"})
null_handler = urllib.request.ProxyHandler({})
tag = True
if tag:
opener = urllib.request.build_opener(proxy_handler)
else:
opener = urllib.request.build_opener(null_handler)
request = urllib.request.Request("http://www.baidu.com/")
response = opener.open(request)
# urllib.request.install_opener(opener)
# response = urllib.request.urlopen(request)
print(response.read())
程序先构建了两个代理handler,一个有代理IP,一个没有代理IP。通过urllib2.build_opener()方法创建OpenerDirector实例,该对象按给定参数顺序处理这些Handler对象。然后使用OpenerDirector.open()方法发送请求才可以使用自定义的代理。这里要注意的一点是直接使用urlopen是不使用自定义代理的,必须先使用install_opener()方法将OpenerDirector对象作为默认的全局opener,之后不管是使用opener.open()还是urlopen() 发送请求,都将使用自定义代理。
urllib.error
urllib.error定义了由urllib.request引起的异常的异常类。基本异常类是URLError,它是继承自IOError。
这个产生的原因可能是网络无连接、连接不到服务器或者是服务器不存在,我们可以使用try-except捕获异常并且分析异常原因。
from urllib import request
from urllib import error
req=request.Request("https://www.xxx.com/")
try:
response=request.urlopen(req)
except error.URLError as e:
print(e.reason)
我们访问了一个不存在的网址,输出结果为:
URLError的一个子类,当使用urlopen()发送请求时,服务器会有一个应答对象response,同时包含着一个状态码。当不能对请求进行处理时,它会产生一个HTTPError,对应一个状态码表示响应的状态。
from urllib import request
from urllib import error
req=request.Request("http://blog.csdn.net/dxk.html")
try:
response=request.urlopen(req)
except error.HTTPError as e:
print(e.code)
结果如下:
404
这表示请求的资源没有在服务器上找到。
由于HTTPError是URLError的子类,所以当我们需要同时捕捉这两个异常时,我们要特别注意将父类异常URLError写在子类异常HTTPError的后面。如果URLError放在前面,出现HTTP异常会先响应URLError,这样HTTPError就捕获不到错误信息了。
我们看下面这个例子:
from urllib import request
from urllib import error
req=request.Request("http://blog.csdn.net/dxk.html")
try:
response=request.urlopen(req)
except error.HTTPError as e:
print(e.code)
print("HTTPError")
except error.URLError as e:
print(e.reason)
print("URLError")
如果捕捉到了HTTPError,则输出code,不再处理URLError。若没有发生HTTPError,则捕捉URLError异常,并输出原因。
结果如下:
404
HTTPError