python爬虫-基础

所谓网页抓取,就是把URL地址中指定的网络资源从网络流中读取出来,保存到本地。 
类似于使用程序模拟IE浏览器的功能,把URL作为HTTP请求的内容发送到服务器端, 然后读取服务器端的响应资源。

1、浏览网页的过程

抓取网页的过程其实和读者平时使用IE浏览器浏览网页的道理是一样的。

比如说你在浏览器的地址栏中输入    www.baidu.com    这个地址。

打开网页的过程其实就是浏览器作为一个浏览的“客户端”,向服务器端发送了 一次请求,把服务器端的文件“抓”到本地,再进行解释、展现。

HTML是一种标记语言,用标签标记内容并加以解析和区分。

浏览器的功能是将获取到的HTML代码进行解析,然后将原始的代码转变成我们直接看到的网站页面。

2 、URL与URI

 

2.1 URI

Web上每种可用的资源,如 HTML文档、图像、视频片段、程序等都由一个通用资源标志符(Universal Resource Identifier, URI)进行定位。 

URI通常由三部分组成:

①访问资源的命名机制;

②存放资源的主机名;

③资源自身 的名称,由路径表示。

如下面的URI:
http://www.why.com.cn/myhtml/html1223/

我们可以这样解释它:

①这是一个可以通过HTTP协议访问的资源,

②位于主机 www.why.com.cn上,

③通过路径“/myhtml/html1223/”访问。 

 

2.2 URL

简单的来讲,URL就是在浏览器端输入的    http://www.baidu.com    这个字符串。

URL是URI的一个子集。它是Uniform Resource Locator的缩写,译为“统一资源定位 符”。

通俗地说,URL是Internet上描述信息资源的字符串,主要用在各种WWW客户程序和服务器程序上。

采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。

 

URL的一般格式为(带方括号[]的为可选项):

protocol :// hostname[:port] / path / [;parameters][?query]#fragment

URL的格式由三部分组成: 

①第一部分是协议(或称为服务方式)。

②第二部分是存有该资源的主机IP地址(有时也包括端口号)。

③第三部分是主机资源的具体地址,如目录和文件名等。

第一部分和第二部分用“://”符号隔开,

第二部分和第三部分用“/”符号隔开。

第一部分和第二部分是不可缺少的,第三部分有时可以省略。 

2.3 比较

URI属于URL更低层次的抽象,一种字符串文本标准。

换句话说,URI属于父类,而URL属于URI的子类。URL是URI的一个子集。

URI的定义是:统一资源标识符;

URL的定义是:统一资源定位符。

二者的区别在于,URI表示请求服务器的路径,定义这么一个资源。

