PythonD11——常用内建(itertools contextlib urllib XML HTMLParser)

本文深入探讨Python的内建函数,包括itertools的chain()与groupby(),contextlib上下文管理器在文件资源操作中的应用,urllib如何操纵URL以及XML与JSON的关联,最后介绍了HTMLParser解析HTML的基本用法。通过实例解析和作业,帮助读者更好地理解和掌握这些工具。

常用内建函数(续)

itertools操作迭代对象

import itertools
#无限打印自然数
natuals = itertools.count(1)
for n in natuals:
    print(n)
#无限打印输入序列
cs = itertools.cycle('ABC') # 注意字符串也是序列的一种
for c in cs:
    print(c)
#指定打印三次
ns = itertools.repeat('A', 3)
for n in ns:
    print(n)
#使用takewhile来限定范围
natuals = itertools.count(1)
ns = itertools.takewhile(lambda x: x <= 10, natuals)
list(ns)

一些常用迭代操作函数

chain()串联作用

chain()可以把一组迭代对象串联起来,形成一个更大的迭代器

groupby()

groupby()把迭代器中相邻的重复元素挑出来放在一起,可以消除重复元素。

for key, group in itertools.groupby('AaaBBbcCAAa', lambda c: c.upper()):
    print(key, list(group))
...
A ['A', 'a', 'a']
B ['B', 'B', 'b']
C ['c', 'C']
A ['A', 'A', 'a']

作业:计算π

def pi(N):
    ' 计算pi的值 '
    # step 1: 创建一个奇数序列: 1, 3, 5, 7, 9, ...
    qi = itertools.count(1,2)
    # step 2: 取该序列的前N项: 1, 3, 5, 7, 9, ..., 2*N-1.
    qu = itertools.takewhile(lambda x:x<=2*N-1,qi)
    # step 3: 添加正负符号并用4除: 4/1, -4/3, 4/5, -4/7, 4/9, ...
    qu_list = [4/x*(-1)**(idx%2) for idx,x in enumerate(list(qu))]
    #step3第二种
    qu_list = [4/x*(-1)**((x+1)/2-1) for x in qu]
    # step 4: 求和:
    return sum(qu_list)

contextlib操作文件资源

主要是通过with作为一个上下文管理器。

@contextmanager
def transaction(db):
    db.begin()
    tryyield 
    except:
        db.rollback()
        raise
    else:
        db.commit()
with transaction(mydb):
    mydb.cursor.execute(sql)
    mydb.cursor.execute(sql)
    mydb.cursor.execute(sql)
    mydb.cursor.execute(sql)

with执行过程:
执行 context_expression,生成上下文管理器 context_manager

调用上下文管理器的__enter__() 方法;如果使用了 as 子句,则将__enter__() 方法的返回值赋值给 as 子句中的 target(s)

执行语句体 with-body

不管是否执行过程中是否发生了异常,执行上下文管理器的__exit__() 方法__exit__() 方法负责执行“清理”工作,如释放资源等。如果执行过程中没有出现异常,或者语句体中执行了语句 break/continue/return,则以 None 作为参数调用__exit__(None, None, None) ;如果执行过程中出现异常,则使用 sys.exc_info 得到的异常信息为参数调用__exit__(exc_type, exc_value, exc_traceback)

出现异常时,如果__exit__(type, value, traceback) 返回 False,则会重新抛出异常,让with 之外的语句逻辑来处理异常,这也是通用做法;如果返回 True,则忽略异常,不再对异常进行处理

https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/index.html?mhsrc=ibmsearch_a&mhq=%E4%B8%8A%E4%B8%8B%E6%96%87%20Python
本体http://www.cnblogs.com/nnnkkk/p/4309275.html

urllib操纵url

urllib提供的功能就是利用程序去执行各种HTTP请求。

如果要模拟浏览器完成特定功能,需要把请求伪装成浏览器。

伪装的方法是先监控浏览器发出的请求,再根据浏览器的请求头来伪装,User-Agent头就是用来标识浏览器的。

from urllib import request

req = request.Request('http://www.douban.com/')
req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
with request.urlopen(req) as f:
    print('Status:', f.status, f.reason)
    for k, v in f.getheaders():
        print('%s: %s' % (k, v))
    print('Data:', f.read().decode('utf-8'))

更复杂的可以通过post和handle去访问。

作业:解析url json并转换

from urllib import request
import json
def fetch_data(url):
    with request.urlopen(str(url)) as f:
        return json.loads(f.read().decode('utf-8'))
    return None

