python网络爬虫学习笔记

By 钟桓

9月 4 2014 更新日期:9月 4 2014

介绍:

网络爬虫的名字非常有意思,英文名称web spider。

真得非常形象。蜘蛛结网为了获取食物,而我们的爬虫程序,也是为了获取网络上的资源。

这篇blog是本人学习过程中的记录。学习过程中,使用的语言是python2.7;python2.7有两个模块,urllib和urllib2,这两个模块提供了非常好的网络訪问的功能。以下会更好的体会。值得一提的时,在python3中。将urllib和urllib2这两个模块合为一个urllib。感兴趣的能够看这里

urllib和urllib2是python中功能强大得网络工作库,它们让你的网络訪问像文件訪问一样(比如。文件訪问我们要先open()一个文件。它的操作也是类似的,后面就会看到样例)。之所以可以这么方便。由于这些模块的内部非常好的使用不同的网络协议来完毕这些功能,(学过网络应该了解,訪问一个网页这个简单的过程,事实上涉及到非常多的网络协议,像http。dns等等,而urllib和urllib2封装了这些协议。让我们不用和它们打交道,仅仅须要调用这些模块的方法来完毕我们须要的功能)。同一时候,这些模块也提供一些略微更复杂一点的借口来处理一些情形,比如用户认证,cookies和代理等等。

以下让我们開始来学习它们吧。


从简单语句中開始:

前面说过。使用两个模块,訪问网页变得就会像訪问文件一样方便。

在普通情况下。urllib2訪问会更好些(效率上更好,只是urllib还是须要使用,后面会介绍须要urllib做一些事情)。所以以下我们来看看使用urllib2的最简单的样例。


import urllib2;

response = urllib2.urlopen("http://www.zhonghuan.info");
html = response.read();
print html;

在终端下下输入命令行 python test.py > zhonghuan.html ,

打开文件后显示的是我的个人blog首页的html代码:

这是最简单的一个利用urllib2訪问网页的样例。urllib2是依据URL中:前面的部分来推断是用什么协议訪问的,比如,上面的样例我们用的时http,这里也能够换成ftp:,file:,等。。。

我们能够不用去了解它内部是怎样封装这些网络协议的。

urllib2中能够用一个镜像对象(Request Object)来表示我们http訪问,它标示你想要訪问的URL地址,我们来看一下以下的样例。

import urllib2

req = urllib2.Request('http://www.zhonghuan.info')
response = urllib2.urlopen(req)
the_page = response.read()
print(the_page)

req变量就是一个Request对象。它确切的标示了你要訪问的URL地址。