而URL同时说明要如何访问这个资源(http://)。

2.4 URL示例

 

http协议

使用超级文本传输协议HTTP,提供超级文本信息服务的资源。 

例:http://www.peopledaily.com.cn/channel/welcome.htm 

其计算机域名为www.peopledaily.com.cn。

超级文本文件(文件类型为.html)是在目录 /channel下的welcome.htm。

这是中国人民日报的一台计算机。 

例:http://www.rol.cn.net/talk/talk1.htm 

其计算机域名为www.rol.cn.net。

超级文本文件(文件类型为.html)是在目录/talk下的talk1.htm。

这是瑞得聊天室的地址,可由此进入瑞得聊天室的第1室。

文件

用URL表示文件时,服务器方式用file表示,后面要有主机IP地址、文件的存取路 径(即目录)和文件名等信息。

有时可以省略目录和文件名,但“/”符号不能省略。 

例:file://ftp.yoyodyne.com/pub/files/foobar.txt 

上面这个URL代表存放在主机ftp.yoyodyne.com上的pub/files/目录下的一个文件,文件名是foobar.txt。

例:file://ftp.yoyodyne.com/pub 

代表主机ftp.yoyodyne.com上的目录/pub。 

例:file://ftp.yoyodyne.com/ 

代表主机ftp.yoyodyne.com的根目录。 

3、urllib2

 

urllib2是Python的一个获取URLs(Uniform Resource Locators)的组件。

它以urlopen函数的形式提供了一个非常简单的接口。

response = urllib2.urlopen('http://www.baidu.com/')
html = response.read()
print html #打印的即是网页的源码

urllib2用一个Request对象来映射你提出的HTTP请求。

在它最简单的使用形式中你将用你要请求的地址创建一个Request对象,

通过调用urlopen并传入Request对象,将返回一个相关请求response对象,

这个应答对象如同一个文件对象,所以你可以在Response中调用.read()。

 

req = urllib2.Request('http://www.baidu.com') #处理http头
req2 = urllib2.Request('ftp://example.com/')  #处理ftp头,urllib2使用相同的接口处理所有的URL头。
response = urllib2.urlopen(req)
the_page = response.read()
print the_page

 

在HTTP请求时,允许你做额外的两件事。

(1)发送data表单数据

有时候你希望发送一些数据到URL(通常URL与CGI[通用网关接口]脚本,或其他WEB应用程序挂接)。

在HTTP中,这个经常使用熟知的POST请求发送。

这个通常在你提交一个HTML表单时由你的浏览器来做。

并不是所有的POSTs都来源于表单,你能够使用POST提交任意的数据到你自己的程序。

一般的HTML表单,data需要编码成标准形式。然后做为data参数传到Request对象。

编码工作使用urllib的函数而非urllib2。

import urllib
import urllib2

url = 'http://www.someserver.com/register.cgi'

values = {'name': 'WHY',
		  'location': 'SDU',
		  'language': 'Python'}

data = urllib.urlencode(values)  # 编码工作
req = urllib2.Request(url, data)  # 发送请求同时传data表单
response = urllib2.urlopen(req)  # 接受反馈的信息
the_page = response.read()  # 读取反馈的内容  

如果没有传送data参数,urllib2使用GET方式的请求。

GET和POST请求的不同之处是POST请求通常有"副作用",

它们会由于某种途径改变系统状态(例如提交成堆垃圾到你的门口)。

Data同样可以通过在Get请求的URL本身上面编码来传送。

 

(2)设置Headers到http请求

有一些站点不喜欢被程序(非人为访问)访问,或者发送不同版本的内容到不同的浏览器。

默认的urllib2把自己作为“Python-urllib/x.y”(x和y是Python主版本和次版本号,例如Python-urllib/2.7),

这个身份可能会让站点迷惑,或者干脆不工作。

浏览器确认自己身份是通过User-Agent头,当你创建了一个请求对象,你可以给他一个包含头数据的字典。

 

urlopen返回的应答对象response(或者HTTPError实例)有两个很有用的方法info()和geturl()

3.1 geturl()

这个返回获取的真实的URL,这个很有用,因为urlopen(或者opener对象使用的)或许会有重定向。获取的URL或许跟请求URL不同。

 

from urllib2 import Request, urlopen

old_url = 'http://i.baidu.com/?from=image'
req = Request(old_url)
response = urlopen(req)
print 'Old url :' + old_url
print 'Real url :' + response.geturl()

输出:
Old url :http://i.baidu.com/?from=image
Real url :http://i.baidu.com/welcome/

 

 

3.2 info()

这个返回对象的字典对象,该字典描述了获取的页面情况。通常是服务器发送的特定头headers。目前是httplib.HTTPMessage 实例。

经典的headers包含"Content-length","Content-type",和其他内容。

from urllib2 import Request, urlopen

old_url = 'http://www.baidu.com'
req = Request(old_url)
response = urlopen(req)
print 'Info():'
print response.info() 

输出:
Info():
Date: Tue, 14 Nov 2017 07:55:18 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: Close
Vary: Accept-Encoding
Set-Cookie: BAIDUID=6AD1DC5A0A2202A069705254C2CE007E:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=6AD1DC5A0A2202A069705254C2CE007E; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1510646118; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=0; path=/
Set-Cookie: H_PS_PSSID=1450_21102_24880_22075; path=/; domain=.baidu.com
P3P: CP=" OTI DSP COR IVA OUR IND COM "
Cache-Control: private
Cxy_all: baidu+164eb539e96e551026e6b365b859f30e
Expires: Tue, 14 Nov 2017 07:54:57 GMT
X-Powered-By: HPHP
Server: BWS/1.1
X-UA-Compatible: IE=Edge,chrome=1
BDPAGETYPE: 1
BDQID: 0xb02af5550000a934
BDUSERID: 0

 

3.3 openers/handles

Openers:

当你获取一个URL你使用一个opener(一个urllib2.OpenerDirector的实例)。

正常情况下,我们使用默认opener:通过urlopen。

但你能够创建个性的openers。

Handles:

Openers使用处理器handlers,所有的“繁重”工作由handlers处理。

每个handlers知道如何通过特定协议打开URLs,或者如何处理URL打开时的各个方面。

例如HTTP重定向或者HTTP cookies。

 

如果你希望用特定处理器获取URLs你会想创建一个openers,例如获取一个能处理cookie的opener,或者获取一个不重定向的opener。

要创建一个 opener,可以实例化一个OpenerDirector,然后调用.add_handler(some_handler_instance)。

 

同样,可以使用build_opener,这是一个更加方便的函数,用来创建opener对象,他只需要一次函数调用。
build_opener默认添加几个处理器,但提供快捷的方法来添加或更新默认处理器。

其他的处理器handlers你或许会希望处理代理,验证,和其他常用但有点特殊的情况。

 

install_opener 用来创建(全局)默认opener。这个表示调用urlopen将使用你安装的opener。

Opener对象有一个open方法。该方法可以像urlopen函数那样直接用来获取urls:通常不必调用install_opener,除了为了方便。

 

 

 

 

 4、异常处理

当urlopen不能够处理一个response时,产生urlError。
不过通常的Python APIs异常如ValueError,TypeError等也会同时产生。
HTTPError是urlError的子类,通常在特定HTTP URLs中产生。

4.1 URLError
通常,URLError在没有网络连接(没有路由到特定服务器),或者服务器不存在的情况下产生。

这种情况下,异常同样会带有"reason"属性,它是一个tuple(可以理解为不可变的数组),

包含了一个错误号和一个错误信息。

import urllib2

req = urllib2.Request('http://www.baibai.com')

try:
	urllib2.urlopen(req)

except urllib2.URLError, e:
	print e.reason  

输出:
[Errno 11002] getaddrinfo failed #错误号是11002,内容是getaddrinfo failed

 

4.2 HTTPError

服务器上每一个HTTP 应答对象response包含一个数字"状态码"。

有时状态码指出服务器无法完成请求。默认的处理器会为你处理一部分这种应答。

例如:假如response是一个"重定向",需要客户端从别的地址获取文档,urllib2将为你处理。

其他不能处理的,urlopen会产生一个HTTPError。

典型的错误包含"404"(页面无法找到),"403"(请求禁止),和"401"(带验证请求)。

HTTP状态码表示HTTP协议所返回的响应的状态。

比如客户端向服务器发送请求,如果成功地获得请求的资源,则返回的状态码为200,表示响应成功。

如果请求的资源不存在, 则通常返回404错误。 

HTTP状态码通常分为5种类型,分别以1~5五个数字开头,由3位整数组成:

 

200
请求成功      
处理方式:获得响应的内容,进行处理 
201
请求完成,结果是创建了新资源。新创建资源的URI可在响应的实体中得到    
处理方式:爬虫中不会遇到 
202
请求被接受,但处理尚未完成    
处理方式:阻塞等待 
204
服务器端已经实现了请求,但是没有返回新的信 息。如果客户是用户代理,则无须为此更新自身的文档视图。    
处理方式:丢弃
300
该状态码不被HTTP/1.0的应用程序直接使用, 只是作为3XX类型回应的默认解释。存在多个可用的被请求资源。    
处理方式:若程序中能够处理,则进行进一步处理,如果程序中不能处理,则丢弃
301
请求到的资源都会分配一个永久的URL,这样就可以在将来通过该URL来访问此资源    
处理方式:重定向到分配的URL
302
请求到的资源在一个不同的URL处临时保存     
处理方式:重定向到临时的URL 
304 
请求的资源未更新     
处理方式:丢弃 
400 
非法请求     
处理方式:丢弃 
401 
未授权     
处理方式:丢弃 
403 
禁止     
处理方式:丢弃 
404 
没有找到     
处理方式:丢弃 
5XX 
回应代码以“5”开头的状态码表示服务器端发现自己出现错误,不能继续执行请求    
处理方式:丢弃
HTTPError实例产生后会有一个整型'code'属性,是服务器发送的相关错误号。

Error Codes错误码
因为默认的处理器处理了重定向(300以外号码),并且100-299范围的号码指示成功,所以你只能看到400-599的错误号码。
BaseHTTPServer.BaseHTTPRequestHandler.response是一个很有用的应答号码字典,显示了HTTP协议使用的所有的应答号。

当一个错误号产生后,服务器返回一个HTTP错误号,和一个错误页面。

你可以使用HTTPError实例作为页面返回的应答对象response。

这表示和错误属性一样,它同样包含了read,geturl,和info方法。

 

import urllib2

req = urllib2.Request('http://bbs.csdn.net/callmewhy')

try:
	urllib2.urlopen(req)
except urllib2.URLError, e:
	print e.code
	#print e.read()

输出:
404

 

 

4.3 Wrapping

所以如果你想为HTTPError或URLError做准备,将有两个基本的办法。推荐使用第二种。

第一种

from urllib2 import Request, urlopen, URLError, HTTPError

req = Request('http://bbs.csdn.net/callmewhy')

try:
	response = urlopen(req)
	
except HTTPError, e:
	print 'The server couldn\'t fulfill the request.'
	print 'Error code: ', e.code

except URLError, e:
	print 'We failed to reach a server.'
	print 'Reason: ', e.reason

else:
	print 'No exception was raised.'

输出:
The server couldn't fulfill the request.
Error code:  404
和其他语言相似,try之后捕获异常并且将其内容打印出来。
这里要注意的一点,except HTTPError 必须在第一个,否则except URLError将同样接受到HTTPError 。
因为HTTPError是URLError的子类,如果URLError在前面它会捕捉到所有的URLError(包括HTTPError )。
第二种
from urllib2 import Request, urlopen, URLError, HTTPError

req = Request('http://bbs.csdn.net/callmewhy')

try:
	response = urlopen(req)

except URLError, e:
	if hasattr(e, 'code'):
		print 'The server couldn\'t fulfill the request.'
		print 'Error code: ', e.code

	elif hasattr(e, 'reason'):
		print 'We failed to reach a server.'
		print 'Reason: ', e.reason

else:
	print 'No exception was raised.'

输出:
The server couldn't fulfill the request.
Error code:  404
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 
 
posted on 2017-11-14 14:47  执剑长老  阅读(307)  评论(0编辑  收藏  举报