urllib2库的基本使用

urllib2库的基本使用

所谓网页抓取,就是把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

urlopen

我们先来段代码:

# urllib2_baidu.py

import urllib2

response = urllib2.urlopen("http://www.baidu.com")
html = response.read()

print html

So Easy! 最简单的获取一个url的页面代码居然只需要4行!

执行写的python代码:

Power@PowerMac ~$: python urllib2_baidu.py

会看到以下结果:

<!DOCTYPE html><!--STATUS OK--><html><head><meta http-equiv="conten
t-type" content="text/html;charset=utf-8"><meta http-equiv="X-UA-Co
mpatible" content="IE=Edge"><meta content="always" name="referrer">
<meta name="theme-color" content="#2932e1"><link rel="shortcut ico
n" href="/favicon.ico" type="image/x-icon" /><link rel="search" typ
e="application/opensearchdescription+xml" href="/content-search.xm
l" title="百度搜索" /><link rel="icon" sizes="any" mask href="//www.b
aidu.com/img/baidu.svg"><link rel="dns-prefetch" href="//s1.bdstati
c.com"/><link rel="dns-prefetch" href="//t1.baidu.com"/><link re
l="dns-prefetch" href="//t2.baidu.com"/><link rel="dns-prefetch" hr
ef="//t3.baidu.com"/><link rel="dns-prefetch" href="//t10.baidu.co
m"/><link rel="dns-prefetch" href="//t11.baidu.com"/><link rel="dn
s-prefetch" href="//t12.baidu.com"/><link rel="dns-prefetch" hre
f="//b1.bdstatic.com"/><title>百度一下,你就知道</title> ...

实际上,如果我们在浏览器上打开百度主页, 右键选择“查看源代码”,你会发现,跟我们刚才打印出来的是一模一样。也就是说,上面的4行代码就已经帮我们把百度的首页的全部代码爬了下来。

分析代码:

我们来分析一下上面的这4行代码:

  1. 第一行:

     import urllib2
    

    就是将urllib2组建引入进来,供给我们使用。

  2. 第二行

     response = urllib2.urlopen("http://www.baidu.com")
    

    然后我们调用的是 urllib2 库里面的 urlopen 方法,传入的url网址是百度首页,urlopen()方法一般接受三个参数:

    urlopen(url, data=None, timeout=<object object>)

    • 第一个参数URL是必须要传送的,可以传入一个字符串类型的url地址,同时打开这个 url 并返回一个像文件对象一样的对象。

    • 第二个参数是data是经过编码的post数据(一般使用urllib.urlencode()来编码,我们后面会说到),默认为空 None;

    • 第三个参数是timeout是可选的超时期(以秒为单位),供所有阻塞操作内部使用。默认为 60s,也可以直接设置timeout=10

  3. 第三行

         html = response.read()
    

    urlopen()返回的文件对象,除了支持文件方法外,还支持下面的这些常用的方法:

    • response.getcode() 返回整数形式的HTTP响应代码,比如成功返回200,未找到文件时返回404

    • response.geturl() 返回所返回的数据的实际url,但是会考虑发生的重定向问题

    • response.info() 返回映射对象,该对象带有与url关联的信息,对HTTP来说,返回的服务器响应包含HTTP报头

  4. 第四行

         print html
    

    最后就是将字符串打出来,显示到终端上。

一个基本的url请求对应的python代码真的非常简单。

Request

我们编辑urllib2_test2.py

# urllib2_request.py

import urllib2

request = urllib2.Request("http://www.baidu.com")
response = urllib2.urlopen(request)
html = response.read()

print html
运行结果是完全一样的:
  • 在我们第一个例子里,urlopen()的url参数就是一个url地址;

  • 但是如果需要执行更复杂的操作,比如增加HTTP报头,可以创建一个 Request 实例来作为urlopen()的url参数,而url地址则作为 Request实例的参数。

新建Request实例,url为url字符串,data是伴随 url 提交的数据(比如要post的数据),headers是一个字典,包含了可表示HTTP报头的键值对。

注意,data请求为空时,默认HTTP请求为"GET",提供data参数时,HTTP请求将从"GET"改为‘POST’。

User-Agent

但是这样直接用python的urllib2给一个网站发送请求的话,确实略有些唐突了,就好比,人家每家都有门,你以一个路人的身份直接闯进去显然不是很礼貌。所以有一些站点不喜欢被程序(非人为访问)访问,有可能会拒绝你的访问请求。

  • 但是如果我们用一个合法的身份去请求别人网站,显然人家就是欢迎的。

  • 所以我们就应该给我们的这个代码加上一个身份,就是所谓的User-Agent头。
  • User-Agent?显然如果你不是学习前端专业的,这个东西确实对于后端开发工程师是一个头疼的东西,不过不要紧,不是我们的东西我们只作为了解即可。

  • 我们只需要知道,用 不同的浏览器 在发送请求的时候,会有不同的 UserAgent 头。

  • 浏览器 就是互联网世界上 被允许的身份 。那么如果你不想你的爬虫代码成为一个路人,你需要伪装成一个被 公认的浏览器 。

  • 伪装的办法就是给自己的请求加上一个对应的User-Agent头。