(这里是http://www.zhonghuan.info)。对于其他的形式的訪问,比如ftp和file。形式也是类似的。详细能够看[这里][2];

事实上。Request对象还能做两个额外的事情。

  1. 你能够发送数据给server。
  2. 你能够发送一些额外的信息(又叫元数据。描写叙述数据的数据。一些语言里面的元类是生成类的类,如python就在这些语言中;所以元数据,顾名思义。描写叙述数据的数据,那么这些被描写叙述的数据是什么呢?上面中,还有request对象的一些信息。而这些描写叙述被放在http头部发送出去了。有关http header,能够看这里);

传送数据给server

有时候,你须要发送数据给server,这个地址是URL表示的。通常呢。这个地址的指向是CGI(Common Gateway Interface)脚本或者是其他一些网络应用。(关于CGI脚本,能够看这里。简单的说就是处理上传数据的脚本程序)。

在HTTP訪问中。通常使用哪个POST方式将数据发送出去,就好像你填完了html中得表单,你须要把表单中得数据发送出去。通常这里使用post请求。当然,post使用还有其他的情况。不单单指的是表单这一种情况。

让我们先看以下的代码:


import urllib
import urllib2 url = 'http://www.someserver.com/cgi-bin/register.cgi'
values = {'name' : 'Michael Foord',
'location' : 'Northampton',
'language' : 'Python' }
data = urllib.urlencode(values) #数据须要又一次编码成合适的格式,这里使用的时urllib中得方法,由于urllib2中没有编码的方法
req = urllib2.Request(url, data) # #这里将须要上传的数据。传递给了equest对象,作为它的參数
response = urllib2.urlopen(req)
the_page = response.read()

关于其他类型的数据上传,能够看这里

除了使用post方式上传数据外,还能够使用get方式上传数据,get上传和post上传明显的差别就是get上传的数据会在URL中得尾部显示出来。

能够看以下的代码:



import urllib
import urllib2 data = {}
data['name'] = 'Somebody Here'
data['location'] = 'Northampton'
data['language'] = 'Python' url_values = urllib.urlencode(data)
print url_values # 这里的顺序不一定 url = 'http://www.example.com/example.cgi'
full_url = url + '?' + url_values
data = urllib2.urlopen(full_url)

能够悄悄打印出来url_value的形式。


HTTP头—描写叙述数据的数据

如今。我们来讨论一下HTTP头。来看看怎样在你的HTTP的Request对象,添加一个HTTP头。

有一些站点,它比較智能,它不喜欢被程序訪问(非人为的点击仅仅会加重它server的负担)。

或者有些站点更加智能点。对于不同的浏览器,会发送不同的网页数据。

但是呢,urllib2默认,会这样标示自己,Python-urllib/x.y(当中。x和y各自是大小版本,比如我如今使用的时Python-urllib/2.7);而这些数据可能会让一些网站认为迷惑。要是遇上了不喜欢被程序訪问的网站,那么这种訪问可能会直接被忽视。

所以,你能够构造一些身份。让网站不会拒绝你。看以下的样例。


import urllib
import urllib2 url = 'http://www.someserver.com/cgi-bin/register.cgi'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' #user_agent用来标示你的浏览器的,向这里就是mozilla
values = {'name' : 'Michael Foord',
'location' : 'Northampton',
'language' : 'Python' } headers = { 'User-Agent' : user_agent }
data = urllib.urlencode(values) req = urllib2.Request(url, data, headers) response = urllib2.urlopen(req) the_page = response.read()


异常

异常时常有,要小心提防呐。想想一般文件操作的时候,会有什么异常呢?文件无法打开,什么权限不够啊。文件不存在啊等等异常。相同的。对于URL訪问。也会遇到这些问题,(python一些内部异常,比如ValueError,TypeError等异常。也可能会发生)

URLError

先说说URLError。当没有网络连接,或者訪问的server地址不存在的时候,在这样的情况下。URLError会被抛出来,这个时候,URLError异常会有个“reason”属性。它是一个元组。包括error code(int型)和text error message(string型),看以下的代码


import urllib
import urllib2 req = urllib2.Request('http://www.pretend_server.org')
try: urllib2.urlopen(req)
except urllib2.URLError as e:
print e.reason

输出[Errno 8] nodename nor servname provided, or not known;输出的内容就是reason。

HTTPError

每个HTTP訪问,会从server那儿获得一个“status code”(状态码)。通常这些状态码告诉我们server无法满足一些訪问(直白点说就是一些数据而已,仅仅只是表示的是当前訪问的状态,比方当前訪问被拒绝了,status code能够告诉你,你哪些举动过分了。出格了。注意,咸猪手不能够有啊~~)。

只是呢,urllib2的默认处理器可以帮助你处理一些server的响应,比方说你当前訪问的网址被server重定向了,就是说你的server给你一个新的URL訪问了,处理器会帮你直接去訪问新的URL。

可是默认的处理器毕竟功能有限。它不能帮助你解决全部问题。比方你訪问的站点不存在了(相应404错误。我们有时候会看到这个错误),或者你的訪问被禁止了(相应403错误,禁止的原因可能是由于你的权限不够啦等等),又或者是须要你验证啦(相应401)。详细的其他错误本文就不介绍啦,详细能够看这里

让我们看以下的程序,看一下当HTTPError的404错误,也就是页面不存在时候。它会输出点什么。


import urllib
import urllib2 req = urllib2.Request('http://www.zhonghuan.info/no_way')
try: urllib2.urlopen(req)
except urllib2.HTTPError as e:
print e.code;
print e.read();

输出:

404

<!DOCTYPE html>

<title>Page not found &middot; GitHub Pages</title>

处理异常

如果你想要捕捉HTTPError和URLError,有两种主要的方法。推荐另外一种噢!

第一种:


from urllib2 import Request, urlopen, URLError, HTTPError

req = Request(http://zhonghuan.info)
try:
response = urlopen(req)
except HTTPError as e:
print 'The server couldn\'t fulfill the request.'
print 'Error code: ', e.code
except URLError as e:
print 'We failed to reach a server.'
print 'Reason: ', e.reason
else:
# everything is fine

第一种方法。HTTPError一定要放在URLError前面,原因呢,和非常多语言的异常处理机制一样,HTTPError是URLError的子类,假设发生了HTTPError。它能够被当做是URLError被捕捉。

另外一种:

from urllib2 import Request, urlopen, URLError

req = Request(someurl)
try:
response = urlopen(req)
except URLError as e:
if hasattr(e, 'reason'):
print 'We failed to reach a server.'
print 'Reason: ', e.reason
elif hasattr(e, 'code'):
print 'The server couldn\'t fulfill the request.'
print 'Error code: ', e.code
else:
# everything is fine

info和geturl

这里介绍两个方法info()和geturl();

geturl():该方法会返回訪问的页面的真实的URL,它的价值在于我们訪问的网页可能会被重定向,所以导致訪问的URL和我们输入的可能不一样。看以下的样例:


import urllib
import urllib2 url = 'http://weibo.com/u/2103243911';
req = urllib2.Request(url);
response = urllib2.urlopen(req) print "URL:",url;
print "After redirection:",response.geturl();

以我的微博个人主页为例,事实上真实訪问被重定向了,真实的网址,从输出中能够看出:

URL: http://weibo.com/u/2103243911



After redirection: http://passport.weibo.com/visitor/visitor?

a=enter&url=http%3A%2F%2Fweibo.com%2Fu%2F2103243911&_rand=1409761358.1794

info():能够得到描写叙述页面的信息,返回的是一个httplib.HTTPMessage实例,打印出来非常像字典。

看以下的代码:


import urllib
import urllib2 url = 'http://zhonghuan.info';
req = urllib2.Request(url);
response = urllib2.urlopen(req);
print response.info();
print response.info().__class__;

输出:


Server: GitHub.com
Content-Type: text/html; charset=utf-8
Last-Modified: Tue, 02 Sep 2014 17:01:39 GMT
Expires: Wed, 03 Sep 2014 15:23:02 GMT
Cache-Control: max-age=600
Content-Length: 4784
Accept-Ranges: bytes
Date: Wed, 03 Sep 2014 16:38:29 GMT
Via: 1.1 varnish
Age: 5127
Connection: close
X-Served-By: cache-lax1433-LAX
X-Cache: HIT
X-Cache-Hits: 1
X-Timer: S1409762309.465760,VS0,VE0
Vary: Accept-Encoding Class: httplib.HTTPMessage


Opener和Handler

这里介绍Opener和Handler。

什么是Opener呢?事实上上面的样例我们一直在用Opener了,就是urlopen。这个是默认的opener。网络訪问情况非常多,你能够创建比較合适的opener,

什么是Handler呢?事实上Opener会调用Handler来处理訪问中得琐事,所以Handler非常重要,对于特定的协议(比如FTP,HTTP)。它知道怎样怎样处理訪问。比如它会帮你处理重定向问题。

在訪问的时候。你可能对于Opener有一些要求,比如,你希望得到的Opener可以处理cookie,或者你不希望Opener帮助你处理重定向。

我们怎样生成须要得Opener呢?(这里插一下,个人认为这里的Opener生成方式,和设计模式中得生成器欧式,又叫建造者模式,英文名称Builder Pattern;有些相似,只是不全然一样,但总认为,在看下去之前。先了解一下这个模式会有优点。没有接触过的朋友能够看这篇Builder
pattern
);

要创建一个 opener,能够实例化一个OpenerDirector,

然后调用.add_handler(some_handler_instance)。

只是,能够使用build_opener。这是一个更加方便的函数,用来创建opener对象,他仅仅须要一次函数调用。build_opener默认加入几个处理器,但提供快捷的方法来加入或更新默认处理器。

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

刚刚提到handler会帮我们处理重定向,可是。假设我们不想要重定向呢,该怎么办,自己定义一个handler。

看以下的代码:


mport urllib
import urllib2 class RedirectHandler(urllib2.HTTPRedirectHandler):# 这个RedirectHandler继承了HTTPRedirectHandler,只是,它覆盖了父类的方法,让它什么都不做。失去了重定向的功能。
def http_error_301(self, req, fp, code, msg, headers):
pass
def http_error_302(self, req, fp, code, msg, headers):
pass webo = "http://weibo.com/u/2103243911"; #訪问的是我的微博页面,由于正常情况下,訪问时会发生重定向
opener = urllib2.build_opener(RedirectHandler) #这里,我们自己定义了一个opener,加入了一个重定向时处理的自己定义handler
response = opener.open(webo);# response = urllib2.urlopen(webo);
print response.geturl();
urllib2.install_opener(opener); #安装自己定义的opener,以后调用urllib2的时候,返回的就是这个opener。

输出结果是:

urllib2.HTTPError: HTTP Error 302: Moved Temporarily

之所以发生http error 302,是由于本来訪问我的微博个人主页的时候。它应该发生重定向的,但是我们自己的重定向Handler什么都不做,结果就是发生异常了。

能够看看以下的urllib2关于创建自己定义Opener的类图


Basic Authentication

假设一个站点。他提供注冊登入这些功能。那么一般它实username/password,假设你訪问的页面,系统要求你提供username/password,这个过程叫做Authentication,实在server那端所做的操作。它给一些页面提供了安全保护。

一个主要的Authentication(验证)过程是这种:

  1. client提出请求訪问某些页面。

  2. server返回一个错误,要求进行身份验证。
  3. client把username/password(一般这样)编码后发给server。
  4. server检查这对username/password是否正确,然后返回用户请求的页面或者是一些错误。

上面的过程。还有可能是其他形式,这里仅仅是举个比較普遍的。

通常server返回的是401错误,表明訪问的网页未授权。同一时候。返回的response的header内有形如

WWW-Authenticate: SCHEME realm="REALM".

的内容,比如,你想要訪问cPanel的管理应用程序,你会收到这种header:WWW-Authenticate: Basic realm="cPanel"(cPanel 是一套在网页寄存业中最享负盛名的商业软件。其基于 Linux 和 BSD 系统及以 PHP 开发且性质为闭源软件;cPanel 主要是面向客户权级的控制系统)

当我们訪问页面的时候,opener会调用handler来处理各种情况。而处理Authentication的handler是urllib2.HTTPBasicAuthHandler。同一时候须要一个用户password管理器urllib2.HTTPPasswordMgr。

不幸的时。HTTPPasswordMgr有一个小问题,就是在获取网页前,你须要知道它的realm。幸运的是,它有一个表兄弟HTTPPasswordMgrWithDefaultRealm,这个表兄弟能够事先不知道realm,在realm參数位置上,能够传一个None进去。它更友好的被使用。

以下參考以下的代码:


import urllib2

url = 'http://www.weibo.com'#分别相应域名,账号。密码
username = 'zhonghuan'
password = 'forget_it'
passman = urllib2.HTTPPasswordMgrWithDefaultRealm() #创建密码管理器
passman.add_password(None, url, username, password)# 參数形式(realm,URL,UserName,Password)
authhandler = urllib2.HTTPBasicAuthHandler(passman)#创建Authentication的handler
opener = urllib2.build_opener(authhandler)
urllib2.install_opener(opener) #和上面介绍的一样。install_opener后,每次调用urllib2的urlopen,返回的就是这个opener
pagehandle = urllib2.urlopen(url)


代理

有时候,我们本机不能直接訪问,须要代理server去訪问。urllib2对这个设置代理支持的还不错,能够直接实例化ProxyHandler,它的參数是一个map,key值是代理的訪问协议名称,value值是代理的地址。看以下的代码实现。


import urllib2

enable_proxy = True
proxy_handler = urllib2.ProxyHandler({"http" : 'http://some-proxy.com:8080'})
null_proxy_handler = urllib2.ProxyHandler({})
if enable_proxy:
opener = urllib2.build_opener(proxy_handler)
else:
opener = urllib2.build_opener(null_proxy_handler)
urllib2.install_opener(opener)


Timeout 设置

在老版 Python 中,urllib2 的 API 并没有暴露 Timeout 的设置,要设置 Timeout 值,仅仅能更改 Socket 的全局 Timeout 值。


import urllib2
import socket socket.setdefaulttimeout(10) # 10 秒钟后超时
urllib2.socket.setdefaulttimeout(10) # 还有一种方式

在 Python 2.6 以后,超时能够通过 urllib2.urlopen() 的 timeout 參数直接设置。


import urllib2

response = urllib2.urlopen('http://www.google.com', timeout=10)


Cookie

urllib2 对 Cookie 的处理也是自己主动的。假设须要得到某个 Cookie 项的值,能够这么做:


import urllib2
import cookielib cookie = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
response = opener.open('http://www.google.com')
for item in cookie:
if item.name == 'some_cookie_item_name':
print item.value


Debug Log

使用 urllib2 时,能够通过以下的方法把 debug Log 打开,这样收发包的内容就会在屏幕上打印出来。方便调试。有时能够省去抓包的工作


import urllib2

httpHandler = urllib2.HTTPHandler(debuglevel=1)
httpsHandler = urllib2.HTTPSHandler(debuglevel=1)
opener = urllib2.build_opener(httpHandler, httpsHandler)
urllib2.install_opener(opener)
response = urllib2.urlopen('http://www.google.com')


參考资料:

  1. python urllib2的使用 (推荐)

  2. python网络爬虫新手教程 (推荐)

  3. CGI脚本入门 (推荐)

  4. urllib2 源代码小剖

  5. Python 标准库 urllib2 的使用细节 (推荐)

  6. Authentication with Python (推荐)

  7. http://en.wikipedia.org/wiki/List_of_HTTP_header_fields


    版权声明:本文博主原创文章。博客,未经同意不得转载。

    python网络爬虫学习笔记的更多相关文章

    1. python网络爬虫学习笔记(一)Request库

      一.Requests库的基本说明 引入Rquests库的代码如下 import requests 库中支持REQUEST, GET, HEAD, POST, PUT, PATCH, DELETE共7个 ...

    2. python网络爬虫学习笔记(二)BeautifulSoup库

      Beautiful Soup库也称为beautiful4库.bs4库,它可用于解析HTML/XML,并将所有文件.字符串转换为'utf-8'编码.HTML/XML文档是与“标签树一一对应的.具体地说, ...

    3. 关于Python网络爬虫实战笔记③

      Python网络爬虫实战笔记③如何下载韩寒博客文章 Python网络爬虫实战笔记③如何下载韩寒博客文章 target:下载全部的文章 1. 博客列表页面规则 也就是, http://blog.sina ...

    4. 关于Python网络爬虫实战笔记①

      python网络爬虫项目实战笔记①如何下载韩寒的博客文章 python网络爬虫项目实战笔记①如何下载韩寒的博客文章 1. 打开韩寒博客列表页面 http://blog.sina.com.cn/s/ar ...

    5. 《精通python网络爬虫》笔记

      <精通python网络爬虫>韦玮 著 目录结构 第一章 什么是网络爬虫 第二章 爬虫技能概览 第三章 爬虫实现原理与实现技术 第四章 Urllib库与URLError异常处理 第五章 正则 ...

    6. Python网络爬虫学习总结

      1.检查robots.txt 让爬虫了解爬取该网站时存在哪些限制. 最小化爬虫被封禁的可能,而且还能发现和网站结构相关的线索. 2.检查网站地图(robots.txt文件中发现的Sitemap文件) ...

    7. Python网络爬虫学习手记(1)——爬虫基础

      1.爬虫基本概念 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.--------百度百科 简单的说,爬 ...

    8. python网络爬虫学习

      网络爬虫 Requests官方中文教程地址:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html Beautiful So ...

    9. python网络编程学习笔记(三):socket网络服务器(转载)

      1.TCP连接的建立方法 客户端在建立一个TCP连接时一般需要两步,而服务器的这个过程需要四步,具体见下面的比较. 步骤 TCP客户端 TCP服务器 第一步 建立socket对象  建立socket对 ...

    随机推荐

    1. 使用Visual Studio 创建新的Web Part项目

      使用Visual Studio 创建新的Web Part项目 Web Part是你将为SharePoint创建的最常见的对象之中的一个.它是平台构建的核心基块. 1. 管理员身份打开Visual St ...

    2. python语言学习7——数据类型和变量

      整数 python可以处理任意大小的整数,包括负整数,在程序中的表示方法和数学上的写法一样 计算机由于使用二进制,有时候采用十六进制表示整数比较方便,十六进制数用0x前缀 浮点数 简单的小数就直接用小 ...

    3. gradle多模块开发(转)

      参考文档:gradle的官方userguide.pdf文档的chapter 55和chapter 56.gradle的多模块或项目开发一定不会比maven差,在我看来!大的项目分成多个模块来开发是常事 ...

    4. Redis缓存实现单点登录SSO

      .NET基于Redis缓存实现单点登录SSO的解决方案 .NET基于Redis缓存实现单点登录SSO的解决方案   一.基本概念 最近公司的多个业务系统要统一整合使用同一个登录,这就是我们耳熟能详的单 ...

    5. 【转】Vim学习资料

      初学资料:1:一个介绍VIM操作的游戏,十分适合初学者.只是:不要怕英文.vim-adventures.com2:http://blog.csdn.net/niushuai666/article/de ...

    6. STL之Vector(不定长数组)

      vector是同一种对象的集合,每一个对象都有一个相应的整数索引值.和string对象一样,标准库将负责管理与存储元素相关的类存. 引入头文件 #include<vector> 1.vec ...

    7. Java中字符串中子串的查找共有四种方法(indexof())

      Java中字符串中子串的查找共有四种方法(indexof()) Java中字符串中子串的查找共有四种方法,如下:1.int indexOf(String str) :返回第一次出现的指定子字符串在此字 ...

    8. Luci - UCI (Unified Configuration Interface)

      参考: http://wiki.openwrt.org/doc/techref/uc http://luci.subsignal.org/api/luci/modules/luci.model.uci ...

    9. ORA-16525: the Data Guard broker is not yet available

      DGMGRL> disable configuration;ORA-16525: the Data Guard broker is not yet available Configuration ...

    10. auto property synthesis will not synthesize proterty ;it will be implementedby its superclass, use @

      Auto property synthesis will not synthesize property 'title'; it will be implemented by its supercla ...