通過 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM