深入解析Python中的urllib2模块


Posted in Python onNovember 13, 2015

Python 标准库中有很多实用的工具类,但是在具体使用时,标准库文档上对使用细节描述的并不清楚,比如 urllib2 这个 HTTP 客户端库。这里总结了一些 urllib2 的使用细节。

  • Proxy 的设置
  • Timeout 设置
  • 在 HTTP Request 中加入特定的 Header
  • Redirect
  • Cookie
  • 使用 HTTP 的 PUT 和 DELETE 方法
  • 得到 HTTP 的返回码
  • Debug Log

Proxy 的设置

urllib2 默认会使用环境变量 http_proxy 来设置 HTTP Proxy。如果想在程序中明确控制 Proxy 而不受环境变量的影响,可以使用下面的方式

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)

这里要注意的一个细节,使用 urllib2.install_opener() 会设置 urllib2 的全局 opener 。这样后面的使用会很方便,但不能做更细粒度的控制,比如想在程序中使用两个不同的 Proxy 设置等。比较好的做法是不使用 install_opener 去更改全局的设置,而只是直接调用 opener 的 open 方法代替全局的 urlopen 方法。

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)

在 HTTP Request 中加入特定的 Header

要加入 header,需要使用 Request 对象:

import urllib2
request = urllib2.Request(uri)
request.add_header('User-Agent', 'fake-client')
response = urllib2.urlopen(request)

对有些 header 要特别留意,服务器会针对这些 header 做检查

User-Agent : 有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求

Content-Type : 在使用 REST 接口时,服务器会检查该值,用来确定 HTTP Body 中的内容该怎样解析。常见的取值有:

  • application/xml : 在 XML RPC,如 RESTful/SOAP 调用时使用
  • application/json : 在 JSON RPC 调用时使用
  • application/x-www-form-urlencoded : 浏览器提交 Web 表单时使用

在使用服务器提供的 RESTful 或 SOAP 服务时, Content-Type 设置错误会导致服务器拒绝服务

Redirect

urllib2 默认情况下会针对 HTTP 3XX 返回码自动进行 redirect 动作,无需人工配置。要检测是否发生了 redirect 动作,只要检查一下 Response 的 URL 和 Request 的 URL 是否一致就可以了。

import urllib2
response = urllib2.urlopen('http://www.google.cn')
redirected = response.geturl() == 'http://www.google.cn'

如果不想自动 redirect,除了使用更低层次的 httplib 库之外,还可以自定义 HTTPRedirectHandler 类。

import urllib2

class RedirectHandler(urllib2.HTTPRedirectHandler):
def http_error_301(self, req, fp, code, msg, headers):
pass
def http_error_302(self, req, fp, code, msg, headers):
pass

opener = urllib2.build_opener(RedirectHandler)
opener.open('http://www.google.cn')

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

使用 HTTP 的 PUT 和 DELETE 方法

urllib2 只支持 HTTP 的 GET 和 POST 方法,如果要使用 HTTP PUT 和 DELETE ,只能使用比较低层的 httplib 库。虽然如此,我们还是能通过下面的方式,使 urllib2 能够发出 PUT 或 DELETE 的请求:

import urllib2

request = urllib2.Request(uri, data=data)
request.get_method = lambda: 'PUT' # or 'DELETE'
response = urllib2.urlopen(request)

得到 HTTP 的返回码

对于 200 OK 来说,只要使用 urlopen 返回的 response 对象的 getcode() 方法就可以得到 HTTP 的返回码。但对其它返回码来说,urlopen 会抛出异常。这时候,就要检查异常对象的 code 属性了:

import urllib2
try:
response = urllib2.urlopen('http://restrict.web.com')
except urllib2.HTTPError, e:
print e.code
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')

PS: 借助urllib2抓取网站生成RSS
看了看OsChina的博客页面,发现可以使用python来抓取.记得前段时间看到有人使用python的RSS模块PyRSS2Gen生成了RSS.于是忍不住手痒自己试着实现了下,幸好还是成功了,下面代码共享给大家.
首先需要安装PyRSS2Gen模块和BeautifulSoup模块,pip安装下就好了,我就不再赘述了.
下面贴出代码

# -*- coding: utf-8 -*-


from bs4 import BeautifulSoup
import urllib2

import datetime
import time
import PyRSS2Gen
from email.Utils import formatdate
import re
import sys
import os
reload(sys)
sys.setdefaultencoding('utf-8')




class RssSpider():
  def __init__(self):
    self.myrss = PyRSS2Gen.RSS2(title='OSChina',
                  link='http://my.oschina.net',
                  description=str(datetime.date.today()),
                  pubDate=datetime.datetime.now(),
                  lastBuildDate = datetime.datetime.now(),
                  items=[]
                  )
    self.xmlpath=r'/var/www/myrss/oschina.xml'

    self.baseurl="http://www.oschina.net/blog"
    #if os.path.isfile(self.xmlpath):
      #os.remove(self.xmlpath)
  def useragent(self,url):
    i_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) \
  AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36", \
  "Referer": 'http://baidu.com/'}
    req = urllib2.Request(url, headers=i_headers)
    html = urllib2.urlopen(req).read()
    return html
  def enterpage(self,url):
    pattern = re.compile(r'\d{4}\S\d{2}\S\d{2}\s\d{2}\S\d{2}')
    rsp=self.useragent(url)
    soup=BeautifulSoup(rsp)
    timespan=soup.find('div',{'class':'BlogStat'})
    timespan=str(timespan).strip().replace('\n','').decode('utf-8')
    match=re.search(r'\d{4}\S\d{2}\S\d{2}\s\d{2}\S\d{2}',timespan)
    timestr=str(datetime.date.today())
    if match:
      timestr=match.group()
      #print timestr
    ititle=soup.title.string
    div=soup.find('div',{'class':'BlogContent'})
    rss=PyRSS2Gen.RSSItem(
               title=ititle,
               link=url,
               description = str(div),
               pubDate = timestr
               )

    return rss
  def getcontent(self):
    rsp=self.useragent(self.baseurl)
    soup=BeautifulSoup(rsp)
    ul=soup.find('div',{'id':'RecentBlogs'})
    for li in ul.findAll('li'):
      div=li.find('div')
      if div is not None:
        alink=div.find('a')
        if alink is not None:
          link=alink.get('href')
          print link
          html=self.enterpage(link)
          self.myrss.items.append(html)
  def SaveRssFile(self,filename):
    finallxml=self.myrss.to_xml(encoding='utf-8')
    file=open(self.xmlpath,'w')
    file.writelines(finallxml)
    file.close()



