2021年5月4日 星期二
2021年4月26日 星期一
Google API - Python 學習筆記 - Upload post
Google Blogger API Table
其實找到好用的 Google API document 就完成 80% 了,這裡紀錄 Python 連結。再紀錄個 Google 給開發者用的 Playground,可以讓你先試試 API。
Post article
這裡紀錄程式碼模擬平常的流程,但其實可以只用 insert 就完成所有事。
-
New Post
新增草稿。
# Create a new draft post. # The return value (newpost) is dict contain all info of this draft newpost = service.posts().insert(blogId=BLOGID, isDraft=True).execute() -
Update content
Update 好 content 後,要轉成 JSON object。以下若程式碼直接傳 JsonPost(str),會回傳錯誤 Invalid JSON payload received. Unknown name “”: Root element must be a message,即使他們 print 出來一模一樣。
# Update some content of the new draft post. newpost['title'] = "posted via python" newpost['content'] = "<div>hello world test</div>" JsonPost = json.dumps(newpost, indent=4, ensure_ascii=False) service.posts().update(blogId=BLOGID, postId=newpost['id'], body=json.loads(JsonPost)).execute() -
Publich
其實就只是把 Draft 的狀態改成 Publish。
# Publish the new post. service.posts().publish(blogId=BLOGID, postId=newpost['id']).execute() -
Insert 解決一切
可以直接用 insert 就好,上面的程式碼只是模擬。
service.posts().insert(blogId=BLOGID, body=body).execute()
參考資料 :
1.Google API Library
2021年4月13日 星期二
Google API - Python 學習筆記
Google API - python ver.
紀錄一些使用 python 呼叫 Google API 的心得
基本前置作業
-
Google Account
首先你要有 google 帳號。
-
Google Cloud Platform project
想要使用 Google Cloud Platform (GCP) 的 service,你需要創建一個 GCP project。左上角 project name 右邊的倒三角按下去新增。
-
Enable API
旁邊的 API&Services => Library => 新增你想要的服務。不過我當初沒有用這步,應該是我用 Oauth 而非 API Keys 的關係 ( 完整介紹 )。
-
Credentials
產生金鑰,我是選 Desktop App,然後下載其 json 檔就樣就好。雖然 Google Guide 其實有給很多講解及步驟,但我沒做也能運行。
安裝 google python api library
-
安裝 google api python
首先安裝 google-api-python-client,點進去有安裝方法,這裡用 Linux 做些紀錄。基本上是安裝一個 virtualenv,讓他新增一個獨立且乾淨的 python 執行環境,然後利用這虛擬環境去下載他門的 library。
# 安裝虛擬 python 環境和 google-api-python-client pip install virtualenv virtualenv <your-env> source <your-env>/bin/activate <your-env>/bin/pip install google-api-python-client -
安裝 google_auth_oauthlib
再來安裝 Oauth 2.0。
<your-env>/bin/pip install google_auth_oauthlib
安裝 google python api library
我寫這篇的主因,因為 google python api library 已經不再更新,有些 sample code 已經過時不能使用所以在這裡紀錄一些可用的 sample code。
-
Sample Code - flow
這是 Google Oauth API Scope 我拿 blogger 做測試。完整的程式碼在 sample.py,輸出結果在 output.json。這裡擷取片段,順序還要參考完整的程式碼。
# 首先是連線設置,Flow 這個 class 是用來讀取你的 Credentials 來認證你的程式。 # client_secrets.json = 你下載的 Oauth json file。 # SCOPES = 你宣告要使用哪些功能 ( ex. gmail, blogger... ) from google_auth_oauthlib.flow import InstalledAppFlow SCOPES = ['https://www.googleapis.com/auth/blogger'] flow = InstalledAppFlow.from_client_secrets_file( 'client_secrets.json', SCOPES) # 用 port 0 連線 GCP,這裡會用預設瀏覽器去做認證,結束後會跟你說可以關閉網頁 creds = flow.run_local_server(port=0) -
Sample Code - token
這裡回傳值 creds,是 GCP 給的暫時性 token,若你的 token 還在且未過期,你就可以用此 token 直接連線而不用再透過瀏覽器認證。
# 如果你有儲存 token,可以直接用 token 重新連線 # token expired 的話且沒過太就的話可以用 creds.refresh(Request()) 做 refresh token creds = Credentials.from_authorized_user_file('token.json', SCOPES) if not creds.valid: if creds.expired and creds.refresh_token: creds.refresh(Request()) -
Sample Code - build
這裡依照 Blogger API Guide 去做取資料。
# 先依照 scope 取得服務,再依照 api 的格式去取得資料 # posts() 跟 list() 都是依照 Blogger API Guide 去填寫的,最後執行 execute() # 回傳會是 dict,要自己轉 json from googleapiclient.discovery import build service = build('blogger', 'v3', credentials=creds) posts = service.posts().list(blogId=BLOGID,maxResults=3).execute()
參考資料 :
1.我跟Google官網
2021年4月11日 星期日
Python Error - UnicodeDecodeError: 'cp950' codec can't decode
讀檔時 UnicodeDecodeError: 'cp950' codec can't decode
老實說這非常常見,即使你的檔案是用 UTF-8 編寫,而且用 Python3 ( 絕大部分 default 是 utf-8 ),仍會報這個錯。所以要避免程式能在不同平台都能正常使用,讀檔時最好都加上 encoding="utf-8"
with open("somefile.py", encoding="utf-8") as f:
f.read()
參考資料 :
1.stackoverflow
2021年4月7日 星期三
Python - BeautifulSoup 基本應用 (2)
Navigating the tree - Going down
-
child Tag
前一篇呼叫 Tag 底下的 subTag 是用像 class 的方式 (Tag.subTag),沒完整說明的是這個 subTag 可呼叫到的範圍並不只侷限 children Tag,而是整個 Tag 底下第一個遇到的 Tag.name == subTag。
""" html <b class="boldest" id="bold man"> <u>Extremely bold</u> </b> <p>Just a paragraph</p> """ soup = BeautifulSoup(html, 'html.parser') print(soup.u) # <u>Extremely bold</u> -
find_all()
依循前面的 subTag 找法,find_all 則會找出所有的 Tag (Tag.name == subTag)。
soup.find_all('a') # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>] -
.contents
Tag.contents 回傳包含所有 child string 的 list。
print(soup.contents) # [<d class="car"><u>Extremely bold.</u></d>, <p>123</p>] -
.children
Tag.children 回傳包含所有 child Tag object 的 list。
for child in soup.children: print(child.name) # b # p -
.descendants
回傳一個不只包含 Tag 底下一層的 subTag,而是整個 tree 上所有 object。底下的結果之所以會有 None,是因為 NavigableString 也是 child 的包含範圍 ( .contents 跟 .children 也一樣 ),而 NavigableString object 沒有 .name。
for child in soup.descendants: print(child.name) # b # u # None # p # None -
.string
回傳底下唯一一個的 NavigableString 。如果 Tag 只有一個 child 而且還是 NavigableString 則回傳。如果 Tag 只有一個 child 且 child 也只有一個 NavigableString child 也是回傳。如果有兩個以上的 NavigableString 則要呼叫 .strings,.string 會回傳 None。
print(soup.u.string) # Extremely bold print(soup.b.string) # Extremely bold print(soup.string) # None -
.strings & .stripped_strings
strings 會回傳所有 NavigableString。但這種情況下仍會紀錄到一些換行或字串前後的空格,所以可以用 stripped_strings 回傳乾淨的 NavigableString list。(如果整個字串都是空格或換行,則這個 NavigableString 會被無視)
for sstr in soup.strings: print(repr(sstr)) # 'Extremely bold' # 'Just a paragraph'
Navigating the tree - Going Up
-
.parent & .parents
基本上就是往上一層的 Tag (Soup 的 parent 為 None)。
link = soup.a link # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a> for parent in link.parents: print(parent.name) # p # body # html # [document]
Navigating the tree - Going sideways
-
.next_sibling &.previous_sibling
往前找跟往後找,但是要注意的是他沒那方便幫你找到下一個或上一個的 Tag object,絕大部分都是 NavigableString,例如換行 (\n) 之類的。
tag1 = soup.b tag2 = soup.p print(tag1.next_sibling) # <p>123</p> print(tag2.previous_sibling) # <b class="boldest" id="bold man"><u>Extremely bold</u></b>
參考資料 :
1.Beautiful Soup 4.9.0 documentation
2021年3月31日 星期三
Python - BeautifulSoup 基本應用 (1)
BeautifulSoup
紀錄下 Python 網頁爬蟲大部分會用到的 BeautifulSoup。BeautifulSoup 本質上就是 parser,知道這點後其 function 和 parameter 的使用就能得心應手。
-
Install
這裡紀錄使用 pip 安裝
pip install beautifulsoup4 pip install lxml # 非必要 -
Import
第一行的 import 就可以包含 99%,第二行則是此篇會用的 import,多了 2 個說明用的 object。
from bs4 import BeautifulSoup from bs4 import BeautifulSoup, NavigableString, Comment -
Load html
此篇會用第一行來 make soup
soup = BeautifulSoup("<html>a web page</html>", features="html.parser") soup = BeautifulSoup("<html>a web page</html>", features="lxml") # 如果你有安裝的話
Tag object
Tag object 對應到的就是 XML 跟 HTML 裡的 Tag。 這裡可以從 soup object 下 2 個 Tag 開始,一個是 b 一個是 p。
""" html
<b class="boldest" id="bold man">
<u>Extremely bold</u>
</b>
<p>Just a paragraph</p>
"""
soup = BeautifulSoup(html, 'html.parser')
print(type(soup)) # <class 'bs4.BeautifulSoup'>
print(soup.b) # <b class="boldest" id="bold man"><u>Extremely bold</u></b>
print(soup.p) # <p>Just a paragraph</p>
# Tag 有兩個基礎參數 name 跟 attrs
tag = soup.b
print(type(tag)) # <class 'bs4.element.Tag'>
print(tag.name) # b
print(tag.attrs) # {'class': ['boldest'], 'id': 'bold man'}
# name 跟 attrs 都可以直接修改
tag.name = "d"
tag.attrs = {'class': ['boldest', 'cutest'], 'id': 'pui pui'}
print(str(tag)) # <d class="boldest cutest" id="pui pui"><u>Extremely bold</u></d>
Tag 的 child Tag 可以像 class 的 element 一樣直接呼叫 (上面的 soup.b 和 soup.p 也是一種呼叫 child Tag)
print(str(tag.u)) # <u>Extremely bold</u>
print(tag['class']) # ['boldest', 'cutest']
print(tag['id']) # pui pui
Tag 的 attr 也可以修改和刪除
tag['class'] = ['car']
del tag['id']
print(str(tag)) # <d class="boldest cutest"><u>Extremely bold</u></d>
P.S. HTML 裡的 class 是可以允許多參數的,所以上述的例子裡 class 是 string list,而非 string
NavigableString object
NavigableString 就等於 Tag 裡的 string,可以使用 replace_with 做修改。
tag.u.string.replace_with("No longer bold.")
print(type(tag.u.string)) # <class 'bs4.element.NavigableString'>
print(str(tag.u.string)) # No longer bold.
BeautifulSoup object
其實一開始就有示範了,可以把他想像成沒有 name 和 attributes 的 Tag。但有時候你在 load 真正的 html 檔,他的 name 可能會是 "[document]"。
print(str(soup))
# <d class="car"><u>No longer bold.</u></d><p>123</p>
print(soup.prettify())
# <d class="car">
# <u>
# No longer bold.
# </u>
# </d>
# <p>
# 123
# </p>
參考資料 :
1.Beautiful Soup 4.9.0 documentation
2021年3月26日 星期五
Python - sys, os.path 常見的 module 應用
Common python module - sys
-
sys parameter
一些常見系統參數
import sys print(sys.platform) # linux -
sys.argv
像 c/c++ 的 argv 一樣,可以吃從 command line 傳入的參數。
# test.py 123 456 789 print (argv) # ['test.py', '123', '456', '789'] print (argv[1]) # 123 -
sys.path
import 時的 search list,可以也透過改變(增加, 刪除, 更改順序) list 來在想要的位置 import。除了 built-in 的 lodule,其他額外安裝的 module 都可以用這個順序做調整。以下例子就等於會先去找 tool/ 底下的 module,找不到才會找 local/lib/python3.X/site-packages 或 local/lib/python3.X/dist-packages
print (sys.path) # [......] sys.path.insert(0, "tool/") # ['tool/',......] import markdown # 已安裝 markdown,但 import 的卻是 tool 底下的 print(markdown.__file__) # tool/markdown.py
Common python module - os
-
os.function
一些常用的 os.function
import os os.system(command) # 執行 command , command 為系統指令的字串。 os.getcwd() # 取得目前所在路徑。 os.rename(src, dst) # 將 src 改名為 dst 。 os.mkdir(path) # 建立 path 路徑,如果已存在就會發起例外。 os.remove(path) # 移除 path ,如果 path 是目錄就會發起例外。 os.rmdir(path) # 刪除 path 目錄。 -
os.path (1)
檢查檔案目錄。
import os os.path.isfile(path) # 判斷 path 檔案是否存在。 os.path.isdir(path) # 判斷 path 路徑是否存在。 -
os.path (2)
os.path 路徑處理。
import os path = "/home/JunYe/123.txt" os.path.dirname(path) # 回傳 path 的目錄路徑。 os.path.basename(path) # 回傳 path 的檔案名稱。 os.path.split(path) # 分割路徑為 (head, tail) ,其中 head 為目錄, tail 為檔案名稱。 os.path.splitext(path) # 分割路徑為 (root, ext) ,其中 root 為目錄包括檔名, ext 為副檔名。 print(os.path.dirname(path)) # /home/JunYe print(os.path.basename(path)) # 123.txt print(os.path.split(path)) # ('/home/JunYe', '123.txt') print(os.path.splitext(path)) # ('/home/JunYe/123', '.txt')
參考資料 :
1.sys — 你所不知道的 Python 標準函式庫用法 01 2.Python 速查手冊 - 12.5 基本檔案與目錄處理 os 與 os.path
2020年2月4日 星期二
Python Windows 換行符號與 Unix 換行轉換 ( convert CRLF to LF )
Convert CRLF to LF
def convertCRLFtoLF(self, filename):
WINDOWS_LINE_ENDING = b'\r\n'
UNIX_LINE_ENDING = b'\n'
with open(filename, "rb") as f:
content = f.read()
with open(filename, "wb") as f:
content = content.replace(WINDOWS_LINE_ENDING, UNIX_LINE_ENDING)
f.write(content)
用 rb wb 而不是 r w
如果用 r w 開啟 Text Mode,不管你是 windows 還是 unix ,換行都會被自動轉成 \n。所以用 str.replace() 取代不了,因為找不到 \r\n。用 binary string
如果沒有特別宣告,python3 會用 utf-8 去讀取操作,所以前加個 b 。參考資料 : stackoverflow/how-to-convert-crlf-to-lf-on-a-windows-machine-in-python
2020年1月22日 星期三
Python bin()
Python bin()
bin(10) // 0b1010
type(bin(10)) // <class 'str'>
bin(10)[2:] // 1010
2019年11月14日 星期四
Python UnitTest
Unit Test
單元測試有一個通用模式 AAA原則,理解了就可以開始實作
- Arrange : 初始化物件、要用到的參數
- Act : 呼叫要測試的方法
- Assert : 驗證測試結果
Python Unit Test
unittest — Unit testing framework,下面是官方給的範例
import unittest
class TestStringMunittestethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
unittest.main() 會執行所有 test_ 開頭的 function,主要判斷的 function 是 assert- assertEqual - 確認是否符合期望的值
- assertTrue、assertFalse - 確認是否符合期望的判斷
- assertRaise - 確認是否有特定 exception raise
D:\temp\JunYe\MyPython\Python3\LeetCode\UnitTest>python3 UnitTest.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
D:\temp\JunYe\MyPython\Python3\LeetCode\UnitTest>
然後就是我自己的實作,拿 LeetCode - 0736 當範例
import unittest
import Solution
class TestMethods(unittest.TestCase):
def test_case1(self):
# Arrange
testclass = Solution.Solution()
spec = '(add 1 2)'
# Act & Assert
self.assertEqual(testclass.evaluate(spec), 3)
def test_case2(self):
# Arrange
testclass = Solution.Solution()
spec = '(mult 3 (add 2 3))'
# Act & Assert
self.assertEqual(testclass.evaluate(spec), 15)
def test_case3(self):
# Arrange
testclass = Solution.Solution()
spec = '(let x 2 (mult x 5))'
# Act & Assert
self.assertEqual(testclass.evaluate(spec), 10)
if __name__ == '__main__':
unittest.main()
參考資料 :
2019年11月13日 星期三
Python List.pop(0)
Python list.pop(0)
python 的 list 為 dynamic array,python.pop() default 值為 -1
- python.pop() # 時間複雜度 : O()
- python.pop(0) # 時間複雜度 : O(n)
Python deque.popleft()
deque = double - ended - queue,deque.popleft() 的時間複雜度為 O(1)
所以當變數為 list 且頻繁地操作 pop(0),不妨考慮
tokens = collections.deque(mylist)
x = tokens.popleft()
參考資料 : https://stackoverflow.com/questions/32543608/deque-popleft-and-list-pop0-is-there-performance-difference
2019年10月23日 星期三
Python 檢查 dict 的索引值是否存在
Check if a given key exists in a dict
要檢查 dictionary 是否存在索引值
直接查會直接 KeyError
>>> d = {}
>>> d['a']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'a'
這時就要靠 collections.defaultdict(default_factory)>>> from collections import defaultdict
>>> d = defaultdict(int)
>>> d['a']
0
再回到檢查 dictionary 是否存在索引值的地方from collections import defaultdict
s = 'mississippi'
my_dict = defaultdict(int)
for key in s:
if key in my_dict:
my_dict[key] += 1
else:
my_dict[key] = 1
print(my_dict)
結果defaultdict(, {'m': 1, 'i': 4, 's': 4, 'p': 2})
參考資料 :
https://docs.python.org/3/library/collections.html#defaultdict-examples
https://stackoverflow.com/questions/473099/check-if-a-given-key-already-exists-in-a-dictionary-and-increment-it
2019年10月15日 星期二
Python 遞迴初學
最近用 python 刷題
因為在 dfs() 這 function 裡的 points,若沒有特別賦予值的話,就是一個指標指到 call function 所傳入的變數
注意這對 string, int, tuple 無效
參考資料 : python 新手初學
刷到要用 DFS 的題目,想用遞迴去寫才發現 python 沒有指標
看著討論的 python 大神寫的程式碼,發現都是 python 裡的參數都是指標
下面拿一題示範 0947 - Most Stones Removed with Same Row or Column
class Solution:
def removeStones(self, stones: List[List[int]]) -> int:
# 想像用 垂直線 跟 水平線 去想辦法連接所有石頭
# 連接了 n 個, 代表可以去掉 n -1, 看圖
# 初始樹的數目 以及 複製一個 stones 去做 DFS
treeNum = 0
points = {(i, j) for i, j in stones}
# 建立 row 跟 col 兩個 List
# row 紀錄該 index 下的所有有石頭的 y 座標值
# col 紀錄該 index 下的所有有石頭的 x 座標值
row = collections.defaultdict(list)
col = collections.defaultdict(list)
for i, j in stones:
row[i].append(j)
col[j].append(i)
for i, j in stones:
if (i, j) in points:
self.dfs(i, j, row, col, points)
treeNum += 1
return len(stones) - treeNum
def dfs(self, x, y, row, col, points):
# 刪除這個點
points.discard((x, y))
# 此兩個刪除是為了加速計算
#row[x].remove(y)
#col[y].remove(x)
for i in row[x]:
if (x, i) in points:
self.dfs(x, i, row, col, points)
for i in col[y]:
if (i, y) in points:
self.dfs(i, y, row, col, points)
以 C 的角度來看 points 不是全域變數根本不可能有用,但在 python 卻成功了因為在 dfs() 這 function 裡的 points,若沒有特別賦予值的話,就是一個指標指到 call function 所傳入的變數
注意這對 string, int, tuple 無效
參考資料 : python 新手初學
2019年10月8日 星期二
Python 'a' + 1 = 'b'
在 Python 3 實現 'a' + 1 = 'b'
參考資料 : https://stackoverflow.com/questions/12797067/how-can-i-do-a-1-b-in-python
a = 'a'
print(chr(ord(a)+1)) # b
參考資料 : https://stackoverflow.com/questions/12797067/how-can-i-do-a-1-b-in-python
2019年10月2日 星期三
Python 數值互換如何運作
在 Python 裡,以下這段 Code 是被允許的
可以 import dis 看其實際如何運作。
參考資料 : stackoverflow
a = 1
b = 2
a, b = b, a # b = 1, a = 2
其理由是 Python 會計算等號右邊的值,計算完後才將值分配到等號左邊。可以 import dis 看其實際如何運作。
import dis
def bar(a, b, c, d):
d, c, b, a = a, b, c, d
dis.dis(bar)
2 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 LOAD_FAST 2 (c)
9 LOAD_FAST 3 (d)
12 BUILD_TUPLE 4
15 UNPACK_SEQUENCE 4
18 STORE_FAST 3 (d)
21 STORE_FAST 2 (c)
24 STORE_FAST 1 (b)
27 STORE_FAST 0 (a)
30 LOAD_CONST 0 (None)
33 RETURN_VALUE
參考資料 : stackoverflow
Popular Posts
-
Don't lie, I know you've been thinking it 別說謊了,我知道你一直很渴望 And two times, you let it slip from your lips 兩次了,你那不想讓人聽見的低語 You...
-
lvalue 、rvalue 基本概念 左值 (lvalue) : 一個佔據某個特定記憶體的值。 右值 (rvalue) : 一個 expression 結束後就消失的值。 基本上這兩個定義包含了全部的值,非左即右,非右即左。 int var = 4; // v...
-
You try to hold me down so I became a soldier 你想要控制所以我成為了戰士 Built up all theses walls and now I'm climbing over 監牢般的城牆如今我已越過 Thos...
-
Ooh, don't we look good together? 我們看起來是不是很棒? There's a reason why they watch all night long 這就是為何他們整晚都看著我們的原因 Yeah, kn...
-
重新踏入網頁開發 重新踏入網頁開發 (1) - Nodejs 重新踏入網頁開發 (2) - ES6 重新踏入網頁開發 (3) - Route 重新踏入網頁開發 (4) - Dependency injection ...
-
Job Company : Houzz Job : Back-End Software Engineer Source : Recruiter on LinkedIn Result : 止步二面 Summary 1. 英文程度不佳 :...
-
報告在製作時,一定會遇到要字串轉數字 在 BCB 裡有方便的函式 常見的有 String str = "123" int x = StrToInt(str); float y = StrToFloat(str); 但有時候...
-
MD5 Intro MD5 訊息摘要演算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼雜湊函式,可以產生出一個128位元(16個字元(BYTES))的雜湊值(hash value),用於確保資訊傳輸完整一致。( ...
-
總覽 讀書心得 - C++ Primer (5th Edition) - Chapter 1 讀書心得 - C++ Primer (5th Edition) - Chapter 2 (1) - Primitive Types 讀書心得 ...
-
BCB TDateTime 時間操作及應用 在 BCB 裡有關時間的操作幾乎都是與 TDateTime 這個 Class 相關 若找不到相關資料可以用 BCB 的 HELP ( 游標移到要查詢的函式或Class 並按 F1 ) 介紹幾個個人有在用的函式 TDate...