iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 18
0
Software Development

從零開始學Python系列 第 18

[Day 18] 從零開始學Python - 系統模組:走路的不是強尼,是你心中的OS

  • 分享至 

  • xImage
  •  

註:本文同步刊載在Medium,若習慣Medium的話亦可去那邊看呦!

我們先來看看昨天的練習吧!
一般來說,我們可以先打開來觀察,
將其複製到JSON Online Editor上看看右邊的tree長什麼樣子,
這樣會有助於後面思考操作方式。

  1. 請下載下來並放到你自己執行的目錄(筆者命名為bookstore.json)
  2. 用以下的讀取方式,將json檔載入到bs裡:
import json
with open('bookstore.json', 'r', encoding="utf-8") as f:
    bs = json.load(f)
  1. bs是一個串列(因為在這個json裡面是以array的型式儲存的)
  2. 我們可以用以下的方式(或者你要去用地址找也可以)
# 記得list comprehension嗎?
taipei = [i for i in bs if '臺北市' in i['cityName']]
  1. taipei中特色書店點閱數超過2000的店家是哪幾家呢?請列出其名字。
# 我們要的是名字,所以要取'name',判斷標準則是用hitRate。
>>> [i['name'] for i in taipei if i['hitRate'] > 2000]
['茉莉二手書店(臺大店)', '信誼小太陽親子書房(臺北重慶店)', '田園城市生活風格書店', '亞典藝術書店', '古原軒書店']

請留意到,這個練習是相對比較輕鬆的,
因為當中所用到的元素都沒有缺漏的部分,
所以不會產生任何錯誤;
但實務狀況上,常常會有一些資料是短少的,
這時候就要先做過資料的前處理(preprocessing)以後,
將缺漏的部分去除,或者補上適當的值,才能讓資料得以正常使用。
在資料清理(data cleaning)這部分,Python常用的有pandas跟numpy,
以後我們有機會再來介紹。

我們今天要來講的是系統模組(os)。
OS是Operating System(作業系統)的縮寫,
主要作用是提供你所使用的作業系統的一些功能,
例如檔案的路徑檢查、列出檔案列表、
檔案複製/移動/改名/刪除等。
下面的示範,我們是基於使用者在以下的資料夾環境操作的:

C:\Users\Desolve\utils>tree /f
列出資料夾 PATH
磁碟區序號為 D6BD-47D7
C:.
│  bookstore.json
│  check.py
│  fromzero.py
│  poem.txt
│  schedule.py
│  __init__.py
│
├─csv
│      student.csv
│      student_dic.csv
│
├─json
│      classA.json
│
└─__pycache__
        check.cpython-38.pyc
        schedule.cpython-38.pyc
        __init__.cpython-38.pyc

要使用os,最重要的就是先import啦!
請留意一件事情,
在Unix系列的系統裡面,路徑分隔資料夾或檔案是使用"/"來處理的
Windows系列的系統中,則是使用"\",兩者剛好相反
如果我們想看某個檔案或目錄是否存在於特定位置
我們可以使用os.path.exists(),可以是相對路徑或絕對路徑
相對路徑是從現在的資料夾為基準出發,來看到到目標位置要怎麼移動;
絕對路徑則是從Unix的根目錄或Windows的C槽D槽之類的起始點出發;
所以以相對路徑來起頭的,前面會有一個"./"或"."開頭
用來表示從現在所在位置起算(一個點代表現在所在位置,兩個點代表上一層)

>>> import os
>>> os.path.exists('check.py')
True
>>> os.path.exists('poem.txt')
True
>>> os.path.exists('.\json\classA.json')
True
>>> os.path.exists('./json/classA.json') # 實際上用linux的寫法給Python去認也行得通,Python會明白含義
True
>>> os.path.exists('./json/classB.json') # 不存在就回傳False
False
>>> os.path.exists('C:\Users\Desolve\utils\json\classA.json') # 用絕對路徑時避免不必要的麻煩,請多加一個"\"將可能造成的轉義去除
  File "<stdin>", line 1
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
>>> os.path.exists('C:\\Users\\Desolve\\utils\\json\\classA.json')
True

再來是分開來的檢查這個路徑是否是檔案/資料夾的方法:
os.path.isfile()os.path.isdir()

>>> os.path.isfile('./json/classA.json') # 存在,且是檔案
True
>>> os.path.isfile('./json/classB.json') # 不存在則回傳False
False
>>> os.path.isdir('./json/classA.json') # 是檔案,不是資料夾
False
>>> os.path.isdir('./json') # 是資料夾
True

