# 使用 Pandas 從網頁讀取衛星軌道資料並儲存成 csv 檔 > 作者:王一哲 > 日期:2021/7/20 ## 前言 我以前寫過一篇關於[克卜勒第三行星運動定律](https://hackmd.io/@yizhewang/SJxnasj9S)的文章,為了將各行星的衛星資料整理成可用的 csv 檔,當時是將表格複製到 LibreOffice Calc 裡面再手動整理,但是使用 Pandas 可以更自動化,以下是我試出來的方法。 <br /> ## 程式碼 為了避免找不到儲存後的 csv 檔案,建議於 Python Shell 中用以下指令查詢目前所在的路徑。 ```python import os os.getcwd() ``` 如果使用 Python 預設的 IDLE,在 Windows 10 預設的路徑是 ``` C:\Users\[UserName]\AppData\Local\Programs\Python\[PythonXX] ``` 在 Ubuntu 預設的路徑是 ``` /home/[UserName] ``` 如果在 Windows 10 可以用以下指令將路徑切換到桌面 ```python os.chdir("C:\\Users\\[UserName]\\Desktop") ``` <br /> 接下來從 [Jovian Satellite Fact Sheet](https://nssdc.gsfc.nasa.gov/planetary/factsheet/joviansatfact.html) 網頁的表格中取得木星衛星軌道資料,在網頁上點擊滑鼠右鍵**檢視網頁原始碼**,於原始碼中搜尋**table class**會看到有兩個表格,名稱分別為 **bulk** 及 **orbital**,我們需要的是衛星軌道資料,因此讀取時需要加上 **attrs={'class':'orbital'}**;為了將最上面的列當作欄位標題,需要加上 **header=0**。 ```python import pandas as pd table = pd.read_html('https://nssdc.gsfc.nasa.gov/planetary/factsheet/joviansatfact.html', attrs={'class':'orbital'}, header=0) ``` <br /> 將資料複製到 data 中,格式為 pandas.DataFrame,再將 data 儲存成 data.csv。 ```python data = table[0].copy() data.to_csv('data.csv') ``` <br /> 如果用文字編輯器打開 data.csv 會看到以下的檔案。 ``` ,Unnamed: 0,Semi-major axis(103km),Semi-major axis(Jovian Radii),OrbitalPeriod*(days),RotationPeriod(days),Inclination(degrees),Eccentricity 0,,,,,,, 1,Galilean Satellites,,,,,, 2,Io (JI),421.8,5.91,1.769138,S,0.04,0.004 3,Europa (JII),671.1,9.4,3.551181,S,0.47,0.009000000000000001 4,Ganymede (JIII),1070.4,14.97,7.154553,S,0.18,0.001 5,Callisto (JIV),1882.7,26.33,16.689017,S,0.19,0.006999999999999999 ... 92,S/2018 J1,11483,160.6,252.0,,30.61,0.094 ``` <br /> 從 csv 檔中可以看到每個欄位的名稱,如果想要直接在 Python Shell 裡印出欄位名稱可以使用以下的指令。 ```python for col in data.columns: print(col) ``` <br /> 目前的欄位名稱為 ``` Unnamed: 0 Semi-major axis(103km) Semi-major axis(Jovian Radii) OrbitalPeriod*(days) RotationPeriod(days) Inclination(degrees) Eccentricity ``` 由於網頁的表格中,最左側的欄位沒有標題,pandas 會將這欄的名稱設定為 **Unnamed: 0**,使用以下指令將此欄的名稱改為 **Name**,指令最後的 **inplace=True** 是為了將修改後的資料儲存到 data 當中。 ```python data.rename(columns={'Unnamed: 0': 'Name'}, inplace=True) ``` <br /> 由於表格中有一些空白列,還有4列只有衛星分類的名稱 Galilean Satellites、Lesser Satellites、Unnamed Satellites、Recently Discovered Satellites,可以用以下的指令刪除這些資料。 ```python data.dropna(subset=['Semi-major axis(103km)'], inplace=True) ``` 如果只要刪除整列都是空白的資料,語法為 ```python data.dropna(how='all', inplace=True) ``` <br /> 由於 **Name** 欄位的某些資料有**逗號**,雖然可以儲存成 csv 檔,但是要再從 csv 檔讀取資料時會遇到麻煩,需要先刪除這些逗號。 ```python data['Name'] = data['Name'].replace(',', '', regex=True) ``` <br /> 在欄位 **OrbitalPeriod\*(days)** 當中有許多的資料數字後面多了一個字母 R,這是網頁上用來標示該衛星為公轉方向為逆行 (retrograde),但是我只想留下數字,並在 data 最後面新增欄位記錄這個衛星是否為逆行。使用以下指令新增欄位 **Retrograde**,並將值設定為 **False**。 ```python data['Retrograde'] = False ``` 找出 **OrbitalPeriod\*(days)** 欄位資料**以 R 結尾**的列,並將 **Retrograde** 欄位資料改為 **True**,如果沒有先新增欄位並設定預設值而是直接使用以下指令,則其它列的 **Retrograde** 欄位值為 **NaN**。 ```python data.loc[data['OrbitalPeriod*(days)'].str.endswith('R'), 'Retrograde'] = True ``` 刪除 **OrbitalPeriod\*(days)** 欄位資料**結尾的R**。 ```python data['OrbitalPeriod*(days)'] = data['OrbitalPeriod*(days)'].map(lambda x: x.rstrip('R')) ``` <br /> 以下是完整的程式碼。 ```python= import pandas as pd planet = 'Juipter' table = pd.read_html('https://nssdc.gsfc.nasa.gov/planetary/factsheet/joviansatfact.html', attrs={'class':'orbital'}, header=0) data = table[0].copy() data.rename(columns={'Unnamed: 0': 'Name'}, inplace=True) data.dropna(subset=['Semi-major axis(103km)'], inplace=True) data['Name'] = data['Name'].replace(',', '', regex=True) data['Retrograde'] = False data.loc[data['OrbitalPeriod*(days)'].str.endswith('R'), 'Retrograde'] = True data['OrbitalPeriod*(days)'] = data['OrbitalPeriod*(days)'].map(lambda x: x.rstrip('R')) data.to_csv(planet+'SatelliteData.csv') ``` <br /> ## 結語 因為木星、土星、天王星的衛星資料網頁格式幾乎一樣,只要將行星的名稱及讀取資料的網址改掉,就可以將土星、天王星的衛星資料也儲存成 csv 檔。以下是木星、土星、天王星的衛星 log a - log T 關係圖,斜率都很接近 2/3。 <img height="60%" width="60%" src="https://i.imgur.com/PrEtuWI.png" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">木星衛星 log a - log T 關係圖</div> <br /> <img height="60%" width="60%" src="https://i.imgur.com/57E0QsX.png" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">土星衛星 log a - log T 關係圖</div> <br /> <img height="60%" width="60%" src="https://i.imgur.com/H25173p.png" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">天王星衛星 log a - log T 關係圖</div> <br /> ## 行星衛星資料 1. [Jovian Satellite Fact Sheet](https://nssdc.gsfc.nasa.gov/planetary/factsheet/joviansatfact.html) 2. [Saturnian Satellite Fact Sheet](https://nssdc.gsfc.nasa.gov/planetary/factsheet/saturniansatfact.html) 3. [Uranian Satellite Fact Sheet](https://nssdc.gsfc.nasa.gov/planetary/factsheet/uraniansatfact.html) <br /> ## 參考資料 1. https://stackoverflow.com/questions/8248397/how-to-know-change-current-directory-in-python-shell 2. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_html.html 3. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rename.html 4. https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dropna.html 5. https://stackoverflow.com/questions/56947333/how-to-remove-commas-from-all-the-column-in-pandas-at-once 6. https://stackoverflow.com/questions/50372272/how-to-add-columns-to-an-empty-pandas-dataframe 7. https://pandas.pydata.org/docs/reference/api/pandas.Series.str.endswith.html 8. https://stackoverflow.com/questions/13682044/remove-unwanted-parts-from-strings-in-a-column <br /> --- **2021/7/21 補充** 由於[海王星衛星資料](https://nssdc.gsfc.nasa.gov/planetary/factsheet/neptuniansatfact.html)的表格比較不一樣,需要稍微修改一下程式碼,忽略表格最上面的列,再手動為每個欄命名,主要修改的部分是第4、5、7、8行程式碼。 ```python= import pandas as pd planet = 'Neptune' table = pd.read_html('https://nssdc.gsfc.nasa.gov/planetary/factsheet/neptuniansatfact.html', attrs={'class':'orbital'}, skiprows=1) data = table[0].copy() data.columns = ['Name', 'Semi-major axis(103km)', 'Semi-major axis(Jovian Radii)', 'OrbitalPeriod*(days)', 'RotationPeriod(days)', 'Inclination(degrees)', 'Eccentricity'] data.dropna(subset=['Name'], inplace=True) data['Name'] = data['Name'].replace(',', '', regex=True) data['Retrograde'] = False data.loc[data['OrbitalPeriod*(days)'].str.endswith('R'), 'Retrograde'] = True data['OrbitalPeriod*(days)'] = data['OrbitalPeriod*(days)'].map(lambda x: x.rstrip('R')) data.to_csv(planet+'SatelliteData.csv') ``` <br /> <img height="60%" width="60%" src="https://i.imgur.com/bXF8Of1.png" style="display: block; margin-left: auto; margin-right: auto;"/> <div style="text-align:center">海王星衛星 log a - log T 關係圖</div> <br /> --- ###### tags:`Python`