XML类似json

操作XML有两种方法:DOM和SAX。DOM会把整个XML读入内存,解析为树,因此占用内存大,解析慢,优点是可以任意遍历树的节点。SAX是流模式,边读边解析,占用内存小,解析快,缺点是我们需要自己处理事件。

champhy_hu大佬的实例

from xml.parsers.expat import ParserCreate
#利用SAX解析XML文档牵涉到两个部分: 解析器和事件处理器
#解析器负责读取XML文档,并向事件处理器发送事件,如元素开始跟元素结束事件。
#而事件处理器则负责对事件作出响应,对传递的XML数据进行处理

class DefualtSaxHandler(object):
    def start_element(self,name,attrs):
        print('sax:start_elment: %s,attrs: %s'%(name,str(attrs)))
        #name表示节点名称,attrs表示节点属性(字典)
    def end_element(self,name):
        print('sax:end_element: %s'%name)

    def char_data(self,text):
        print('sax:char_data: %s'%text)
        #text表示节点数据
xml=r'''<?xml version="1.0"?>
<ol>
    <li><a href="/python">Python</a></li>
    <li><a href="/ruby">Ruby</a></li>
</ol>
'''

#处理器实例
handler=DefualtSaxHandler()
#解析器实例
parser=ParserCreate()

#下面3为解析器设置自定义的回调函数
#回调函数的概念,请搜索知乎,见1.9K赞的答案
parser.StartElementHandler=handler.start_element
parser.EndElementHandler=handler.end_element
parser.CharacterDataHandler=handler.char_data
#开始解析XML
parser.Parse(xml)
#然后就是等待expat解析,
#一旦expat解析器遇到xml的 元素开始,元素结束,元素值 事件时
#会回分别调用start_element, end_element, char_data函数

#关于XMLParser Objects的方法介绍下
#详见python文档:xml.parsers.expat
#xmlparser.StartElementHandler(name, attributes)
#遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典
#xmlparser.EndElementHandler(name)
#遇到XML结束标签时调用。
#xmlparser.CharacterDataHandler(data) 
#调用时机:
#从行开始,遇到标签之前,存在字符,content 的值为这些字符串。
#从一个标签,遇到下一个标签之前, 存在字符,content 的值为这些字符串。
#从一个标签,遇到行结束符之前,存在字符,content 的值为这些字符串。
#标签可以是开始标签,也可以是结束标签。

#为了方便理解,我已经在下面还原来解析过程,
#标出何时调用,分别用S:表示开始;E:表示结束;D:表示data

如果看不明白,请配合脚本输出结果一起看
S<ol>C
C   S<li>S<a href="/python">CPython</a>E</li>EC
C   S<li>S<a href="/ruby">CRuby</a>E</li>EC
S</ol>E

作业:写一个可以输出天气的api

'''练习:
请利用SAX编写程序解析XML格式的天气预报,获取天气预报:

功能:获取北京市所有地区的当天天气预报信息——(地区名:{当日天气状态:’‘,最高温度:’‘,最低温度})
'''
# -*- coding:utf-8 -*-

from xml.parsers.expat import ParserCreate            # 引入xml解析模块
from urllib import request                            # 引入URL请求模块

class WeatherSaxHandler(object):                      # 定义一个天气事件处理器
    weather ={'city':1,'cityname':[], 'forecast':[]}               # 初始化城市city和预报信息forecast

    def start_element(self, name, attrs):             # 定义开始标签处理事件
        if name=='beijing':
            self.weather['city']='北京'
        if name == 'city':               # 获取location信息
            self.weather['cityname'].append(attrs['cityname'])          #获取地区名
            # 获取forecast信息
            self.weather['forecast'].append({
                'state':attrs['stateDetailed'], 
                'high':attrs['tem2'], 
                'low':attrs['tem1']
            })

def parseXml(xml_str):                                # 定义xml解析器

    handler = WeatherSaxHandler()
    parser = ParserCreate()
    parser.StartElementHandler = handler.start_element
    parser.Parse(xml_str)                             # 解析xml文本
    print('City'+handler.weather['city'])
    for (x,y) in zip(handler.weather['cityname'],handler.weather['forecast']):             # 打印天气信息
        print('Region:'+x)
        print(y)
       
    return handler.weather
    

# 测试:
URL = 'http://flash.weather.com.cn/wmaps/xml/beijing.xml'