if __name__=='__main__':
  rssSpider=RssSpider()
  rssSpider.getcontent()
  rssSpider.SaveRssFile('oschina.xml')

可以看到,主要是使用BeautifulSoup来抓取站点然后使用PyRSS2Gen来生成RSS并保存为xml格式文件.
顺便共享下我生成的RSS地址

http://104.224.129.109/myrss/oschina.xml

大家如果不想折腾的话直接使用feedly订阅就行了.
脚本我会每10分钟执行一次的.

Python 相关文章推荐
Python生成验证码实例
Aug 21 Python
python通过自定义isnumber函数判断字符串是否为数字的方法
Apr 23 Python
再谈Python中的字符串与字符编码(推荐)
Dec 14 Python
对python 矩阵转置transpose的实例讲解
Apr 17 Python
Python os.rename() 重命名目录和文件的示例
Oct 25 Python
基于Python打造账号共享浏览器功能
May 30 Python
使用python批量修改文件名的方法(视频合并时)
Mar 24 Python
在Python中字符串、列表、元组、字典之间的相互转换
Nov 15 Python
Python3如何对urllib和urllib2进行重构
Nov 25 Python
python socket通信编程实现文件上传代码实例
Dec 14 Python
Python学习工具jupyter notebook安装及用法解析
Oct 23 Python
Python数据清洗工具之Numpy的基本操作
Apr 22 Python
Python下的常用下载安装工具pip的安装方法
Nov 13 #Python
Python的Flask框架的简介和安装方法
Nov 13 #Python
Python编程入门之Hello World的三种实现方式
Nov 13 #Python
python编程开发之textwrap文本样式处理技巧
Nov 13 #Python
python编程开发之日期操作实例分析
Nov 13 #Python
python编程开发之类型转换convert实例分析
Nov 13 #Python
python开发之文件操作用法实例
Nov 13 #Python
You might like
linux使用crontab实现PHP执行计划定时任务
2014/05/10 PHP
php检测mysql表是否存在的方法小结
2017/07/20 PHP
JavaScript高级程序设计 读书笔记之十 本地对象Date日期
2012/02/27 Javascript
nodejs中exports与module.exports的区别详细介绍
2013/01/14 NodeJs
浅析Javascript使用include/require
2013/11/13 Javascript
jquery基础教程之deferred对象使用方法
2014/01/22 Javascript
jquery选择器大全 全面详解jquery选择器
2014/03/06 Javascript
Javascript中的getUTCHours()方法使用详解
2015/06/10 Javascript
浅析函数声明和函数表达式——函数声明的声明提前
2016/05/03 Javascript
JavaScript lodash常见用法系列小结
2016/08/24 Javascript
jquery判断页面网址是否有效的两种方法
2016/12/11 Javascript
javaScript 逻辑运算符使用技巧整理
2017/05/03 Javascript
Vue-Router2.X多种路由实现方式总结
2018/02/09 Javascript
es6 filter() 数组过滤方法总结
2019/04/03 Javascript
javascript实现鼠标点击生成文字特效
2019/12/24 Javascript
基于vue实现微博三方登录流程解析
2020/11/04 Javascript
[01:15:00]LGD vs Mineski Supermajor 胜者组 BO3 第一场 6.5
2018/06/06 DOTA
[02:47]2018年度DOTA2最佳辅助位选手4号位-完美盛典
2018/12/17 DOTA
[52:02]DOTA2-DPC中国联赛 正赛 Phoenix vs Dragon BO3 第二场 2月26日
2021/03/11 DOTA
Python 性能优化技巧总结
2016/11/01 Python
使用python和Django完成博客数据库的迁移方法
2018/01/05 Python
python中单下划线_的常见用法总结
2018/07/10 Python
Python 实现文件打包、上传与校验的方法
2019/02/13 Python
Python 3 使用Pillow生成漂亮的分形树图片
2019/12/24 Python
Python3.7 读取音频根据文件名生成脚本的代码
2020/04/07 Python
Python 爬虫的原理
2020/07/30 Python
Pycharm无法打开双击没反应的问题及解决方案
2020/08/17 Python
html+css实现自定义图片上传按钮功能
2019/09/04 HTML / CSS
介绍一下linux的文件系统
2012/03/20 面试题
教师推荐信范文
2013/11/24 职场文书
大学运动会通讯稿
2014/01/28 职场文书
2014年公司庆元旦活动方案
2014/03/05 职场文书
聘任通知书
2015/09/21 职场文书
2019年描写人生经典诗句大全
2019/07/08 职场文书
超级实用!五步法则,教你写好年终工作总结
2019/12/05 职场文书
Python的这些库,你知道多少?
2021/06/09 Python