#urllib2_useragent.py

import urllib2

url = "http://www.itcast.cn"

#IE 9.0 的 User-Agent
header = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} 

request = urllib2.Request(url, headers = header)
response = urllib2.urlopen(req)

html = response.read()

print html

添加更多的Header信息

在 HTTP Request 中加入特定的 Header,来构造一个完整的HTTP请求消息。

  • 可以通过调用Request.add_header() 添加/修改一个特定的header
  • 也可以通过调用Request.get_header()来查看已有的header。
# urllib2_headers.py

import urllib2

url = "http://www.itcast.cn"

#IE 9.0 的 User-Agent
header = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"} 
request = urllib2.Request(url, headers = header)

#也可以通过调用Request.add_header() 添加/修改一个特定的header
request.add_header("Connection", "keep-alive")

# 也可以通过调用Request.get_header()来查看header信息
# request.get_header(header_name="Connection")

response = urllib2.urlopen(req)

print response.code     #可以查看响应状态码
html = response.read()

print html
headers的一些属性,需要特别注意一下:
  • User-Agent : 有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求;
  • Content-Type : 用来确定 HTTP Body 中的内容该怎样解析,服务器会检查该值,设置错误会导致服务器拒绝服务
  • application/xml : 在 XML RPC 调用时使用
  • application/json : 在 JSON RPC 调用时使用
  • application/x-www-form-urlencoded : 浏览器提交 Web 表单时使用

进阶:数据传送

上面演示的都是最基本的网页抓取,有时候我们也希望发送一些数据到URL,比如账号密码、表单数据等等,这样也能得到相应的响应。

urllib2默认只支持HTTP的GETPOST方法

Get方式

GET请求一般用于我们向服务器获取数据,比如说,我们用百度搜索播客https://www.baidu.com/s?wd=汽车

浏览器的url会跳转成如图所示:

在其中我们可以看到在http://www.baidu.com/s?之后出现一个长长的字符串,其中就包含我们要查询的关键词。通过Fiddler观察,发现URL的QueryString查询字符串的键是 wd,于是我们可以尝试用默认的Get方式来发送请求。

# urllib2_get.py

import urllib      #负责url编码处理
import urllib2

url = "http://www.baidu.com/s"
word = {"wd":"汽车"}
word = urllib.urlencode(word) #转换成url编码格式(字符串)
newurl = url + "?" + word    # url首个分隔符就是 ?

headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"}

request = urllib2.Request(newurl, headers=headers)

response = urllib2.urlopen(request)

print response.read()
一般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() )

# IPython2 中的测试结果
In [1]: import urllib

In [2]: word = {"wd":"汽车"}

# 将字典按URL编码转换,汉字部分先转成GBK编码,然后把 \x 替换成 %
In [3]: urllib.urlencode(word)  
Out[3]: "wd=%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2"

# 把 % 替换成 \x,变回 GBK编码,打印出来
In [4]: print urllib.unquote("wd=%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2")
wd=汽车

POST方式:

上面我们说了Request请求对象的里有data参数,它就是用在POST里的,我们要传送的数据就是这个参数data,data是一个字典,里面要匹配键值对。

拿拉勾网站数据举例,https://www.lagou.com/,在站内搜索任意关键字。

输入测试数据,再通过使用Fiddler观察,其中有一条是POST请求,响应文件是JSON格式文件,而向服务器发送的请求数据并不是在url里,那么我们可以试着模拟这个POST请求。

于是,我们可以尝试用POST方式发送请求。

# urllib2_post.py

import urllib2
import urllib

output = open("lagou.json", "w")
page = 1

# POST 请求要传送的数据
formdata = "first=false&pn=" + str(page) + "&kd=xxx"

headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"}

request = urllib2.Request("http://www.lagou.com/jobs/positionAjax.json?px=new&needAddtionalResult=false", headers=headers)

# 通过request.add_data() 将 data数据传进入request内
request.add_data(formdata)
#print request.get_data()

response = urllib2.urlopen(request)
print response.code

#print resHtml
output.write(response.read())
output.close()
当然可以用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 上:

<form action="form_action.asp" method="get">
    <p>First name: <input type="text" name="fname" /></p>
    <p>Last name: <input type="text" name="lname" /></p>
    <input type="submit" value="Submit" />
</form>

自定义Opener

  • 基本的urlopen()函数不支持代理、cookie或其他的HTTP高级功能。要支持这些功能,必须使用 build_opener()函数来创建自己的自定义opener对象。

  • opener是urllib2.OpenerDirector的实例,我们之前一直都在使用的urlopen,它是一个特殊的opener

  • install_opener 将自定义的 opener对象 定义为 全局opener,表示如果之后凡是调用urlopen,都将使用这个opener(根据自己的需求来选择)

Proxy(代理)的设置

很多网站会检测某一段时间某个IP的访问次数,如果访问次数过多,它会禁止你的访问。所以我们可以设置一些代理服务器,每隔一段时间换一个代理,网站管理员就不知道是谁在捣鬼了。

