所谓⽹⻚抓取,就是把 URL 地址中指定的⽹络资源从⽹络流中读取出来,保存到本地。 在 Python 中,我们使⽤ urllib2 这个组件来抓取⽹⻚。
urllib2 是 Python2.7 ⾃带的模块(不需要下载),
是 Python 的⼀个获取URLs(Uniform Resource Locators)的重要组件。
urllib2 官⽅⽂档:https://docs.python.org/2/library/urllib2.html
urllib2 源码:https://hg.python.org/cpython/file/2.7/Lib/urllib2.py
urllib2 在 python3.x 中被改为 urllib.request
1.urlopen
我们先来段代码:
1 | # urllib2_baidu.py |
So Easy! 最简单的获取⼀个 url 的⻚⾯代码居然只需要 4⾏! 执⾏写的
python 代码:
1 | Power@PowerMac ~$: python urllib2_baidu.py |
会看到以下结果:
1 | <!--STATUS OK--><html><head><meta http-equiv="conten |
实际上,如果我们在浏览器上打开百度主⻚, 右键选择“查看源代码”,你会发现,跟我们刚才打印出来的是⼀模⼀样。也就是说,上⾯的4⾏代码就已经帮我们把百度的⾸⻚的全部代码爬了下来。
分析代码: 我们来分析⼀下上⾯的这 4⾏代码:
1.第⼀⾏:
1 | import urllib2 |
就是将 urllib2 组建引⼊进来,供给我们使⽤。
2.第⼆⾏
1 | response = urllib2.urlopen("http://www.baidu.com") |
然后我们调⽤的是 urllib2 库⾥⾯的 urlopen ⽅法,传⼊的 url⽹址是百度⾸⻚,
urlopen()⽅法⼀般接受三个参数:urlopen(url, data=None, timeout=
第⼀个参数 URL 是必须要传送的,可以传⼊⼀个字符串类型的url 地址,同时打开这个 url 并返回⼀个像⽂件对象⼀样的对象。
第⼆个参数是 data 是经过编码的 post 数据(⼀般使⽤urllib.urlencode()来编码,我们后⾯会说到),默认为空None;第三个参数是 timeout 是可选的超时期(以秒为单位),供所有阻塞操作内部使⽤。默认为 60s,也可以直接设置 timeout=10
3.第三⾏
1 | html = response.read() |
urlopen()返回的⽂件对象,除了⽀持⽂件⽅法外,还⽀持下⾯的这 些常⽤的⽅法:
-
response.getcode() 返回整数形式的 HTTP 响应代码,⽐如成功返回 200,未找到⽂件时返回 404
-
response.geturl() 返回所返回的数据的实际 url,但是会考虑发⽣的重定向问题
-
response.info() 返回映射对象,该对象带有与 url 关联的信息,对 HTTP 来说,
-
返回的服务器响应包含 HTTP 报头
4.第四⾏
1 | print html |
最后就是将字符串打出来,显示到终端上。
⼀个基本的 url 请求对应的 python 代码真的⾮常简单。
Request
我们编辑 urllib2_test2.py
1 | # urllib2_request.py |
运⾏结果是完全⼀样的:
在我们第⼀个例⼦⾥,urlopen()的 url 参数就是⼀个 url 地址;但是如果需要执⾏更复杂的操作,⽐如增加 HTTP 报头,可以创建⼀个Request 实例来作为 urlopen()的 url 参数,⽽url 地址则作为 Request 实例 的参数。

新建 Request 实例,url 为 url 字符串,data 是伴随 url 提交的数据(⽐如要 post 的数据),headers 是⼀个字典,包含了可表示 HTTP 报头的键值对,注意,data 请求为空时,默认 HTTP 请求为"GET",提供 data 参数时,HTTP 请求将从"GET"改为‘POST’。
2.User-Agent
但是这样直接⽤python的urllib2给⼀个⽹站发送请求的话,确实略有些唐突了,就好⽐,⼈家每家都有⻔,你以⼀个路⼈的身份直接闯进去显然不是很礼貌。所以有⼀些站点不喜欢被程序(⾮⼈为访问)访问,有可能会拒绝你的访问请求。

但是如果我们⽤⼀个合法的身份去请求别⼈⽹站,显然⼈家就是欢迎的。

所以我们就应该给我们的这个代码加上⼀个身份,就是所谓的 User-Agent 头。
User-Agent?显然如果你不是学习前端专业的,这个东⻄确实对于后端开发⼯程师是⼀个头疼的东⻄,
不过不要紧,不是我们的东⻄我们只作为了解即可。
我们只需要知道,⽤ 不同的浏览器 在发送请求的时候,会有不同的 UserAgent 头。
浏览器 就是互联⽹世界上 被允许的身份 。 那么如果你不想你的爬⾍代码成为⼀个路⼈,你需要伪装成⼀个被 公认的浏览器 。伪装的办法就是给⾃⼰的请求加上⼀个对应的 User-Agent 头。
1 | #urllib2_useragent.py |
3.添加更多的 Header 信息
-
在 HTTP Request 中加⼊特定的 Header,来构造⼀个完整的 HTTP 请求消息。
-
可以通过调⽤ Request.add_header() 添加/修改⼀个特定的 header
1 | # urllib2_headers.py |
headers 的⼀些属性,需要特别注意⼀下:
-
User-Agent : 有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求;
-
Content-Type : ⽤来确定 HTTP Body 中的内容该怎样解析,
服务 器会检查该值,设置错误会导致服务器拒绝服务
-
application/xml : 在 XML RPC 调⽤时使⽤
-
application/json : 在 JSON RPC 调⽤时使⽤
-
application/x-www-form-urlencoded : 浏览器提交 Web 表单时使⽤
4.数据传送
上⾯演示的都是最基本的⽹⻚抓取,有时候我们也希望发送⼀些数据到URL,
⽐如账号密码、表单数据等等,这样也能得到相应的响应。
urllib2 默认只⽀持 HTTP 的 GET 和 POST ⽅法
Get⽅式
GET 请求⼀般⽤于我们向服务器获取数据,⽐如说,我们⽤百度搜索 老男孩
https://www.baidu.com/s?wd= 老男孩

在其中我们可以看到在 http://www.baidu.com/s? 之后出现⼀个⻓⻓的字符串,其中就包含我们要查询的关键词。通过 Fiddler 观察,发现 URL 的QueryString 查询字符串的键是 wd ,于是我们可以尝试⽤默认的 Get⽅式来 发送请求。
1 | # urllib2_get.py |
⼀般 HTTP 请求提交 HTML 表单数据,word 需要编码成 URL 编码格式,然后 做为参数传到 Request 对象。
urllib 和 urllib2 都是接受 URL 请求的相关模块,但是提供了不同的功能。
两个最显著的不同如下:
-
urllib 仅可以接受 URL,⽽ urllib2 可以接受⼀个设置了 headers 的Request 类实例。这表示我们可以伪装⾃⼰的 User Agent 字符串等。
-
urllib 提供 urlencode ⽅法⽤来 GET 查询字符串的产⽣,⽽ urllib2没有。这是为何 urllib 常和 urllib2 ⼀起使⽤的原因。
-
编码⼯作使⽤urllib 的 urlencode() 函数,帮我们将 key:value 这样的键值对转换成 “key=value” 这样的字符串,解码⼯作可以使⽤urllib 的 unquote() 函数。(注意,不是 urllib2.urlencode() )
1 | # IPython2 中的测试结果 |
**POST⽅式: **
上⾯我们说了 Request 请求对象的⾥有 data 参数,它就是⽤在 POST⾥的,我 们要传送的数据就是这个参数 data,data 是⼀个字典,⾥⾯要匹配键值对。
拿拉勾⽹站数据举例,https://www.lagou.com/ 在站内搜索任意关键字 。 输⼊测试数据,再通过使⽤Fiddler 观察,其中有⼀条是 POST 请求,响应⽂件是 JSON 格式⽂件,⽽向服务器发送的请求数据并不是在 url⾥,那么我们可以试着模拟这个 POST 请求。

于是,我们可以尝试⽤POST⽅式发送请求
1 | # urllib2_post.py |
当然可以⽤post 的⽅式发送账号密码到登录界⾯模拟登陆,当⽹⻚采⽤JavaScript 动态技术以后,想封锁基于 HttpClient 的模拟登录就太容易了,甚⾄可以根据你的⿏标活动的特征准确地判断出是不是真⼈在操作。
所以,想做通⽤的模拟登录还得选别的技术,⽐如⽤内置浏览器引擎的爬⾍(关键词:Cookie,PhantomJS,Selenium),这个我们将在以后会学习到。
问题:为什么有时候 POST 也能在 URL 内看到数据?
-
GET⽅式是直接以链接形式访问,链接中包含了所有的参数,服务器端⽤Request.QueryString获取变量的值。如果包含了密码的话是⼀种不安全的选择,不过你可以直观地看到⾃⼰提交了什么内容。
-
POST则不会在⽹址上显示所有的参数,服务器端⽤Request.Form获取提交的数据,在 Form 提交的时候。但是 HTML 代码⾥如果不指 定 method 属性,则默认为 GET 请求,Form 中提交的数据将会附加 在 url 之后,以 ? 分开与 url 分开。
-
表单数据可以作为 URL 字段(method=“get”)或者 HTTP POST(method=“post”)的⽅式来发送。⽐如在下⾯的 HTML 代码中,表单数据将因为 (method=“get”) ⽽附加到 URL 上:
1 | <form action="form_action.asp" method="get"> |
5.⾃定义 Opener
-
基本的 urlopen()函数不⽀持代理、cookie 或其他的 HTTP⾼级功能。要⽀持这些功能,
必须使⽤ build_opener() 函数来创建⾃⼰的⾃定义opener 对象。
-
opener 是 urllib2.OpenerDirector 的实例,我们之前⼀直都在使⽤的urlopen,它是⼀个特殊的 opener
-
install_opener 将⾃定义的 opener 对象 定义为 全局 opener,表示如果之后凡是调⽤urlopen,都将使⽤这个 opener(根据⾃⼰的需求来选择)
6.Proxy(代理)的设置
很多⽹站会检测某⼀段时间某个 IP 的访问次数,如果访问次数过多,它会禁⽌你的访问。所以我们可以设置⼀些代理服务器,每隔⼀段时间换⼀个代理,⽹站管理员就不知道是谁在捣⻤了。
urllib2 中通过 ProxyHandler 来设置使⽤代理服务器,下⾯代码说明如何实⽤⾃定义 opener 来使⽤代理:
1 | #urllib2_proxy.py |
7.Debug Log
使⽤ urllib2 时,可以通过下⾯的⽅法把 HTTP 和 HTTPS 的 debug Log 打开,这样程序在执⾏的时候,会把收发包的内容在屏幕上打印出来,⽅便调试,有时可以省去抓包的⼯作 。
1 | # urllib2_debuglog.py |
8.Cookie
Cookie 是指某些⽹站的 Web 服务器为了辨别⽤户身份和进⾏Session 跟踪⽽储存在⽤户浏览器上的⽂本⽂件,Cookie 可以保持登录信息到⽤户下次与服务器的会话。
Cookie 由变量名和值组成,根据 Netscape 公司的规定,Cookie 格式如下:Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;
SECURE但是注意:
- 登录⼀般都会先有⼀个 HTTP GET,⽤于拉取⼀些信息及获得Cookie,然后再 HTTP POST 登录。
- http POST 登录的链接有可能是动态的,从 GET 返回的信息中获取。
- password 有些是明⽂发送,有些是加密后发送,有些甚⾄⽤动态加密的,包括了很多其他数据的加密信息,不只是密码。能通过查看JS 源码获得加密算法。
- ⼤多数⽹站的登陆整体流程类似,可能有些细节不⼀样,所以不能
保证其他⽹站登录成功。
cookielib 库
cookielib 模块的主要作⽤是提供⽤于存储 cookie 的对象,⼀般与 urllib2 模块配 合使⽤,Python 处理 cookie 是⼀般是 cookielib 和 HTTPCookieProcessor⼀ 起使⽤。
该模块主要的对象有 CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。
它们的关系:CookieJar —-派⽣—-> FileCookieJar(Cookie⽂件保存) —-派⽣—–> MozillaCookieJar(Firefox 浏览器 Cookie) 和 LWPCookieJar。
CookieJar
- 管理 HTTP cookie 值、存储 HTTP 请求⽣成的 cookie、向传出的 HTTP请求添加 cookie 的对象。整个 cookie 都存储在内存中,对 CookieJar 实例进⾏垃圾回收后 cookie 也将丢失。
FileCookieJar (filename,delayload=None,policy=None)
- 创建 FileCookieJar 实例,检索 cookie 信息并将 cookie 存储到⽂件中。filename 是存储 cookie 的⽂件名。delayload 为 True 时⽀持延迟访问访问⽂件,即只有在需要时才读取⽂件或在⽂件中存储数据
MozillaCookieJar (filename,delayload=None,policy=None)
- 创建与 Mozilla 浏览器 cookies.txt 兼容的 FileCookieJar 实例。
LWPCookieJar (filename,delayload=None,policy=None)
- 创建与 libwww-perl 的 Set-Cookie3⽂件格式兼容的 FileCookieJar 实例。
1)使⽤get⽅式获取 Cookie 保存到变量
1 | # urllib2_cookielibtest1.py |
我们使⽤以上⽅法将 cookie 保存到变量中,然后打印出了 cookie 中的值,运⾏结果如下:
1 | BAIDUID=4327A58E63A92B73FF7A297FB3B2B4D0:FG=1;BIDUPSID=4327A58E63A9 |
2) 访问⽹站获得 cookie,并把获得的 cookie 保存在 cookie⽂件中
1 | # urllib2_cookielibtest2.py |
3) 从⽂件中获取 cookies 并访问
1 | # urllib2_cookielibtest2.py |