那麼如果想要複製或移動檔案呢?這就要用到另一個模組shutil了:

>>> import shutil
>>> shutil.copy('poem.txt', 'poem2.txt') # 前面是來源,後面是目的地
'poem2.txt'
>>> shutil.move('poem2.txt', 'poem3.txt') # 移動檔案並更名
'poem3.txt'

要修改名字的話,則可以使用os.rename()

>>> os.rename('./json/poem3.txt', 'poem3.txt') # 也可以做為移動檔案用

要新增/刪除資料夾的話,可以使用os.mkdir()/os.rmdir()
刪除檔案則使用os.remove()
(有沒有覺得跟Unix系統的shell script很像呢XD?)

>>> os.mkdir('poems') # 開一個新的資料夾
>>> os.rename('poem3.txt', './poems/poem3.txt') # 將poem3.txt移入
>>> os.rmdir('poems') # 將poems資料夾刪除...咦?
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [WinError 145] 目錄不是空的。: 'poems'
>>> os.remove('poems/poem3.txt') # 只能先將檔案移除
>>> os.rmdir('poems') # 再移除資料夾

讀者可能會問:
「我怎麼會知道要先刪除哪些檔案?」
問的好!我們可以使用os.listdir()先列出資料夾的所有檔案及資料夾
(注意,只會列出第一層的,再往下的子資料夾就不會展開囉!)

>>> os.listdir()
['bookstore.json', 'check.py', 'csv', 'fromzero.py', 'json', 'poem.txt', 'schedule.py', '__init__.py', '__pycache__']
>>> os.listdir('csv')
['student.csv', 'student_dic.csv']

最後,是我們的終極必殺武器:os.walk()
os.walk()就像一個偏執的用路人一樣,
它就是要將整個資料夾,連同子資料夾都走過一遍才會罷休。
走完之後,所有的內容會以(dirpath, dirnames, filenames)的形式回傳,
我們可以使用for ... in ...的方式取得,
請看範例:

for root, dirs, files in os.walk('.'): # os.walk必須要給一個起始點
    print(root)
    for f in files:
        print(os.path.join(root, f)) # os.path.join()可用來處理路徑和檔案的結合
# 結果如下
.
.\bookstore.json
.\check.py
.\fromzero.py
.\poem.txt
.\schedule.py
.\__init__.py
.\csv
.\csv\student.csv
.\csv\student_dic.csv
.\json
.\json\classA.json
.\__pycache__
.\__pycache__\check.cpython-38.pyc
.\__pycache__\schedule.cpython-38.pyc
.\__pycache__\__init__.cpython-38.pyc

如果想要刪除掉某個目錄以下的所有檔案及資料夾,
可以參考Python的Document提供的範例:
請特別留意慎用以下操作,底下的動作可是真的會將目標資料夾全刪光的!!!
尤其是Unix系列的系統,如果給的top起始點是"/"(根目錄)而非"./"(當前目錄),
則會將整個檔案系統都砍光光呦!!!

for root, dirs, files in os.walk(top, topdown=False):
    for name in files:
        os.remove(os.path.join(root, name))
    for name in dirs:
        os.rmdir(os.path.join(root, name))

今天我們介紹了os中跟檔案系統相關操作有關的方法,
其他當然也有很多不錯的相關模組,
例如剛剛有提到的shutil,或是pathlib
有興趣的讀者可再行挖掘。

那麼,我們就明天見囉!


上一篇
[Day 17] 從零開始學Python - 檔案讀寫:妳出現在我詩的每一頁(下)
下一篇
[Day 19] 從零開始學Python - 日期與時間:你知不知道,我等到花兒也謝了
系列文
從零開始學Python30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
pythonithome
iT邦新手 5 級 ‧ 2022-02-24 13:43:53

Desolve前輩你好:
最近用os寫了一個程式,要將檔名有jan和feb的檔案分別移到jan和feb的資料夾遇到了問題,程式碼如下:
https://ithelp.ithome.com.tw/upload/images/20220224/20144788iZy6soQgax.jpg
https://ithelp.ithome.com.tw/upload/images/20220224/20144788doN8NNF3ET.jpg
結果出現了如上圖的錯誤我也上網google了好幾天,都找不到答案。請問前輩我是哪裡出錯了,謝謝前輩。

Desolve iT邦新手 5 級 ‧ 2022-02-24 15:13:50 檢舉

Hi,
看起來你沒有將檔案名放上去耶,
file_source你只有指定到路徑的部分,
建議你試著把m(檔名)的部分接續到file_source再試試看。