with request.urlopen(URL, timeout=4) as f:
    data = f.read()

result = parseXml(data.decode('utf-8'))

'''结果
City北京
Region:延庆
{'state': '晴', 'high': '7', 'low': '-7'}
Region:密云
{'state': '晴', 'high': '9', 'low': '-6'}
Region:怀柔
{'state': '晴', 'high': '10', 'low': '-6'}
Region:昌平
{'state': '晴', 'high': '9', 'low': '-1'}
Region:平谷
{'state': '晴', 'high': '8', 'low': '-5'}
Region:顺义
{'state': '晴', 'high': '10', 'low': '0'}
Region:门头沟
{'state': '晴', 'high': '10', 'low': '-2'}
Region:海淀
{'state': '晴', 'high': '11', 'low': '-2'}
Region:朝阳
{'state': '晴', 'high': '10', 'low': '-1'}
Region:石景山
{'state': '晴', 'high': '11', 'low': '-2'}
Region:市中心
{'state': '晴', 'high': '9', 'low': '-2'}
Region:丰台
{'state': '晴', 'high': '10', 'low': '-2'}
Region:房山
{'state': '晴', 'high': '9', 'low': '-4'}
Region:大兴
{'state': '晴', 'high': '9', 'low': '-4'}
Region:通州
{'state': '晴', 'high': '9', 'low': '-2'}
'''

HTMLParser

如果我们要编写一个搜索引擎,第一步是用爬虫把目标网站的页面抓下来,第二步就是解析该HTML页面,看看里面的内容到底是新闻、图片还是视频。

解析HTML就要用到HTMLParser

# -*-coding:UTF-8-*-

from html.parser import HTMLParser
from urllib.request import Request,urlopen
import re

def get_data(url):
   '''
   GET请求到指定的页面
   :return: HTTP响应
   '''

   headers = {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36'
      }
   req = Request(url, headers=headers)
   with urlopen(req, timeout=25) as f:
      data = f.read()
      print(f'Status: {f.status} {f.reason}')
      print()
   return data.decode("utf-8")

class MyHTMLParser(HTMLParser):
   def __init__(self):
      super().__init__()
      self.__parsedata='' # 设置一个空的标志位
      self.info = []

   def handle_starttag(self, tag, attrs):
      if ('class', 'event-title') in attrs:
         self.__parsedata = 'name'  # 通过属性判断如果该标签是我们要找的标签,设置标志位
      if tag == 'time':
         self.__parsedata = 'time'
      if ('class', 'say-no-more') in attrs:
         self.__parsedata = 'year'
      if ('class', 'event-location') in attrs:
         self.__parsedata = 'location'

   def handle_endtag(self, tag):
      self.__parsedata = ''# 在HTML 标签结束时,把标志位清空

   def handle_data(self, data):

      if self.__parsedata == 'name':
         # 通过标志位判断,输出打印标签内容
         self.info.append(f'会议名称:{data}')

      if self.__parsedata == 'time':
         self.info.append(f'会议时间:{data}')

      if self.__parsedata == 'year':
         if re.match(r'\s\d{4}', data): # 因为后面还有两组 say-no-more 后面的data却不是年份信息,所以用正则检测一下
            self.info.append(f'会议年份:{data.strip()}')

      if self.__parsedata == 'location':
         self.info.append(f'会议地点:{data} \n')

def main():
   parser = MyHTMLParser()
   URL = 'https://www.python.org/events/python-events/'
   data = get_data(URL)
   parser.feed(data)
   for s in parser.info:
      print(s)

if __name__ == '__main__':
   main()
#第二种
from html.parser import HTMLParser
from urllib import request

class MyHTMLParser(HTMLParser):

    def __init__(self):
        super().__init__()
        self.flag = 0  # 状态 1:目标标签 0:不是目标标签

    def handle_starttag(self, tag, attrs):
        if tag == 'h3' and ('class', 'event-title') in attrs:  # 筛选会议名称
            self.flag = 1
        elif tag == 'time' and 'datetime' in attrs[0]:  # 筛选会议时间
            self.flag = 1
        elif tag == 'span' and ('class', 'event-location') in attrs:  # 筛选会议地点
            self.flag = 1

    def handle_data(self, data):
        if self.flag:
            print(data)
            self.flag = 0  # 还原状态


with request.urlopen('https://www.python.org/events/python-events/') as f:
    data = f.read().decode('utf-8')

parser = MyHTMLParser()
parser.feed(data)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值