受保護的文章:Python 336期 作業繳交
受保護的文章:Python 335期 作業繳交
受保護的文章:Python 330期 作業繳交
Python 326期 4/2 清明連假,停課一次
依據行政院人事室規定,4/2-4/5為清明連續假期,
故此段期間為國定假日,不需到班上課。
Python 320期 9/30 停課一次
9/30 台北市停班停課,所以當天課程將暫停一次,課程會順延至 10/24 為最後一堂上課
Python IDEs 常見的整合開發環境介紹
Python IDEs
俗語說 ”工欲善其事,必先利其器 ” 。
所以在撰寫 python 程式時,挑選好的開發工具 Python IDEs 是蠻重要的一件事情。其實 Python官方的安裝包裡面含有 IDLE,已經是個麻雀雖小五臟俱全的IDE了,對於初學的人其實已經蠻夠用了。
但如果能了解有哪些不錯的IDEs,對於我們未來撰寫功能更複雜的大型專案,或是有資料科學領域上的需求就能使用更方便、更合適的工具,對於開發效率上也能有所提升。
以下介紹一些 Python 常見的 IDEs (Integrated Development Environment):
Spyder
- 編輯器:支援多語言,具有函式和類檢視器,代碼分析特性(pyflakes 和 pylint 獲得了支援),代碼補全,水平與垂直視窗的分離,直接跳入定義等等。
- 互動埠: Python 或 IPython 埠都在工作區可以調整和使用。支援對編輯器里的代碼直接偵錯。此外整合了 Matplotlib 的圖表顯示。
- 文件瀏覽器:在編輯器或埠中顯示任意類或函式呼叫的文件。
- 可變的瀏覽窗口:在檔案的執行過程中可以建立可變的瀏覽窗口。同時也可以對其進行編輯。
- 在檔案中尋找:支援正規表示式與 Mercurial 倉庫
- 檔案瀏覽器
- 歷史記錄
Jupyter Notebook
- 支援超過 40 種程式語言,比如 Python、R,、Julia 與 Scala
- 可以透過 email、Dropbox、GitHub 或是 Jupyter Notebook Viewer 與他人分享 Notebook
- 可以顯示多媒體資源,比如圖片、影片、LaTeX 甚至是JavaScript,也可以做 realtime 的資料視覺化
PyCharm
https://www.jetbrains.com/pycharm/
是一套功能非常完善也強大的跨平台開發的環境。
- 代碼分析與輔助功能,擁有補全代碼、高亮語法和錯誤提示;
- 項目和代碼導航:專門的項目視圖,文件結構視圖和和文件、類、方法和用例的快速跳轉;
- 重構:包括重新命名,提取方法,引入變量,引入常量、pull,push等;
- 支持網絡框架: Django, web2py 和 Flask;
- 集成Python 調試器;
- 集成單元測試,按行覆蓋代碼;
- Google App Engine下的Python開發;
- 集成版本控制系統:為Mercurial, Git, Subversion, Perforce 和 CVS提供統一的用戶界面,擁有修改以及合併功能。
PyDev
Visual Studio
結語
Pandas+SQL-空氣品質資料擷取
Pandas+SQL 操作 – 前言
Pandas+SQL 的操作範例,本篇文章以政府開放資料平台中的 空氣品質指標(AQI) 為資料來源,展示了如何使用 pandas 搭配 SQL 語法 來做資料搜尋,對於已經有 SQL 語法操作基礎的人,可以用之前學過的 SQL 語法快速的搜尋並篩選出所需要的資料,提供大家參考。
Pandas 簡介
Pandas 是 python 的一個數據分析模組庫,於 2009 年底開源出來,提供高效能、簡易使用的資料格式 (Data Frame) 讓使用者可以快速操作及分析資料,主要特色描述如下:
- 在異質數據的讀取、轉換和處理上,讓分析人員更容易處理,例如:從列欄試算表中找到想要的值。
- Pandas 提供兩種主要的資料結構,Series 與 DataFrame。
- Series: 用來處理時間序列相關的資料(如感測器資料等),主要為建立索引的一維陣列。
- DataFrame: 用來處理結構化(Table like)的資料,有列索引與欄標籤的二維資料集,例如:關聯式資料庫、 CSV 等等。
- 透過載入至 Pandas 的資料結構物件後,透過結構化物件所提供的方法,快速地資料前處理。例如:資料補值,空值去除或取代等。
- 更多的輸入來源及輸出整合性,例如:可以從資料庫讀取資料進入 Dataframe ,也可將處理完的資料存回資料庫。
本篇文章就是使用 DataFrame 搭配關連式資料庫,使用 SQL 語法進行資料查詢篩選,而未來有時間再整理更多其他 Pandas 相關的應用範例。
安裝 Pandas
Python 安裝方式一樣都是透過 pip install 即可完成安裝,語法如下:
pip install pandas
其他介紹可以參考 官方網站
JSON
JSON(JavaScript Object Notation)是一種由道格拉斯·克羅克福特構想設計、輕量級的資料交換語言,以文字為基礎,且易於讓人閱讀。儘管 JSON 是 Javascript 的一個子集,但 JSON 是獨立於語言的文字格式,並且採用了類似於 C語言 家族的一些習慣。此種格式與XML格式常被用於API的資料回傳格式。本篇文章的程式碼範例就是以 JSON 格式的內容為資料的輸入來源。
範例程式碼
此範例程式主要流程大致為:
- 透過 requests 呼叫政府公開資料的 API 得到空氣品質指標的資料( JSON 格式)
- 透過 json.loads 函數將資料讀入
- 使用 pandas DataFrame 產生出資料框
- 透過 sqlalchemy 模組中的 create_engine 函數來建立 sqlite 的連線 ,並設定將資料表儲存在 memory 中(提供一次性的操作使用,若需要永久儲存則可以寫入檔案中)
- 使用 SQL 語法作取出特定的欄位(縣市、區域、平均 PM2.5 值)並以 PM2.5 的值由大至小排序。
import json import requests import pandas as pd ''' 資料庫的讀寫函數可以使用 SQLAlchemy,支援 PostgreSQL, MySQL, Oracle, Microsoft SQL server 等資料庫... ''' from sqlalchemy import create_engine req = requests.get('http://opendata2.epa.gov.tw/AQI.json') data = json.loads(req.content.decode('utf8')) df = pd.DataFrame(data) #透過sqlalchemy模組中的create_engine函數來建立sqlite的連線 #並設定將資料表儲存在memory中(提供一次性的操作使用,若需要永久儲存則可以寫入檔案中) engine = create_engine('sqlite:///:memory:') df.to_sql('db_table', engine, index=False) print(pd.read_sql_query('SELECT `County` as `縣市`, `SiteName` as `區域`, \ CAST(`PM2.5_AVG` AS int) as `PM2.5` FROM `db_table` \ order by CAST(`PM2.5_AVG` AS int) ASC', engine))
點我觀看執行範例 (使用 Microsoft Azure Notebooks )
若有其他想法,例如:想知道哪個縣市的整體平均空氣品質較差就可以在 SQL 語法中使用 GROUP BY 語法,對於 SQL 語法熟悉的話就能輕易地藉由改變查詢的語法來獲得想查詢 / 篩選的資料。
PS. 對於Python語法上有疑問可以參考課程投影片
PEP8 Python 編碼規範手冊
PEP 8 介紹
PEP8 是 Python 社群共通的風格指南,一開始是 Python 之父 Guido van Rossum 自己的撰碼風格,慢慢後來演變至今,目的在於幫助開發者寫出可讀性高且風格一致的程式。許多開源計畫,例如 Django 、 OpenStack等都是以 PEP8 為基礎再加上自己的風格建議。
程式碼編排 (Code lay-out)
這邊列出PEP8裡面的重點,有興趣可以直接看官方原文的文件會更清楚一些。
縮排 (Indentation)
每層縮排使用 4 個空格
斷行風格
正確:
# 斷行首字母與開頭的括號垂直對齊.
foo = long_function_name(var_one, var_two, var_three, var_four)
# 垂直縮排,首行不能有參數.
def long_function_name( var_one, var_two, var_three, var_four): print(var_one)
# 垂直縮排,後面還有其它代碼時,需要添加一層額外的縮排加以區別:
foo = long_function_name( var_one, var_two, var_three, var_four)
錯誤:
# 垂直縮排方式首行不能有引數
foo = long_function_name(var_one, var_two, var_three, var_four)
# 垂直縮排,首行不能有參數; 後面還有其它代碼部分時,斷行要添加一層縮進,使其與其它代碼部分能區別開來
def long_function_name( var_one, var_two, var_three, var_four): print(var_one)
if 條件斷行
if ( 剛好有 4 個字符,相當於一層縮排。
對於 if 條件斷行,以下幾種風格都可以:
沒有額外的縮排
if (this_is_one_thing and that_is_another_thing): do_something()
添加註釋加以區分
# Add a comment, which will provide some distinction in editors # supporting syntax highlighting. if (this_is_one_thing and that_is_another_thing): # Since both conditions are true, we can frobnicate. do_something()
添加額外的縮排加以區分
# Add some extra indentation on the conditional continuation line. if (this_is_one_thing and that_is_another_thing): do_something()
多行的括號
括號結束符與最後行的首字符對齊,如:
my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )
括號結束符與首行的首字符對齊, 如:
my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )
上面兩種排版的方式都可以
使用Tab還是空格縮排
用空格
每行最長長度
所有行都不超過 80 個字符
限制編輯器視窗的寬度,使能並排同時打開多個文件。
設置編輯器寬度(set width to 80),來避免 wrapping
對於較少結構限制的長文本(如 docstrings 或註釋),行長應限制為 72 個字符。
如果團隊成員都同意使用長行,則可以將行長增加到不超過 100 個字符,但是 docstrings 和註釋還必須為 72 個字符。
有括號的長行可以用 implicit continuation 來斷行,其它的可以用 \ 來斷行,如:
with open('/path/to/some/file/you/want/to/read') as file_1, \ open('/path/to/some/file/being/written', 'w') as file_2: file_2.write(file_1.read())
操作符要和操作數在一起
# 推薦的正確的風格,這樣很容易將操作符與操作數匹配:
income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
# 這種風格現今已不推薦使用了:
income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
空行分隔
模組中最頂層的函數和類定義都要用兩行空行分隔
類別中的方法定義用單行分隔
要把一組相關的函數分組,可以用一些額外的空行
函數中的邏輯區塊可以加空行來分隔
原始碼的編碼
Python 核心模組文件的編碼都必須用 UTF-8(Python2 是 ASCII)。
使用默認的編碼時(Python3: UTF-8,Python2: ASCII),不能使用編碼聲明
標準庫中,只有測試、作者名才能使用非默認的編碼,其它情況下的非 ASCII 字符用 \x, \u, \u, \N 表示法表示。
Import
每行 import 只導入一個模組:
# 正確:
import os
import sys
# 錯誤:
import sys, os
# 正確:同一模組中的內容可以在同一行導入
from subprocess import Popen, PIPE
import 語句要在文件的前面,在模組註釋及 docstrings 之後,在模組全域變數和常數定義之前。
import 分組及導入順序,每個分組之間用一行空行分隔 1. 標準庫 2. 相關第三方庫 3. 本地應用/庫的特殊導入
推薦使用絕對導入,如:
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
在比較覆雜的套件組成中,也可以用顯式的相對導入,如:
from . import sibling
from .sibling import example
從一個模塊中導入一個類別時,要顯示拼寫出類別名,如:
from myclass import MyClass
from foo.bar.yourclass import YourClass
如果與本地名稱衝突,可以先導入模組:
import myclass
import foo.bar.yourclass
然後使用:"myclass.MyClass" 和 “foo.bar.yourclass.YourClass"。
應該避免使用 from <module> import *
模組層級的特殊名稱(如__all__)的位置:
必須在模組的 docstrings 或註釋之後,但在任何的 import 語句之前。from __future__ 比較特殊,Python 強制該語句必須在 docstrings 或註釋之後,因此風格如下:
"""This is the example module. This module does stuff. """ from __future__ import barry_as_FLUFL __all__ = ['a', 'b', 'c'] __version__ = '0.1' __author__ = 'Cardinal Biggles' import os import sys
字符引號 (String Quotes)
單引號和雙引號的功能是等同的。
對於多行字符串,應該用雙引號字符形式的三引號""",以便與 PEP257 中的 docstrings 規範兼容
表達式和語句中的空格 (Whitespace in Expressions and Statements)
()、[]、{} 等括號內不要多餘的空格,如:
# 正確:
spam(ham[1], {eggs: 2})
# 錯誤:
spam( ham[ 1 ], { eggs: 2 } )
,、;、: 之前不要有空格,如:
# 正確:
if x == 4: print x, y; x, y = y, x
# 錯誤:
if x == 4 : print x , y ; x , y = y , x
在 slice 語句中的 :實際上是一個二元操作符,因此其兩側的空格數必須相同; 但當無 slice 參數時,兩側的空格可以都省略,如:
# 以下是正確的風格:
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
# 以下是錯誤的風格:
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
函數調用的 () 及索引的 [] 前不要加空格,如:
# 正確風格:
spam(1)
dct[‘key’] = lst[index]
# 錯誤風格:
spam (1)
dct [‘key’] = lst [index]
不要在賦值語句中加入額外的空格來對齊,如:
# 正確的風格:
x = 1
y = 2
long_variable = 3
# 錯誤的風格:
x = 1
y = 2
long_variable = 3
其它推薦風格
任何行的行尾都不要有空白符。
在二元操作符號兩側一般都要加一個空格,一般的二元操作符號如:
賦值: =, +=, -=
比較:==, <, >, !=, <>, <=, >=, in, not in, is, is not
布林操作: and, or, not
在分優先級的表達式中,在最低優先級的操作符兩側加一個空格,但至多只能加一個空格,如:
# 正確的風格:
x = x*2 – 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
# 錯誤的風格:
x = x * 2 – 1
hypot2 = x * x + y * y
c = (a + b) * (a – b)
在關鍵字參數和默認參數值中的 = 兩側不要加空格,如:
# 正確的風格:
def complex(real, imag=0.0):
return magic(r=real, i=imag)
錯誤的風格:
def complex(real, imag = 0.0):
return magic(r = real, i = imag)
函數註解中的 : 前不要加空格,這符合 : 的常規風格,但是 -> 兩側要加空格,如:
# 正確的風格:
def munge(input: AnyStr): …
def munge() -> AnyStr: …
# 錯誤的風格:
def munge(input:AnyStr): …
def munge()->PosInt: …
參數註解中,如果註解的參數有默認值,指定默認值的 = 兩側要加空格,如:
# 正確的風格:
def munge(sep: AnyStr = None): …
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): …
# 錯誤的風格:
def munge(input: AnyStr=None): …
def munge(input: AnyStr, limit = 1000): …
不要將多條語句組合在一行中,如:
# 正確的風格:
if foo == ‘blah’:
do_blah_thing()
do_one()
do_two()
do_three()
# 錯誤風格:
if foo == ‘blah’: do_blah_thing()
do_one(); do_two(); do_three()
# 如果 if/for/while 區塊內的程式碼很少,組合在一行有時還是可以接受的
# 但是不推薦,如:
if foo == ‘blah’: do_blah_thing()
for x in lst: total += x
while t < 10: t = delay()
# 但是在有多段語句時,絕對不能這樣,如:
if foo == ‘blah’: do_blah_thing()
else: do_non_blah_thing()
try: something()
finally: cleanup()
do_one(); do_two(); do_three(long, argument,
list, like, this)
if foo == ‘blah’: one(); two(); three()
註解 (Comments)
註解內容必須要和程式碼相關!
註解應該是完整的語句,首字母一般大寫(英文),一般要有句號。
註解很短時句號可以省略。
區塊註解一般由多個段落組成。用英文寫註解
區塊註解 (Block Comments)
每行用 # 及一個空格開始,如 #
段落用一個只有 # 的行分隔
行內註解 (Inline Comments)
註解與語句內容至少用兩個空格分開,註解用 # 加一個空格開始
# 不要像下面這樣,註解內容沒有必要
x = x + 1 # Increment x
# 但是有時,如下面的註解會很有用
x = x + 1 # Compensate for border
文件字串 (Documentation Strings)
公開的模組、函數、類及方法都應該有文件字串,而非公開的方法可以用註解來代替,且註解放置在 def 行之後。
多行的文件字串,結束符號要自成一行,如:
"""Return a foobang Optional plotz says to frobnicate the bizbaz first. """
單行的文件字串,結束符號和內容放在同一行
命名 (Naming Conventions)
沒有推薦的風格,但是別人要能從你的程式碼中看出你用的是什麽風格,常用的風格如下:
- b 單個小寫字母
- B 單個大寫字母
- lowercase
- lower_case_with_underscores
- UPPERCASE
- UPPER_CASE_WITH_UNDERSCORES
- CapitalizedWords, 這種風格中,對於縮寫詞應全部用大寫,如
- HTTPServerError 比 HttpServerError 好
- mixedCase
Capitalized_Words_With_Underscores,這個太醜,不要用這種! - st_mode、st_mtime 等前綴,一般是系統接口返回,如果自己寫的程式碼不推薦用這種
- _single_leading_underscore : 弱 “內部使用” 指示器,這種對象用 from M import * 不會被導入
- single_trailing_underscore_ : 可以用來和 Python 關鍵詞進行區分,如 Tkinter.Toplevel(master, class_=’ClassName’)
- __double_leading_underscore : 命名一個類屬性時,可以進行命名矯正,例如 class FooBar 內的 __boo 會變成 _FooBar__boo
- double_leading_and_trailing_underscore : “magic” 對象,不要自己發明這種對象
命名習慣 (Prescriptive: Naming Conventions)
- 不用單個 l, O, I 等這樣的單個字符來命名,它們與數字1,0不好區分
- 套件名和模組名:全部用小寫,必要時可用 _,但不推薦,C/C++ 的擴展模組,如果其對應有 Python 版本,那麽 C/C++ 擴展模組名前加 _
- 類別名:用 CapWords 風格
- 異常名:用 CapWords 風格,一般應該有 Error 後綴
- 全域變數名:能用 from M import * 導入的變數全部放在 __all__ 中,或者全域變數用 _ 做前綴
- 函數名:應該用全部用小寫,單詞間可以用 _ 分隔,如 my_func,不推薦用 mixedCase 風格
- 函數和方法的參數:實例方法的第一個參數用 self, 類別方法的第一個參數用 cls,如果參數與關鍵字衝突,在參數名後加 _ 後綴,如 class_
- 實例變數和方法: 用小寫字符和 _, 非公開的實例變數和方法用一個 _ 做前綴; 避免與子類別中的名字衝突,類的變數可用兩個 _ 作前綴,例如 class FooBar 內的 __boo 會變成只能通過 FooBar._FooBar__boo 讀取
- 常數:全部大寫,可用 _ 分隔,如 MAX_OVERFLOW、TOTAL
推薦的程式撰寫方式 (Programming Recommendations)
字符串連接不要用 a += b 或者 a = a + b, 用 “.join(), 後者性能更好。跟 None 的比較用 is 和 is not,不要用 ==,如果你想判斷 if x is not None, 不要縮寫成 if x 使用 if foo is not None,而不是 if not foo is None,前者更加易讀
如果要實現序列比較操作的話,應將 6 個操作(__eq__ , __ne__ , __lt__ , __le__ , __gt__ , __ge__)全部實現,可以借助 functools.total_ordering() 修飾器來減少工作量
將函數保存在一個變數中應該用 def f(x): return 2*x, 而非 f = lambda x: 2*x,後者不利於調試
自定義的異常類應該繼承至 Exception 類,而非 BaseException 類。Python 2 中拋出異常用 raise ValueError(‘message’),而非 raise ValueRoor, ‘message’
盡量可以的指明異常名,如:
try: import platform_specific_module except ImportError: platform_specific_module = None
避免使用無異常名的 except: 語句,它會捕獲全部的異常(如 Ctrl C)。
將異常綁定到名字的方法:
try: process_data() except Exception as exc: raise DataProcessingFailedError(str(exc))
try: 中的語句要盡量減少,如:
# 正確的寫法:
try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value)
# 錯誤的寫法
try: # Too broad! return handle_value(collection[key]) except KeyError: # Will also catch KeyError raised by handle_value() return key_not_found(key)
如果資源只適用於某個代碼段內,使用 with 或 try/finally 來確保能進行清理工作
上下文管理器應用通過一個單獨的函數或方法來呼叫,如:
# 正確的做法:
with conn.begin_transaction(): do_stuff_in_transaction(conn)
# 錯誤的做法:
with conn: do_stuff_in_transaction(conn)
return 語句應該一致,如:
# 正確的做法:
def foo(x): if x &amp;amp;amp;amp;amp;amp;amp;amp;gt;= 0: return math.sqrt(x) else: return None def bar(x): if x &amp;amp;amp;amp;amp;amp;amp;amp;lt; 0: return None return math.sqrt(x)
# 錯誤的做法:
def foo(x): if x &amp;amp;amp;amp;amp;amp;amp;amp;gt;= 0: return math.sqrt(x) def bar(x): if x &amp;amp;amp;amp;amp;amp;amp;amp;lt; 0: return return math.sqrt(x)
使用字符串的方法,而不是用 string 模組中的方法
使用 “.startswith() 和 “.endswidth() 而不用 slicing 來檢查前綴和後綴:
# 正確:
if foo.startswith(‘bar’):
# 錯誤:
if foo[:3] == ‘bar’:
判斷對象的類型用 isinstance 而不直接 type 比較,如:
# 正確:
if isinstance(obj, int):
# 錯誤:
if type(obj) is type(1):
對序列是否空的判斷不用 len,如:
# 正確:
if not seq:
if seq:
# 錯誤:
if len(seq):
if not len(seq):
布林值的比較:
# 正確:
if greeting:
# 不要這樣:
if greeting == True:
# 這樣更不行:
if greeting is True:
參考資料:
Google關鍵字搜尋,將搜尋結果排名紀錄下來
使用Python撰寫自動化搜尋特定關鍵字
Google關鍵字搜尋將搜尋結果排名紀錄下來
Selenium 使用 Chrome 瀏覽器 webdriver
SEO (Search Engine Optimization),是維護網站非常重要的一項工作,利用這個小程式可以自動化的搜尋關鍵字,並將搜尋排名結果存入檔案,以利分析成效與後續的持續優化。
需要先安裝 selenium 套件
下載 Chrome 瀏覽器 webdriver
額外會需要用到兩隻檔案,需要先建立好放置於py檔同層資料夾下
site.csv : 裡面放要搜尋的網站位置
terms.csv : 搜尋關鍵字
from selenium import webdriver import csv import time chromedriver = "/Users/chifu/Documents/chromedriver" chrome_options = webdriver.ChromeOptions() chrome_options.add_argument("--incognito") driver = webdriver.Chrome(chromedriver, chrome_options=chrome_options) csvr_site = csv.reader(open('site.csv', 'r')) csvr_terms = csv.reader(open('terms.csv', 'r')) outfile = open('rank.csv', 'w') csvwriter = csv.writer(outfile) search_pages = 5 for s in csvr_site: site = s[0] for row in csvr_terms: print(row[0]) current_page = 1 while current_page &amp;lt;= search_pages: if current_page == 1: driver.get("https://www.google.com.tw/search?q=%s" % row[0]) else: driver.get(next_pageurl) time.sleep(1) doms = driver.find_elements_by_css_selector("div.rc h3.r a") next_pageurl = driver.find_element_by_css_selector("a#pnnext.pn").get_attribute("href") counter = 0 rank = "not found" for dom in doms: counter += 1 href = dom.get_attribute("href") if site in href: rank = counter break if rank != "not found": break current_page += 1 csvwriter.writerow([row[0], current_page, rank]) outfile.flush() time.sleep(1) driver.close()
相關API參考