還是不行,請問我是放錯地方還是哪裡寫錯了。
https://ithelp.ithome.com.tw/upload/images/20220224/20144788jgGRSS98OH.jpg

Desolve前輩可以了。不好意思讓你看笑話了,這麼簡單的問題搞這麼久。對於程式出錯時的解決方法,以這次的例子來說python給的方向是錯誤的,那前輩對於程式設計新手而言,往後在寫程式時,如果遇到問題時你會建議如何尋找解決辦法或方向。

0
pythonithome
iT邦新手 5 級 ‧ 2022-06-07 10:20:37

Desolve前輩你好:
我用python程式抓取資料儲存成excel後,資料裏會有標點符號,要刪除,上google搜索都找不到解決的辦法。可以麻煩你幫我看一下是哪裡出錯嗎?麻煩你了謝謝。
以下是有標點符號的excel圖片和程式:
https://ithelp.ithome.com.tw/upload/images/20220607/20144788WKZsOESUou.jpg

if url == "https://www.tpex.org.tw/web/stock/3insti/qfii_trading/forgtr.php?l=zh-tw":
          options1 = browser.find_elements(By.XPATH, '//*[@id="forgtr_result"]/tbody/tr[1]/td[1]')   
#使用bs4的BeautifulSoup
          soup = BeautifulSoup(browser.page_source, 'html.parser')

#找到table
          data = soup.select('table')[0]

#使用read_html建立DataFrame
          df = pd.read_html(data.prettify(),header=1)
          dfs = df[0]
          newdfs = dfs.drop(['排行', '買進', '賣出', '買賣超(仟股)', '買進.1', '賣出.1', '買賣超(仟股).1', '買進.2', '賣出.2'], axis=1)
          newdfs.columns=[u"股票代號",u"名稱",u"買超張數"]
#取得投信買超張數大於50張的個股
          data = newdfs[newdfs["買超張數"]>=100]

#匯出成excel
          data.to_excel('D:/download/exe/python/foreginfounds/OTC/day/'+str(timeString)+'forOTC'+'.xlsx',index = False)
          browser.quit()
if url == "https://www.tpex.org.tw/web/stock/3insti/qfii_trading/forgtr.php?l=zh-tw":
          options1 = browser.find_elements(By.XPATH, '//*[@id="forgtr_result"]/tbody/tr[1]/td[1]')   
#使用bs4的BeautifulSoup
          soup = BeautifulSoup(browser.page_source, 'html.parser')

#找到table
          data = soup.select('table')[0]

#使用read_html建立DataFrame
          df = pd.read_html(data.prettify(),header=1)
          dfs = df[0]
          newdfs = dfs.drop(['排行', '買進', '賣出', '買賣超(仟股)', '買進.1', '賣出.1', '買賣超(仟股).1', '買進.2', '賣出.2'], axis=1)
          newdfs.columns=[u"股票代號",u"名稱",u"買超張數"]
#取得投信買超張數大於50張的個股
          data = newdfs[newdfs["買超張數"]>=100]

#匯出成excel
          data.to_excel('D:/download/exe/python/foreginfounds/OTC/day/'+str(timeString)+'forOTC'+'.xlsx',index = False)
          browser.quit()
Desolve iT邦新手 5 級 ‧ 2022-06-07 19:46:04 檢舉

我是沒辦法正常運行你的程式啦XD
用字串處理方法應該可以處理才對(如果你是指逗號的話)。
千分位的分隔在金融相關的表格裡應該蠻常見的,
如果真的不會的話可以以它為關鍵字搜尋應該有答案。
  
順便回應上次沒看到的問題,
Python在程式出錯的時候會給出traceback,
以前面你提供過的錯誤訊息來說,
我認為應該不會太難指出問題,
因為一般都會能看到最後出錯的起始點行數。

其實不論是新手還是老手而言,
遇到問題的關鍵還是在於想辦法拼湊出問題的核心點,
除錯的時候一時間看不出來很正常,
但還是不要直接認定Python給的資訊是錯的比較好。
只要是人就會犯錯,程式方面如果都是自己寫的,
先假定自己有地方寫錯會比較好。

總之:
遇到問題->讀錯誤資訊->
找trace的最開始發生點,看那行告訴你的資訊有什麼->
根據錯誤資訊去搜尋尋找正確的用法->
找不到的話,嘗試換關鍵字或者用更小單位的描述來搜尋->
找到合適的用法,放上去嘗試,
多print出來東西觀察自己的理解是否正確->
解決問題

大概是這樣,提供給你參考。

我要留言

立即登入留言