• 通过 python下载csv文件并保存成csv文件


    直接上代码:

    import requests
    import csv
    from contextlib import closing
    
    
    # 保存csv文件
    def save_csv(f_name, data):
        # 1. 创建文件对象
        f = open(f_name, 'w', encoding='utf-8', newline='')
        # 2. 基于文件对象构建 csv写入对象
        csv_writer = csv.writer(f)
        # 4. 写入csv文件内容
        for row in data:
            csv_writer.writerow(row)
        # 5. 关闭文件
        f.close()
    
    # 下载csv文件
    def get_pag():
        url = '********'
    
        # 读取数据
        with closing(requests.get(url, stream=True)) as r:
            f = (line.decode('utf-8') for line in r.iter_lines())
            reader = csv.reader(f, delimiter=',', quotechar='"')
            save_csv(f_name, reader)
    
    if __name__ == '__main__':
        f_name = '123.csv'
        reader = get_pag()

    亮点来了:

    contextlib.closing 这个库,学习了.

    1、之前的我,只知道with会用来关闭文件,数据库资源,这很好。
      只要实现了__enter__() 和 __exit__()这两个方法的类都可以轻松创建上下文管理器,就能使用with。
     
    2、我打开两个数据库的时候,都是 
    with xxx as conn1:
        with yyy as conn2:
            code

      真是蠢如老狗呀,其实可以:

    with xxx as conn1, yyy as conn2:
        code
    3、总感觉离开了with block,语句体的资源(文件啊,数据库连接啊,网络请求呀)就会自动关闭。
      可是有一天我看到contextlib.closing()。 一脸懵逼,有了with还要这个干嘛,这是我内心真实OS。。
    from contextlib import closing
    from urllib2 import urlopen
     
    with closing(urlopen('http://www.python.org';)) as page:
        for line in page:
            print(line)

      先来否定我的想法,凡用with就万事大吉,自动帮我关闭。

    class Door(object):
        def open(self):
            print 'Door is opened'
     
        def close(self):
            print 'Door is closed'
     
    with Door() as d:
        d.open()

      结果:

    # 报错:
    Traceback (most recent call last):
      File "1.py", line 38, in <module>
        with Door() as d:
    AttributeError: __exit__
      果然呢,因为with语句体执行之前运行__enter__方法,在with语句体执行完后运行__exit__方法。
      如果一个类如Door连这两个方法都没有,是没资格使用with的。 
     
    4、好吧,正式认识下contextlib:https://docs.python.org/dev/library/contextlib.html
      有些类,并没有上述的两个方法,但是有close(),能不能在不加代码的情况下,使用with呢?
      可以: 
    class Door(object):
        def open(self):
            print 'Door is opened'
     
        def close(self):
            print 'Door is closed'
     
    with contextlib.closing(Door()) as door:
        door.open()

      结果:

    Door is opened
    Door is closed

      contextlib.closing(xxx),原理如下:

    class closing(object):
        """Context to automatically close something at the end of a block.
        Code like this:
            with closing(<module>.open(<arguments>)) as f:
                <block>
        is equivalent to this:
            f = <module>.open(<arguments>)
            try:
                <block>
            finally:
                f.close()
        """
        def __init__(self, thing):
            self.thing = thing
        def __enter__(self):
            return self.thing
        def __exit__(self, *exc_info):
            self.thing.close()

      这个contextlib.closing()会帮它加上__enter__()和__exit__(),使其满足with的条件。

    5、是不是只有类才能享受with的便利呀? 我单单一个方法行不行?
      行!既然认识了contextlib.closing(),必须认识下contextlib.contextmanager
      这是一个装饰器,可以让一个func()变成一个满足with条件的类实例… 

      !!!这个func()必须是生成器…
      yield前半段用来表示__enter__()
      yield后半段用来表示__exit__() 

    from contextlib import contextmanager
     
    @contextmanager
    def tag(name):
        print("<%s>" % name)
        yield
        print("</%s>" % name)
     
    with tag("h1"):
        print 'hello world!'

      结果:

    <h1>
    hello world!
    </h1>
      Wow,这个还真的挺酷的,以后可以用这个contextmanager来实现一些装饰器才能做的事,
      比如给一段代码加时间cost计算:
      装饰器版本: 
    import time
    def wrapper(func):
        def new_func(*args, **kwargs):
            t1 = time.time()
            ret = func(*args, **kwargs)
            t2 = time.time()
            print 'cost time=', (t2-t1)
            return ret
        return new_func
     
    @wrapper
    def hello(a,b):
        time.sleep(1)
        print 'a + b = ', a+b
     
    hello(100,200)

      结果:

    a + b =  300
    cost time= 1.00243401527

      contextmanger版本:

    from contextlib import contextmanager
     
    @contextmanager
    def cost_time():
        t1 = time.time()
        yield
        t2 = time.time()
        print 'cost time=',t2-t1
     
    with cost_time():
        time.sleep(1)
        a = 100
        b = 200
        print 'a + b = ', a + b

      结果:

    a + b =  300
    cost time= 1.00032901764

      当然还是用装饰器方便美观点啦~

    这是contextmanager原理:
    1、因为func()已经是个生成器了嘛,所以运行__enter__()的时候,contextmanager调用self.gen.next()会跑到func的yield处,停住挂起,这个时候已经有了t1=time.time()
    2、然后运行with语句体里面的语句,也就是a+b=300
    3、跑完后运行__exit__()的时候,contextmanager调用self.gen.next()会从func的yield的下一句开始一直到结束。这个时候有了t2=time.time(),t2-t1从而实现了统计cost_time的效果,完美。 
    源码:

    class GeneratorContextManager(object):
        """Helper for @contextmanager decorator."""
     
        def __init__(self, gen):
            self.gen = gen
     
        def __enter__(self):
            try:
                return self.gen.next()
            except StopIteration:
                raise RuntimeError("generator didn't yield")
     
        def __exit__(self, type, value, traceback):
            if type is None:
                try:
                    self.gen.next()
                except StopIteration:
                    return
                else:
                    raise RuntimeError("generator didn't stop")
            else:
                if value is None:
                    # Need to force instantiation so we can reliably
                    # tell if we get the same exception back
                    value = type()
                try:
                    self.gen.throw(type, value, traceback)
                    raise RuntimeError("generator didn't stop after throw()")
                except StopIteration, exc:
                    return exc is not value
                except:
                    if sys.exc_info()[1] is not value:
                        raise
     
     
    def contextmanager(func):
        @wraps(func)
        def helper(*args, **kwds):
            return GeneratorContextManager(func(*args, **kwds))
        return helper

    感谢博主 ouyangbro 的博客 https://blog.csdn.net/emaste_r/article/details/78105713

    感谢博主 大蛇王 的博客 https://blog.csdn.net/t8116189520/article/details/103408560

  • 相关阅读:
    HDOJ1203 I NEED A OFFER!
    在.NET上进行线性代数等科学计算 (转)
    Autodesk布道GIS新理念
    Arcmap中将点图层的样式修改为自定义的图片(转)
    NASA从太空发射激光,制作地球森林高度地图
    C#获取cpu序列号,硬盘ID,网卡MAC地址
    Fortress
    ArcGIS Engine开发-自定义图层类型(转)
    学术会议网站列表
    网络疯传IT男女标配图
  • 原文地址:https://www.cnblogs.com/yuanyongqiang/p/12609852.html
Copyright © 2020-2023  润新知