urllib2中通过ProxyHandler来设置使用代理服务器,下面代码说明如何实用自定义opener来使用代理:

#urllib2_proxy.py

import urllib2

proxyWork = True #定义一个代理开关

# 定义了两个代理模式,其中一个为不适用代理
httpProxyHandler = urllib2.ProxyHandler({"http" : "124.88.67.81:80"})
nullProxyHandler = urllib2.ProxyHandler({})

if proxyWork:  #根据代理开关是否打开,使用不同的代理模式
    opener = urllib2.build_opener(httpProxyHandler)
else:
    opener = urllib2.build_opener(nullProxyHandler)

# 如果这么写,之后的urlopen将使用这个opener
#urllib2.install_opener(opener)
#response = urlopen("http://www.baidu.com/")

# 使用我们自定义的代理opener的open()方法打开url
response = opener.open("http://www.baidu.com/")

html = response.read()

print html

Debug Log

使用 urllib2 时,可以通过下面的方法把HTTP 和 HTTPS 的 debug Log 打开,这样程序在执行的时候,会把收发包的内容在屏幕上打印出来,方便调试,有时可以省去抓包的工作。

# urllib2_debuglog.py

import urllib2

# 打开 HTTP debug log
httpHandler = urllib2.HTTPHandler(debuglevel=1)
# 打开 HTTPS debug log
httpsHandler = urllib2.HTTPSHandler(debuglevel=1)

# 同时使用两种不同的 debug log 模式
opener = urllib2.build_opener(httpHandler, httpsHandler)

# 使用install_opener 用来创建全局的opener
urllib2.install_opener(opener)

# urlopen() 默认使用之前创建的全局opener
response = urllib2.urlopen("http://www.baidu.com")

Cookie

Cookie 是指某些网站的 Web 服务器为了辨别用户身份和进行Session跟踪而储存在用户浏览器上的文本文件,Cookie可以保持登录信息到用户下次与服务器的会话。

Cookie由变量名和值组成,根据Netscape公司的规定,Cookie格式如下:

Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE

但是注意:

  1. 登录一般都会先有一个HTTP GET,用于拉取一些信息及获得Cookie,然后再HTTP POST登录。
  2. http POST登录的链接有可能是动态的,从GET返回的信息中获取。
  3. password有些是明文发送,有些是加密后发送,有些甚至用动态加密的,包括了很多其他数据的加密信息,不只是密码。能通过查看JS源码获得加密算法。
  4. 大多数网站的登陆整体流程类似,可能有些细节不一样,所以不能保证其他网站登录成功。

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保存到变量
# urllib2_cookielibtest1.py

import urllib2
import cookielib

#声明一个CookieJar对象实例来保存cookie
cookie = cookielib.CookieJar()

#利用urllib2库的HTTPCookieProcessor对象来创建cookie处理器
handler=urllib2.HTTPCookieProcessor(cookie)

#通过handler来构建opener
opener = urllib2.build_opener(handler)

#此处的open方法同urllib2的urlopen方法,也可以传入request
response = opener.open("http://www.baidu.com")

# 按标准格式存储Cookie
cookies = ""
for item in cookie:
    cookies = cookies + item.name + "=" + item.value + ";"

# 舍去最后一位的分号
print cookies[:-1]

我们使用以上方法将cookie保存到变量中,然后打印出了cookie中的值,运行结果如下:

BAIDUID=4327A58E63A92B73FF7A297FB3B2B4D0:FG=1;BIDUPSID=4327A58E63A92B73FF7A297FB3B2B4D0;H_PS_PSSID=1429_21115_17001_21454_21409_21554_21398;PSTM=1480815736;BDSVRTM=0;BD_HOME=0
2. 访问网站获得cookie,并把获得的cookie保存在cookie文件中
# urllib2_cookielibtest2.py

import cookielib
import urllib2

# 设置保存cookie的文件,同级目录下的cookie.txt
filename = 'cookie.txt'

# 声明一个LWPCookieJar(有save实现)对象实例来保存cookie,之后写入文件
cookie = cookielib.LWPCookieJar(filename)

# 利用urllib2库的HTTPCookieProcessor对象来创建cookie处理器
handler = urllib2.HTTPCookieProcessor(cookie)

# 通过handler来构建opener
opener = urllib2.build_opener(handler)

# 创建一个请求,原理同urllib2的urlopen
response = opener.open("http://www.baidu.com")

# 保存cookie到文件,且忽略cookie失效限制
cookie.save(ignore_discard=True, ignore_expires=True)
3. 从文件中获取cookies并访问
# urllib2_cookielibtest2.py

import cookielib
import urllib2

# 创建LWPCookieJar(有load实现)实例对象
cookie = cookielib.LWPCookieJar()

# 从文件中读取cookie内容到变量,忽略cookie的使用时效
cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)

# 创建请求的request
req = urllib2.Request("http://www.baidu.com")

# 利用urllib2的build_opener方法创建一个opener
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))

response = opener.open(req)

print response.read()
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值