Python
文章目录
:::info PPT
課程
排版教學
HackMD完整功能
:::
:::warning
VScode python
Shift + Alt + F
自動排版
ctrl+C
強制結束
ctrl+/
全部註解
:::
init、new__和__call
https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/364923/
https://ithelp.ithome.com.tw/m/articles/10231756
型態
- int (整數)
- float (浮點數)
- bool (布林值)
- str (字串)
- List(列表) : [ ]
- Tuple(元组) : ( ) 不能更動裡面的內容
- Dictionary(字典) : { }
- None(空)
len(資料)
回傳(字數、list等)長度或項目個數。
Tuple
元組基本語法
變數 = (東西1, 東西2, ...)
元組中只包含一個元素時
需要在元素後面加逗號(,)
tup = (50,)
特殊字串
如果字串裡面有包含特殊字元
要使用脫逸字元 反斜線
\
成員運算子
運算子 | 意義 |
---|---|
in | 在指定的序列中找到值返回True ,否則返回False |
not in | 指定的序列中沒有找到值返回True ,否則返回False |
s=[1,2,3]
print(3 in s)
#print True
input
取得字串形式的使用者輸入
x=input("請輸入數字:")
x=int(x) #將字串轉換成數字型態
集合Set 基本語法
自動把字串拆解成集合
set("字串")
s=set("Hello")
print(s)
# print {'l','H','e','o'} :順序是隨機的
print("H" in s) #print True
Set 運算子
運算子 | 意義 |
---|---|
& | 交集: 取兩個集合中,相同的資料 |
| | 聯集: 取兩個集合中的所有資料,但不重複取 |
- | 差集: 從S1中,減去和S2重疊的部分 |
^ | 反交集: 取兩個集合中,不重疊的部分 |
字典Dictionary 基本語法
key-value 配對
變數 = {key1:value1, key2:value2, ...}
key是唯一的,如果重複,最後一個key的value會代替前面的
value可以重複沒關係
只能用key去拿value
所以要修改value時,只能用key去找
也可以用
in
去搜尋key值
刪除字典中的key-value pair
del dic["key"]
從列表的資料產生字典
dic={x:x*2 for x in 列表}
範例
dic={"apple":"蘋果","bug":"蟲蟲"} #字典
# key: apple,bug
print(dic["apple"]) #print 蘋果
#刪除apple鍵值對
del dic["apple"]
#轉換鍵值對
dic={x:x*2 for x in [3,4,5]}
print(dic)
# print {3: 6, 4: 8 ,5: 10}
判斷式
if (條件式一):
條件式一成立,要做的事
elif (條件式二):
條件式二成立,要做的事
else:
上述條件式都不成立,要做的事
要記得加
:
四則運算
n1=int(input("請樹入數字1:"))
n2=int(input("請樹入數字2:"))
op=input("請樹入運算: +, -, *, /")
if op=="+":
print(n1+n2)
elif op=="-":
print(n1-n2)
elif op=="*":
print(n1*n2)
elif op=="/":
print(n1/n2)
else:
print("不支援")
流程控制:迴圈基礎
range()
# 不包含3
for 變數名稱 in range(3):
||
for 變數名稱 in [0,1,2]:
# 不包含6
for 變數名稱 in range(3, 6):
||
for 變數名稱 in [3,4,5]:
while 迴圈
while迴圈基本語法
while (條件):
條件成立要做的事
for 迴圈
for迴圈基本語法
for 變數 in 列表或字串:
要做的事
for c in "Hello":
print("逐一取得字串中的字元",c)
"""
H
e
l
l
o
"""
要記得加
:
break
強制結束迴圈
n=1
while n<5:
if n==3:
break
n+=1
print(n) #print 3
continue
強制執行下一圈
忽略接下來的程式,跳進下一圈迴圈
範例
n=0
for x in range(4):
if x%2==0:
# if True 直接進入下一圈,不執行n+=1
continue #if 偶數,直接跳新迴圈
print(x) #列出非偶數
n+=1 #計算非偶數數量
print("最後的 n:",n)
else
迴圈結束前執行此區塊的命令
while 布林值:
...
else:
迴圈結束前執行此區塊的命令
for 變數 in 列表或字串:
要做的事
else:
迴圈結束前執行此區塊的命令
要記得加
:
:::info
用break強制結束迴圈時,不會執行else區塊
:::
綜合範例
找出整數平方根
輸入9. 得到 3.
輸入11 得到"沒有"整數平方根
n=int(input("請輸入數字"))
for i in range(n):
if i*i == n:
print("找到整數平方根",i)
break
else: #全部迴圈跑完之後,沒有執行到break 才會進入此區塊
print("沒有 整數平方根")
函式
1. 定義函式
def 函式名稱(參數名稱):
內部程式碼
return 資料 #or 結束函式,回傳None
要記得加
:
2. 定義參數的預設值
def 函式名稱(參數名稱=預設資料):
內部程式碼
return 資料
:::info
example
def power(base ,exp=0):
print(base**exp)
power(3,2) # print 9
power(4) # 少一個參數,用預設值
:::
-
回傳與回傳值
2.1 使用 return 結束函式
2.2 使用 return 定義回傳值
2.3 呼叫函式後,如何串接回傳值def add(n1, n2): result = n1+n2 return result # call func. get return value=add(3,4) print(value) # print 7
使用參數名稱對應
呼叫函式,以參數名稱對應資料
def 函式名稱(名稱1,名稱2):
內部程式
# 呼叫函式,以參數名稱對應資料
函式名稱(名稱2=3,名稱1=5)
def divide(n1,n2):
result=n1/n2
print(result)
divide(2,4) #印出0.5
divide(n2=2,n1=4) #print 2.0
無限/不定 參數資料
在參數名稱前面加*
def 函式名稱(*無限參數): #在參數名稱前面加*
無限參數以Tuple資料形態處理
函式內部的程式碼
# 呼叫函式,可傳入無線數量的參數
函式名稱(資料1,資料2,資料3)
# 函式接受無限參數msgs
def say(*msgs):
#Tuple的方式處理
for msg in msgs:
print(msg)
#呼叫函式,傳入三個參數資料
say("Hello","Arbitrary","Arguments")
# 平均數
def avg(*nums):
sum=0
for n in nums: # 把Tuple中的資料拿出來放入n
sum=sum+n
return sum/len(nums)
:::info
更多
- 函式的目的:包裝需要重複利用的程式碼。
- 參數的目的:替函式加入更多彈性。
- 回傳值的目的:將函式內部的資料傳遞出來。
:::
範例 SUM
def calculate(n):
sum=0
for i in range(1,n+1):
sum=sum+i
return sum
print(calculate(10))
要記得加
:
torch.nn.funtional.softmax()
torch.nn.functional.Softmax(input,dim=1)
要注意的是当dim=0时, 是对每一维度相同位置的数值进行softmax运算,举个栗子:
每一个维度(2,3)对应的数值相加为1,例如:
在第0维度中:
- [0][0][0]+[1][0][0]=0.0159+0.9841=1
- [0][0][1]+[1][0][1]=0.6464+0.3536=1
在第1维度中:
- [0][0][0]+[0][1][0]=0.1058+0.8942=1
在第2维度中:
- [1][0][0]+[1][1][0]=0.3189+0.6811=1
map(function, iterable, …)
https://www.runoob.com/python/python-func-map.html
input = [1, 2, 3, 4, 5]
output = list(map(lambda x: x+1, input))
print(output)
>>> [2, 3, 4, 5, 6]
参数
- function – 函数
- iterable – 一个或多个序列
返回值
- Python 2.x 返回列表。
- Python 3.x 返回迭代器(iterator)。
lambda教學結合map,sort
Data Structure - 資料結構
https://algorithm.yuanbin.me/zh-tw/basics_data_structure/linked_list.html#python
安裝第三方套件
PIP 套件管理工具
安裝python時,就一起安裝在你的電腦裡了
python 加入環境變數path中 才能使用pip指令 > https://hackmd.io/@yizhewang/B1zdXG4br
安裝BeautifulSoup
pip install beautifulsoup4
解讀 HTML 格式 > 使用第三方套件 BeautifulSoup 來做解析
Module 模組
載入
# 載入
import 模組名稱
# 載入+幫模組取別名
import 模組名稱 as 模組別名
使用
模組名稱/別名.函式名稱(參數資料)
模組名稱/別名.變數名稱
內建模組 - sys 模組
取得系統相關資訊
# 載入sys模組
import sys
sys.platform #系統作業系統
sys.maxsize #整數型態最大值
sys.path #模組路徑
||
# 別名
import s
s.platform #系統作業系統
s.maxsize #整數型態最大值
s.path #模組路徑
自訂模組的設計
建立幾何運算模組
- 建立新的geo.py檔案
# 在geo模組中定義幾何運算功能
# 計算2點間距離
def distance(x1,y1,x2,y2):
return ((x2-x1)**2+(y2-y1)**2)**0.5
#計算line斜率
def slope(x1,y1,x2,y2):
return (y2-y1)/(x2-x1)
載入
- 在主程式.py中,輸入import (同個路徑)
import geo
result=geo.distance(1,1,5,5)
print(result)
調整搜尋模組的路徑
在模組的搜尋路徑列表中【新增路徑】
sys.path.append("名稱")
# 當前路徑下,再加上"名稱"
# 讓python去找"名稱"的資料夾
在專案資料夾中建立一個module資料夾,專門放模組用,但是路徑會跑掉
import sys
# 搜尋模組的路徑
print(sys.path) #印出模組的搜尋路徑
# 會印出一個列表,python搜尋模組的位址全部列出來
#調整路徑
sys.path.append("modules") # 當前路徑下在+上modules
import geo
封包的設計與使用
包含模組的資料夾
用來整理分類模組程式
檔案系統中的檔案對應到模組
檔案系統中的資料夾對應到python的封包
建立封包
專案檔案配置
- 專案資料夾
- 主程式.py
- 封包資料夾
- __init __.py (有這個才會被當作封包)
- 模組一.py
- 模組二.py
如果沒有__init __.py,該資料夾會被視為普通的資料夾
專案檔案配置範例
- 專案資料夾
- main.py
- geometry
- __init __.py
- point.py
- line.py
使用封包
import某個封包中的模組
import 封包名稱.模組名稱
import 封包名稱.模組名稱 as 模組別名
封包名稱.模組名稱.函式
範例
- 先建立一個新的資料夾 geometry
- 在資料夾中建立一個檔案__init__.py (geometry變成"封包"了)
- 在封包中建立新的模組 line.py , point.py
# point.py def distance(x,y): return (x**2+y**2)**0.5
# line.py def len(x1,y1,x2,y2): return (((x2-x1)**2)+((y2-y1)**2))**0.5 def slope(x1,y1,x2,y2): return (y2-y1)/(x2-x1)
# main.py主程式 import geometry.point import geometry.line #使用模組 result=geometry.point.distance(3,4) print(result) result=geometry.line.slope(1,1,3,3) print("slope:",result) # 名字太長 使用別名 import geometry.point as point result=point.distance(3,4) print(result)
讀取、儲存文字檔案
開啟檔案 > 讀取或寫入 > 關閉檔案
開啟檔案
檔案物件=open(檔案路徑,mode=開啟模式,encoding=文字編碼)
開啟模式 | 意思 |
---|---|
r | 讀取模式 |
w | 寫入模式 |
r+ | 讀寫模式 |
utf-8 | 中文/日文編碼 |
最佳實務:使用 with … as … 語法
:::info
最佳實務
用檔案物件去操作檔案,會自動關閉檔案且安全
with open(檔案路徑, mode=開啟模式) as 檔案物件:
讀取或寫入檔案的程式
#以上區塊會自動、安全的關閉檔案
:::
要記得加
:
讀取檔案
- 讀取全部文字
檔案物件.read()
- 一次讀取一行
for 變數 in 檔案物件: 從檔案依序讀取每行文字到變數中
- 讀取 JSON 格式
import json #載入json模組 讀取到的資料=json.load(檔案物件)
寫入檔案
-
寫入文字
檔案物件.write(字串)
-
寫入換行符號
檔案物件.write("這是範例文字\n")
-
寫入 JSON 格式
import json json.dump(要寫入的資料, 檔案物件)
關閉檔案
檔案物件.close()
範例
# 存檔案
file=open("data.txt" , mode="w",encoding="utf-8") #開啟 #寫入中文需要用utf-8開啟
file.write("Hello file\nSecond 好棒棒") #操作
file.close() #關閉
# 最佳實務:存檔案
with open("data.txt", mode="w",encoding="utf-8") as file:
file.write("5\n3")
# 以上區塊會自動、安全的關閉檔案,不需要自己close
# 最佳實務:讀檔案
with open("data.txt", mode="r",encoding="utf-8") as file:
data=file.read() #一次把所有資料讀出來
print(data)
# 打算把file裡面的數字一行行讀出來,並計算總合
sum=0
with open("data.txt", mode="r",encoding="utf-8") as file:
for line in file: #一行行讀取
sum+=int(line) #把數字讀出加總
print(sum)
使用JSON格式讀取,複寫檔案
先建立一個檔案 config.json在路徑中
# 先建立一個檔案 config.json在路徑中
{
"name":"My Name",
"version":"1.2.5"
}
# 使用JSON格式 從檔案中讀取 JSON資料,放入 變數data 裡面
import json
with open("config.json",mode="r") as file:
data=json.load(file)
print(data) # data 是一個字典資料
print("name: " , data["name"])
print("version: " , data["version"])
# {'name': 'My Name', 'version': '1.2.5'}
# name: My Name
# version: 1.2.5
# 修改變數中的資料;只有修改在py檔案中,還要再寫回
data["name"]="New Name"
# 把最新資料複寫回檔案
with open("config.json",mode="w") as file:
json.dump(data, file) # 複寫檔案
:::info
json讀取資料會是一個字典
讀取dict 要使用[ ]
:::
Class的定義與使用
封裝變數或函式: 封裝的變數或函式,統稱 類別的屬性
class定義
class 類別名稱:
定義封裝的變數
定義封裝的函式
要記得加
:
# 定義 Test 類別
class Test:
x=3 # 定義變數
def say(): # 定義函式
print("Hello")
class使用
類別名稱.屬性名稱
# 定義 Test 類別
class Test:
x=3
def say():
print("Hello")
# 使用 Test 類別
Test.x+3 # 取得屬性 x 的資料 +3
Test.say() # 呼叫屬性 say 函式
範例
- 定義類別 與 類別屬性 (封裝在類別中的變數和函式)
- 定義一個類別 IO,有兩個屬性 supportedSrcs 和 read
# 定義類別 與 類別屬性 (封裝在類別中的變數和函式)
# 定義一個類別 IO,有兩個屬性 supportedSrcs 和 read
class IO:
supportedSrcs=["console","file"] # 資料來源: 支援的來源用一個列表表示
def read(src):
if src not in IO.supportedSrcs:
print("Not Supported")
else:
print("Read from", src)
# 使用類別
print(IO.supportedSrcs) # ['console', 'file']
IO.read("file") # Read from file
IO.read("internet") # Not Supported
實體物件的建立與使用
類別的兩種用法
- 類別與類別屬性
- 類別與實體物件、實體屬性、實體方法
本篇教學為2.部分
建立實體物件
建立 > 使用
要先建立實體物件,然後才能使用實體屬性
class 類別名稱:
# 定義初始化函式,固定叫做__init__
def __init__(self,其他變數):
透過操作 self 來定義實體屬性
self.變數=其他變數
# 建立實體物件,放入變數 obj 中
obj=類別名稱() # 呼叫初始化函式
class Point:
def __init__(self):
self.x=3
self.y=4
# 建立實體物件
# 此實體物件包函 x 和 y 兩個實體屬性
p=Point()
class Point:
def __init__(self, x, y):
self.x=x
self.y=y
# 建立實體物件
# 建立時,可直接傳入參數資料
p=Point(1, 5)
使用實體
實體物件.實體屬性名稱
class Point:
def __init__(self, x, y):
self.x=x
self.y=y
# 建立實體物件,並取得實體屬性資料
p=Point(1, 5)
print(p.x+p.y)
範例
# Point 實體物件的設計: cl平面座標上的點
class Point:
def __init__(self,x=0,y=0): #設計初始
self.x=x
self.y=y
p1=Point(3,4) #建立物件初始化
print(p1.x, p1.y) # 3 4
p2=Point(5,6)
print(p2.x, p2.y) # 5 6
p3=Point() #如果未打初始
print(p3.x, p3.y) # 0 0
# FullName 實體物件的設計: 分開記錄姓、名資料的全名
class FullName:
def __init__(self,first,last):
self.first=first
self.last=last
name1=FullName("C.W.","Peng")
print(name1.first, name1.last)
name2=FullName("S.W.","Seng")
print(name2.first, name2.last)
實體方法
封裝在實體物件中的函式
class 類別名稱:
# 定義初始化函式
def __init__(self):
定義實體屬性
定義實體方法 / 函式
# 建立實體物件,放入變數 obj 中
obj=類別名稱()
class 類別名稱:
# 定義的初始化函式
def __init__(self):
封裝在實體物件中的變數
def 方法名稱(self, 更多自訂參數):
方法主體,透過 self 操作實體物件
# 建立實體物件,放入變數 obj 中
obj=類別名稱()
使用實體方法
實體物件.實體方法名稱(參數資料)
class Point:
def __init__(self, x, y):
self.x=x
self.y=y
def show(self): #self一定要寫
print(self.x, self.y)
p=Point(1, 5) # 建立實體物件
p.show() # 呼叫實體方法
範例
# Point 實體物件的設計: 平面座標上的點
class Point:
def __init__(self,x=0,y=0): # 設計初始
self.x=x
self.y=y
# 定義實體方法
def show(self):
print(self.x, self.y)
def distance(self,targetX,targetY):
return ((self.x-targetX)**2+(self.y-targetY)**2)**0.5
p=Point(3,4)
p.show() # 呼叫實體方法/函式
result=p.distance(0,0) # 需要放參數
print(result)
先準備2個檔案:
data1.txt: 哈哈哈哈哈哈
data2.txt: FUXK YOU
# File 實體物件設計L 包裝檔案讀取的程式
class File:
def __init__(self, name):
self.name=name
self.file=None #尚未開啟檔案: 初期是 None
def open(self):
#開啟檔案路徑,並放入實體物件file裡面
self.file=open(self.name, mode="r",encoding="utf-8")
def read(self):
return self.file.read()
# 第一個檔案
f1=File("data1.txt")
f1.open()
data=f1.read()
print(data)
# 第二個檔案
f2=File("data2.txt")
f2.open()
data=f2.read()
print(data)
:::info
尚未開啟檔案: 初期是 None
要記得寫self
:::
網路連線程式、公開資料串接
抓取台北市政府的資料
確認公開資料格式
JSON、CSV、或其他格式
解讀 JSON 格式 > 使用內建的 json 模組
解讀 HTML 格式 > 使用第三方套件 BeautifulSoup 來做解析
適合的資料來源: 台北市政府公開資料
載入urllib模組、下載特定網址資料
import urllib.request
檔案名稱請不要取得跟模組一樣
系统搜索模块的优先顺序是:程序主目录,然后是系统环境变量定义的路径,然后才是标准库目录。 如果按这个顺序找到了,当然就不再向下找了。因为文件名正好是urllib,本意想导入标准库目录下的urllib,结果把自己的当前文件导入了
import urllib.request as request
# 在"網址"放入想要連線的網址
# python會回傳response物件
with request.urlopen(網址) as response:
data=response.read()
print(data)
網路連線
import urllib.request as request
src="https://www.ntu.edu.tw/"
with request.urlopen(src) as response:
data=response.read().decode("utf-8")
# 因網站有中文要用uft-8解讀
print(data)
# 取得台大網站原始碼(HTML, CSS , JS)
:::info
網站有中文要用.decode(“utf-8”)解讀
:::
串接,擷取公開資料
用API去連線
臺北市內湖科技園區廠商名錄
會提供連線API網址範例
https://data.taipei/api/v1/dataset/296acfa2-5d93-4706-ad58-e83cc951863c?scope=resourceAquire
用網頁打開API網址會看到一堆資料
仔細看資料是一個JSON格式
results是用陣列去存 in JSON
python中是list裡面 dict.字典方式去存資料
import urllib.request as request
import json
src="https://data.taipei/api/v1/dataset/296acfa2-5d93-4706-ad58-e83cc951863c?scope=resourceAquire"
with request.urlopen(src) as response:
data=json.load(response)
# 利用 json模組處理 json資料格式
# 將公司名稱列表出來
clist=data["result"]["results"]
# 寫法是在API中: data是指全部 > reslut下的results
print(clist) #仔細看資料列表的格式
#把公司名稱資料抓進檔案中
with open("data.txt","w",encoding="utf-8") as file
# 一筆筆將公司名稱列出來
for company in clist:
print(company["公司名稱"])
# '公司名稱' 是key , '台積電' 是value
:::info
json.load(response)
用 json模組處理 json資料格式,會自己處理json格式資料細節,不用自己處理資料
:::
網路爬蟲 Web Crawler
- 抓取特定網址的資料
- 解析 HTML 格式資料
盡可能讓程式模仿使用者樣子
抓取的網頁,取得網址
解讀 HTML 格式 > 使用第三方套件 BeautifulSoup 來做解析
HTML格式資料
<html> // 用小括號框起來的叫【標籤】
<head>
</title>HTML格式</title> //有title代表【網頁的標題】
</head>
//head 和 body是相鄰的標籤
<body>
<div class="list"> //div標籤 有個屬性叫做class
<span>階層結構</span>
<span>樹狀結構</span>
</div>
</body>
</html> // 有斜線代表【結束】
安裝BeautifulSoup
在terminal中
pip install beautifulsoup4
python 加入path> https://hackmd.io/@yizhewang/B1zdXG4br
:::info
CMD
去CMD 移動到python磁碟機
C:\Users\Tracy HO>F:
//確認Python是否有讀取到
F:>python --version
Python 3.8.9
//安裝pip版本最新
F:>py -m pip install -U pip
Requirement already satisfied: pip in f:\python_3.8.9\lib\site-packages (21.0.1)
//確認pip版本
F:>py -m pip --version
pip 21.0.1 from F:\Python_3.8.9\lib\site-packages\pip (python 3.8)
//安裝第三方套件
F:>pip install beautifulsoup4
Collecting beautifulsoup4
Downloading beautifulsoup4-4.9.3-py3-none-any.whl (115 kB)
|██████████████ | 51 kB 544 kB/s eta 0:00:01
|█████████████████ | 61 kB 653 kB/s eta 0:00
|████████████████████ | 71 kB 657 kB/s eta 0
|██████████████████████▌ | 81 kB 655 kB/s et
|█████████████████████████▌ | 92 kB 737 kB/s
|████████████████████████████ | 102 kB 819 k
|███████████████████████████████ | 112 kB 81
|████████████████████████████████| 115 kB 8
19 kB/s
Collecting soupsieve>1.2
Downloading soupsieve-2.2.1-py3-none-any.whl (33 kB)
Installing collected packages: soupsieve, beautifulsoup4
Successfully installed beautifulsoup4-4.9.3 soupsieve-2.2.1
完成
:::
實務操作:抓取 PTT 電影版的文章標題
看起來像人類
- 觀察網址
- 右鍵 【檢視網頁原始碼】 會抓到這些原始碼
- chrome中 【選單】>【更多工具】>【開發人員工具】 或 直接按【F12】
- 上方有個標籤選單【Network】>【All】> 再按【F5】
為了要把網頁顯示出來 會做了非常多的網路連線
- 看到最上面有個【index.html】>右邊視窗的左邊【Headers】>【Request Headers】> 最重要的是【user-agent】
用瀏覽器發送網路連線到PTT的伺服器會附加很多資訊,附加【Request Headers】資訊會代表我們是正常的使用者
【user-agent】代表我們是使用怎樣的OS/瀏覽器
必須讓我們的程式也發送這些訊息,PTT就會認為我們是正常人
解析原始碼
使用BeautifulSoup
隨便複製一個標題,ctrl+f 搜尋原始碼中標題的位子在哪一層結構
:::info
發現每個文章標題都會被 <div class="title">``````<a>
包住
:::
# 抓取 PTT 電影版的網頁原始碼(HTML)
import urllib.request as req
url="https://www.ptt.cc/bbs/movie/index.html"
# 連線會被拒絕,因為網站看出來我們不像是正常的使用者
# 解決方法: 建立一個Request物件,附加Request Headers的資訊
request=req.Request(url, headers={
#複製自己瀏覽器裡面的user-agent
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"
})
# urlopen不要直接給網址,改給request
with req.urlopen(request) as response:
data=response.read().decode("utf-8")
print(data) # 有東西出來就表示有抓到資料
# 解析原始碼,取得每篇文章的標題
# 在terminal 打上pip install beautifulsoup4
import bs4
root=bs4.BeautifulSoup(data, "html.parser") # 讓 BeautifulSoup 協助我們解析HTML 格式文件
# 告訴他是html的原始碼,請bs4做解析
#以下為篩選想要的資料#
#root代表整份網頁,title代表網頁的標籤
print(root.title) # <title>看板 movie 文章列表 - 批踢踢實業坊</title>
print(root.title.string) # 抓title內的文字: 看板 movie 文章列表 - 批踢踢實業坊
# 只會找到其中一個標題
titles=root.find("div",class_="title") #尋找 class="title" 的 div標籤
print(titles)
#如果發現標籤裡面有個<a,要怎麼抓到中間的文字
#如果沒有a代表他是被刪除的文章
print(titles.a.string)
#.a 代表 <a> 為什麼尚氣不找老周(鄭肯)演? </a>
#.string 代表 為什麼尚氣不找老周(鄭肯)演?
# 抓到所有標題
titles=root.find_all("div",class_="title") #尋找所有class="title" 的 div標籤
print(titles) #會提供一個list列表
# 把標題一個個抓出來
for title in titles:
#有些本文已被刪除,不會有a標籤
if title.a != None: #如標題包含 a標籤(沒有被刪除),印出來
print(title.a.string)
Cookie 操作實務
網站存放在瀏覽器的一小段內容
與伺服器的互動
如果使用者的瀏覽器中有放cookies,在連線的時候cookies會放在request headers中被帶出去
追蹤網頁連結
HTML格式資料
<html> // 用小括號框起來的叫【標籤】
<head>
</title>HTML格式</title> //有title代表【網頁的標題】
</head>
//head 和 body是相鄰的標籤
<body>
<a href="https://www.google.com/">Google</a> //html原始碼中經常會包含<a>超連結
// 追蹤網頁的連結href的屬性去抓下一個網頁
</body>
</html> // 有斜線代表【結束】
連續抓取頁面實務
解析頁面的超連結並結合程式完成
抓ptt第二頁並追蹤他的上一頁連結
ptt八卦板
因為ptt 八卦版多了一個畫面 年齡限制
:::info
複製上面的程式
# 抓取 PTT 電影版的網頁原始碼(HTML)
import urllib.request as req
url="https://www.ptt.cc/bbs/movie/index.html"
# 連線會被拒絕,因為網站看出來我們不像是正常的使用者
# 解決方法: 建立一個Request物件,附加Request Headers的資訊
request=req.Request(url, headers={
#複製自己瀏覽器裡面的user-agent
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"
})
# urlopen不要直接給網址,改給request
with req.urlopen(request) as response:
data=response.read().decode("utf-8")
print(data) # 有東西出來就表示有抓到資料
# 解析原始碼,取得每篇文章的標題
# 在terminal 打上pip install beautifulsoup4
import bs4
root=bs4.BeautifulSoup(data, "html.parser") # 讓 BeautifulSoup 協助我們解析HTML 格式文件
# 告訴他是html的原始碼,請bs4做解析
#以下為篩選想要的資料#
#root代表整份網頁,title代表網頁的標籤
print(root.title) # <title>看板 movie 文章列表 - 批踢踢實業坊</title>
print(root.title.string) # 抓title內的文字: 看板 movie 文章列表 - 批踢踢實業坊
# 只會找到其中一個標題
titles=root.find("div",class_="title") #尋找 class="title" 的 div標籤
print(titles)
#如果發現標籤裡面有個<a,要怎麼抓到中間的文字
#如果沒有a代表他是被刪除的文章
print(titles.a.string)
#.a 代表 <a> 為什麼尚氣不找老周(鄭肯)演? </a>
#.string 代表 為什麼尚氣不找老周(鄭肯)演?
# 抓到所有標題
titles=root.find_all("div",class_="title") #尋找所有class="title" 的 div標籤
print(titles) #會提供一個list列表
# 把標題一個個抓出來
for title in titles:
#有些本文已被刪除,不會有a標籤
if title.a != None: #如標題包含 a標籤(沒有被刪除),印出來
print(title.a.string)
:::
:::info
如果純粹改網址就跑上面的code會無法抓到東西
這件事情跟cookie有關
因為八卦板有18歲的警告畫面
我們沒有給他over18的訊息 所以沒有給我們回應
:::
【F12 or 開發人員工具】 > 【Application】 > 【Cookies】 > https://www.ptt.cc
列表中 就是ptt放在我的瀏覽器的cookies (放了5個小資料)
over18 的餅乾就是紀錄有沒有超過18歲 就不會跳出那個確認畫面
Name | Value | Domain |
---|---|---|
名字 | 資料 | 誰放的 |
over18 | 1 | www.ptt.cc |
:::info
可以手動把cookie清掉
:::
觀察網路連線
模仿他 帶上over18的cookie
【F12 開發者工具】 > 【Network】 > 【index.html】 > 【Headers】 > 【cookie】 > 【over18=1】
over18用分號隔開了
把cookie放在headers中
request=req.Request(url, headers={
=="cookie":"over18=1",==
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"
})
不斷抓別的頁面
觀察八卦版的頁面
上頁 是一個超連結,也就是一個<a>
所以概念很簡單,抓到第一個頁面後動態的去抓上一頁抓取連結
-
對【上一頁】右鍵 > 【檢查】
-
觀察這個程式沒什麼特別之處
利用篩選 ‹ 上頁 去抓取超連結的網址 (對文字按兩下就可以複製起來) -
再去code寫
nextLink=root.find("a",string="‹ 上頁")
# 找到內文是 "‹ 上頁"的a標籤 -
定義一個函式
def getData(url):
,且把url=...
刪除 -
將底下程式進行縮排 成為一個function內容
-
main程式 先設定原網址 呼叫getData(原網址)
包裝完畢確認能跑出一樣的結果
:::info
code# 抓取 PTT 八卦板 HTML原始碼 import urllib.request as req # 將抓取頁面內容變成一個function def getData(url): # 連線會被拒絕,因為網站看出來我們不像是正常的使用者 # 解決方法: 建立一個Request物件,附加Request Headers的資訊 request=req.Request(url, headers={ #告訴ptt 我們已經按過over18, 回傳cookie內容 "cookie":"over18=1", #複製自己瀏覽器裡面的user-agent "User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36" }) # urlopen不要直接給網址,改給request with req.urlopen(request) as response: data=response.read().decode("utf-8") # 解析原始碼,取得每篇文章的標題 import bs4 root=bs4.BeautifulSoup(data, "html.parser") # 告訴他是html的原始碼,請bs4做解析 titles=root.find_all("div",class_="title") #尋找所有class="title" 的 div標籤 # 把標題一個個抓出來 for title in titles: #有些本文已被刪除,不會有a標籤 if title.a != None: #如標題包含 a標籤(沒有被刪除),印出來 print(title.a.string) # 抓取上一頁的連結 nextLink=root.find("a",string="‹ 上頁") # 找到內文是 "‹ 上頁"的a標籤 return nextLink["href"] # nextLink代表整個<a>標籤 只要再打個["屬性名稱"] # > /bbs/Gossiping/index39701.html (缺少前面的ptt.cc,但足夠使用) # 抓取一個頁面的標題 pageURL="https://www.ptt.cc/bbs/Gossiping/index.html" pageURL=getData(pageURL) print(pageURL)
:::
-
將pageURL 前面加
"https://www.ptt.cc" +getData(pageURL)
-
增加 迴圈 算要抓幾個頁面
# 頁數計算器 count=0 while count<3 : pageURL="https://www.ptt.cc"+getData(pageURL) count+=1
PTT 八卦板 連續抓3頁標題
# 抓取 PTT 八卦板 HTML原始碼
import urllib.request as req
# 將抓取頁面內容變成一個function
def getData(url):
# 連線會被拒絕,因為網站看出來我們不像是正常的使用者
# 解決方法: 建立一個Request物件,附加Request Headers的資訊
request=req.Request(url, headers={
#告訴ptt 我們已經按過over18, 回傳cookie內容
"cookie":"over18=1",
#複製自己瀏覽器裡面的user-agent
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"
})
# urlopen不要直接給網址,改給request
with req.urlopen(request) as response:
data=response.read().decode("utf-8")
# 解析原始碼,取得每篇文章的標題
import bs4
root=bs4.BeautifulSoup(data, "html.parser") # 告訴他是html的原始碼,請bs4做解析
titles=root.find_all("div",class_="title") #尋找所有class="title" 的 div標籤
# 把標題一個個抓出來
for title in titles:
#有些本文已被刪除,不會有a標籤
if title.a != None: #如標題包含 a標籤(沒有被刪除),印出來
print(title.a.string)
# 抓取上一頁的連結
nextLink=root.find("a",string="‹ 上頁") # 找到內文是 "‹ 上頁"的a標籤
return nextLink["href"] # nextLink代表整個<a>標籤 只要再打個["屬性名稱"]
# > /bbs/Gossiping/index39701.html (缺少前面的ptt.cc,但足夠使用)
# 抓取一個頁面的標題
pageURL="https://www.ptt.cc/bbs/Gossiping/index.html"
# 頁數計算器
count=0
while count<3 : # 抓3頁
pageURL="https://www.ptt.cc"+getData(pageURL)
count+=1
AJAX / XHR 網站技術分析實務
AJAX 網頁前端的JavaScript程式技術
網頁載入後持續和伺服器互動的技術
從第二次請求之後額外的動作在網頁的前端 都稱做address的技巧
:::info
傳統網頁運作流程
:::
Medium 文章列表 (舊版)
抓取知名網站medium.com的首頁文章列表
-
分析網站運作模式
2.1 仔細觀察網站資料的載入時間點
html > F5 會瞬間跑出來
ajax > F5 會重新跑出一個殼 才慢慢把內容秀出來
2.2 檢查原始碼是否包含網站資料
html > 撿查網頁原始碼 html原始碼內有文章標題名稱
ajax > 原始碼沒有文章標題名稱
:::info
關鍵問題
認出網站運作模式,找出真正能抓到資料的網址
(現在medium不給抓取了 可以參考下一部最新的爬蟲教學哦)
::: -
【F12 or 開發人員工具】>【XHR】>【F5】
就會紀錄所有採用ajax技術所發送的連線
列出一堆連線:代表著這些網站中有跑一些javascript程式 然後去跟網站伺服器作連線
在這些列表連線中有沒有包含文章標題資訊 也就是address技術
-
點擊每個【連線】> 會提供連線的資訊 > 【Preview】連線發出去得到的結果/【Response】原始的JSON格式 > Name很像會跟標題做連結的
-
【Preview】JSON格式 >【Payload】> 底下一個個點開看有沒有文章標題的資訊
-
網址
https://medium.com/_/api/home-feed
-
在PTT電影版的程式中做一些修改
:::info
code
# 抓取medium的文章資料
import urllib.request as req
url="https://medium.com/_/api/home-feed"
# 連線會被拒絕,因為網站看出來我們不像是正常的使用者
# 解決方法: 建立一個Request物件,附加Request Headers的資訊
request=req.Request(url, headers={
#複製自己瀏覽器裡面的user-agent
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"
})
# urlopen不要直接給網址,改給request
with req.urlopen(request) as response: #發送連線出去
data=response.read().decode("utf-8") #取得data,根據觀察 取得的資料是 JSON格式
# 解析json原始碼取得篇文章標題
# JSON格式需要用python內建的json模組 ,BeautifulSoup是解析 HTML格式
import json
data=json.loads(data) # 把原始 JSON的資料 解析成 字典/列表的表示形式
print(data)
#if會解析失敗,要在去觀察發生什麼事
:::
- 發現【Preview】code前面有多一個
])}while(1);</x>
很奇怪,影響到JSON格式的解析 - 對data使用replace進行奇怪文字的替換,在進行JSON解析
# JSON格式需要用python內建的json模組 import json data=data.replace("])}while(1);</x>","") # 把多出來會影響解析JSON格式的莫名其妙的文字替換掉 data=json.loads(data) # 把原始 JSON的資料 解析成 字典/列表的表示形式 print(data) #if解析失敗,要在去觀察發生什麼事
- 【Preview】> 在JSON的格式中尋找文章的標題在哪裡
在 payload > references > Post 底下有一整串的文章
# 抓取Medium.com的文章資料
import urllib.request as req
url="https://medium.com/_/api/home-feed"
# 連線會被拒絕,因為網站看出來我們不像是正常的使用者
# 解決方法: 建立一個Request物件,附加Request Headers的資訊
request=req.Request(url, headers={
#複製自己瀏覽器裡面的user-agent
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"
})
# urlopen不要直接給網址,改給request
with req.urlopen(request) as response: #發送連線出去
data=response.read().decode("utf-8") #取得data,根據觀察 取得的資料是 JSON格式
# 解析json原始碼取得篇文章標題
# JSON格式需要用python內建的json模組 ,BeautifulSoup是解析 HTML格式
import json
data=data.replace("])}while(1);</x>","") # 把多出來會影響解析JSON格式的莫名其妙的文字替換掉
data=json.loads(data) # 把原始 JSON的資料 解析成 字典/列表的表示形式
# 取得 JSON 資料中的文章標題
posts=data["payload"]["references"]["Post"]
# 這個字典就去跑個迴圈取得文章資料 字典中的key取出來
for key in posts:
post=posts[key] # 就可以取得單篇的文章
print(post["title"]) # 取得每篇的title
:::info
KKDAYS ajax
- 找到文章標題的連結
- 找到真正資料的網址
# 抓取KKDAYS的文章資料
import urllib.request as req
url="https://www.kkday.com/zh-tw/product/ajax_productlist/A01-001?city=A01-001-00001&row=15&glang=&cat=TAG_2_1,TAG_2_2,TAG_2_3,TAG_2_4,TAG_2_6&availstartdate=&availenddate=&fprice=&eprice=&sort=&page=1"
# 連線會被拒絕,因為網站看出來我們不像是正常的使用者
# 解決方法: 建立一個Request物件,附加Request Headers的資訊
request=req.Request(url, headers={
#複製自己瀏覽器裡面的user-agent
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36"
})
# urlopen不要直接給網址,改給request
with req.urlopen(request) as response: #發送連線出去
data=response.read().decode("utf-8") #取得data,根據觀察 取得的資料是 JSON格式
# 解析json原始碼取得篇文章標題
# JSON格式需要用python內建的json模組 ,BeautifulSoup是解析 HTML格式
import json
data=json.loads(data) # 把原始 JSON的資料 解析成 字典/列表的表示形式
# 取得 JSON 資料中的文章標題
posts=data["data"]
# 這個字典就去跑個迴圈取得文章資料 字典中的key取出來
for key in posts:
print(key["name"]) # 就可以取得單篇的文章
# 取得每篇的title
:::
Request Data 操作實務
抓取 Medium.COM 網站的文章列表資料 2021 版
- 什麼是 Request Data
1.1 簡單的請求 v.s 複雜的請求
簡單的請求: 發出request的時候,不夾帶任何除了標頭headers以外的資料
複雜的請求: 發出request的時候,除了header資料還夾帶額外的資料
1.2 Request Data
請求中附加的額外資料叫做Request Data(Request Payload, Request Body)
抓取 Medium.COM 網站實務操作
:::info
關鍵問題
認出網站的AJAX模式,並懂得發出複雜的請求
:::
2. 複雜請求的觀察
2.1 注意是否有 Request Payload 資訊
2.2 注意 Content-Type 標頭的設定
- 實務操作 Medium.COM 爬蟲 2021 版
3.1 觀察網站運作
3.2 找到首頁文章標題資料網址和相關特性
3.3 利用爬蟲程式,處理標頭、Request Data 等細節後,連線抓取
觀察步驟
-
開起無痕視窗
-
【F12】 (開發者工具)
-
【Network】
-
【重新整理網頁F5】
-
找title相關的地方 一個個去找
-
【Header】 Request URL: https://medium.com/_/graphql
-
Request Payload 就是我們發送請求的附加資訊,一定要附上資訊才會執行
-
Request Payload(Request Data) >
view source
>Show more
> 要全部複製 -
Request Headers > user-agent 資訊也要代上 user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36
-
多一個header要做,會提供附加資訊的格式 Request Headers > content-type: application/json
:::info
application/json 是JSON的全名
::: -
寫code > 研究格式資料 確認標題有在資料中subtitle
-
Preview > 找出title在哪一層
# 抓取 Mdeium.COM 的文章資料 2021板 ,加入Request Data 的觀念
import urllib.request as req
import json
# 建立連線網址
url="https://medium.com/_/graphql"
# 建立一個 Request 物件,附上Request Headers 和 Request Data
#放Request Payload所有文字
requestData={"operationName":"ExtendedFeedQuery","variables":{"items":[{"postId":"f9aabc04804a","topicId":""},{"postId":"29ca6686bcab","topicId":""},{"postId":"a8d962c8f721","topicId":""},{"postId":"86d5909f748c","topicId":""},{"postId":"c4b28ed97980","topicId":""},{"postId":"9d60d4147083","topicId":""},{"postId":"89cfe75fea35","topicId":""},{"postId":"ba865d42c95a","topicId":""},{"postId":"e884883c0173","topicId":""},{"postId":"5c1283b297e7","topicId":""},{"postId":"bd3a18d9432e","topicId":""},{"postId":"56d4844f34fd","topicId":""},{"postId":"b6360bed7b06","topicId":""},{"postId":"77daca52c0f1","topicId":""},{"postId":"3ae61539e92a","topicId":""},{"postId":"a4e8468435e5","topicId":""},{"postId":"334f246f5894","topicId":""},{"postId":"1ed2b625b4cf","topicId":""},{"postId":"8b9d9adb7ddd","topicId":""},{"postId":"78cd10656656","topicId":""}]},"query":"query ExtendedFeedQuery($items: [ExtendedFeedItemOptions!]!) {\n extendedFeedItems(items: $items) {\n post {\n ...PostListModulePostPreviewData\n __typename\n }\n metadata {\n topic {\n id\n name\n __typename\n }\n __typename\n }\n __typename\n }\n}\n\nfragment PostListModulePostPreviewData on Post {\n id\n firstPublishedAt\n readingTime\n createdAt\n mediumUrl\n previewImage {\n id\n __typename\n }\n title\n collection {\n id\n domain\n slug\n name\n navItems {\n url\n __typename\n }\n logo {\n id\n __typename\n }\n avatar {\n id\n __typename\n }\n __typename\n }\n creator {\n id\n name\n username\n imageId\n mediumMemberAt\n ...userUrl_user\n __typename\n }\n visibility\n isProxyPost\n isLocked\n ...HomeFeedItem_post\n ...HomeReadingListItem_post\n ...HomeTrendingModule_post\n __typename\n}\n\nfragment HomeFeedItem_post on Post {\n __typename\n id\n title\n firstPublishedAt\n mediumUrl\n collection {\n id\n name\n domain\n logo {\n id\n __typename\n }\n __typename\n }\n creator {\n id\n name\n username\n imageId\n mediumMemberAt\n __typename\n }\n previewImage {\n id\n __typename\n }\n previewContent {\n subtitle\n __typename\n }\n readingTime\n tags {\n ...TopicPill_tag\n __typename\n }\n ...BookmarkButton_post\n ...CreatorActionOverflowPopover_post\n ...PostPresentationTracker_post\n ...PostPreviewAvatar_post\n}\n\nfragment TopicPill_tag on Tag {\n __typename\n id\n displayTitle\n}\n\nfragment BookmarkButton_post on Post {\n ...SusiClickable_post\n ...WithSetReadingList_post\n __typename\n id\n}\n\nfragment SusiClickable_post on Post {\n id\n mediumUrl\n ...SusiContainer_post\n __typename\n}\n\nfragment SusiContainer_post on Post {\n id\n __typename\n}\n\nfragment WithSetReadingList_post on Post {\n ...ReadingList_post\n __typename\n id\n}\n\nfragment ReadingList_post on Post {\n __typename\n id\n viewerEdge {\n id\n readingList\n __typename\n }\n}\n\nfragment CreatorActionOverflowPopover_post on Post {\n allowResponses\n id\n statusForCollection\n isLocked\n isPublished\n clapCount\n mediumUrl\n pinnedAt\n pinnedByCreatorAt\n curationEligibleAt\n mediumUrl\n responseDistribution\n visibility\n ...useIsPinnedInContext_post\n pendingCollection {\n id\n name\n creator {\n id\n __typename\n }\n avatar {\n id\n __typename\n }\n viewerEdge {\n id\n isEditor\n __typename\n }\n domain\n slug\n __typename\n }\n creator {\n id\n viewerEdge {\n id\n isBlocking\n __typename\n }\n ...MutePopoverOptions_creator\n ...auroraHooks_publisher\n __typename\n }\n collection {\n id\n name\n creator {\n id\n __typename\n }\n avatar {\n id\n __typename\n }\n viewerEdge {\n id\n isEditor\n __typename\n }\n domain\n slug\n ...MutePopoverOptions_collection\n ...auroraHooks_publisher\n __typename\n }\n viewerEdge {\n clapCount\n id\n shareKey\n __typename\n }\n ...ClapMutation_post\n __typename\n}\n\nfragment MutePopoverOptions_creator on User {\n id\n __typename\n}\n\nfragment MutePopoverOptions_collection on Collection {\n id\n __typename\n}\n\nfragment ClapMutation_post on Post {\n __typename\n id\n clapCount\n viewerEdge {\n id\n clapCount\n __typename\n }\n ...MultiVoteCount_post\n}\n\nfragment MultiVoteCount_post on Post {\n id\n ...PostVotersNetwork_post\n __typename\n}\n\nfragment PostVotersNetwork_post on Post {\n voterCount\n viewerEdge {\n id\n clapCount\n __typename\n }\n recommenders {\n name\n __typename\n }\n __typename\n id\n}\n\nfragment useIsPinnedInContext_post on Post {\n id\n collection {\n id\n __typename\n }\n pendingCollection {\n id\n __typename\n }\n pinnedAt\n pinnedByCreatorAt\n __typename\n}\n\nfragment auroraHooks_publisher on Publisher {\n __typename\n ... on Collection {\n isAuroraEligible\n isAuroraVisible\n viewerEdge {\n id\n isEditor\n __typename\n }\n __typename\n id\n }\n ... on User {\n isAuroraVisible\n __typename\n id\n }\n}\n\nfragment PostPresentationTracker_post on Post {\n id\n visibility\n previewContent {\n isFullContent\n __typename\n }\n collection {\n id\n slug\n __typename\n }\n __typename\n}\n\nfragment PostPreviewAvatar_post on Post {\n __typename\n id\n collection {\n id\n name\n ...CollectionAvatar_collection\n ...collectionUrl_collection\n __typename\n }\n creator {\n id\n username\n name\n ...UserAvatar_user\n ...userUrl_user\n __typename\n }\n}\n\nfragment CollectionAvatar_collection on Collection {\n name\n avatar {\n id\n __typename\n }\n ...collectionUrl_collection\n __typename\n id\n}\n\nfragment collectionUrl_collection on Collection {\n id\n domain\n slug\n __typename\n}\n\nfragment UserAvatar_user on User {\n __typename\n username\n id\n name\n imageId\n mediumMemberAt\n ...userUrl_user\n}\n\nfragment userUrl_user on User {\n __typename\n id\n customDomainState {\n live {\n domain\n __typename\n }\n __typename\n }\n username\n hasSubdomain\n}\n\nfragment HomeReadingListItem_post on Post {\n id\n title\n creator {\n id\n name\n username\n ...UserAvatar_user\n __typename\n }\n mediumUrl\n createdAt\n readingTime\n collection {\n id\n name\n navItems {\n url\n __typename\n }\n ...CollectionAvatar_collection\n __typename\n }\n visibility\n __typename\n}\n\nfragment HomeTrendingModule_post on Post {\n id\n ...HomeTrendingPostPreview_post\n __typename\n}\n\nfragment HomeTrendingPostPreview_post on Post {\n id\n title\n mediumUrl\n readingTime\n firstPublishedAt\n ...PostPreviewAvatar_post\n ...PostPresentationTracker_post\n __typename\n}\n"}
#req.Request(參數:(1)網址,(2)標頭,(3)data=請求的額外資料(requestData))
request=req.Request(url, headers={
"Content-Type":"application/json", #代表Request Data的格式
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36"
}, data=json.dumps(requestData).encode("utf-8"))
# 發出請求
with req.urlopen(request) as response:
result=response.read().decode("utf-8")
# print(result) #確認有無順利印出preview那些文字
# 解析 JSON 格式 的資料,取得每篇文章的標題
result=json.loads(result) #把json格式讀進來py 轉換成字典
# print(result["data"]["extendedFeedItems"][0]["post"]["title"]) #試著印出第一篇文章的標題
items=result["data"]["extendedFeedItems"]
for item in items:
print(item["post"]["title"])
Flask 網站架設
基礎環境建置
-
安裝 Flask
pip install Flask -
建立專案資料夾 網站專案,撰寫程式
在電腦中的任一位置建立"專案資料夾"
撰寫第一支網站伺服器端的程式
2.1 認識__name__
代表目前執行的模組名稱
2.2 認識函式的裝飾 (function decorators):附加在函式上的功能 -
啟動網站 伺服器、測試網站
3.1 學會啟動網站(使用命令列執行py程式,即起動網站)
測試網站: 將網址貼到瀏覽器的網址列中,測試網站運作
3.2 網站程式的基礎觀念
3.3 網站的路徑 (path) 處理
:::warning
此部分只有在自己主機才能運作
:::
專案建立步驟
- 在電腦中建立一個資料夾
- VScode打開剛剛的資料夾
- 在Terminal 安裝 pip install Flask
- 建立新的python程式app.py檔
:::info
如果成功啟動,會出現
* Running on http://127.0.0.1:5000/
如果要測試的話 直接按住ctrl+點擊http://127.0.0.1:5000/
:::
from flask import Flask
app=Flask(__name__) # __name__ 代表目前執行的模組
@app.route("/") # 函式的裝飾(Decorator): 以函式為基礎提供附加的功能
def home():
return "Hello Flask"
if __name__=="__main__": # 如果以主程式執行
app.run() # 立刻起動伺服器
如果在網址http://127.0.0.1:5000/test
後面加上test
會無法找到網頁
這部分可以額外處理
@app.route("/test") # 代表我們要處理的網路路徑
def test():
return "this is test"
Heroku 雲端主機教學
-
建立Flask專案描述檔
1.1 建立 runtime.txt
描述使用的python環境
1.2 建立 requirements.txt
描述程式運作所需要的套件
1.3 建立 Procfile
告訴Heroku如何執行程式 -
安裝 Git Tool
:::info
請搜尋Git ,下載並安裝Git tool
::: -
建立 Heroku App 應用程式
選擇建立App應用程式
3.1 註冊帳號,注意信箱驗證
3.2 建立 Heroku 應用程式 -
部屬到 Heroku App
4.1 安裝 Heroku CLI 命令列工具
按照Heroku官網,應用程式中的指示安裝
4.2 執行初始化命令
4.3 執行部屬命令
- 使用命令列模式 > 以下步驟使用命令列模式執行
- 登入Heroku > Heroku login
- 初始化專案:未來專案不用一直做 這2行命令在一開始做一次就好
git init heroku git:remote -a 專案名稱
- 更新專案:只要新程式要送到雲端更新 就要跑這3行命令
git add . git commit -m "更新的訊息" #可以通知大家更新什麼 git push heroku master
- 將程式部屬到Heroku App並測試
建立檔案
runtime.txt
python-3.6.8
requirements.txt
Flask
gunicorn
:::info
gunicorn用途: 在heroku上面啟動我的專案
:::
Procfile
web gunicorn app:app
:::info
Procfile沒有附檔名,不用附檔名
主要用途 告訴heroku要怎麼啟動我們的專案
第一個app 對應的是app.py檔案
第二個app 對應的是code中
app=Flask(__name__) # __name__ 代表目前執行的模組
:::
安裝Git Tool
瘋狂點擊next完成安裝
註冊Heroku
測試開發用,密碼:
- 點
New
>create new app
- 建立 App name:
python-training-sunny
choose region(主機位址) >create app
- 找到
Deploy
(部屬) >
安裝Download and install the Heroku CLI
也是閉眼安裝
:::info
在CMD 確認有無裝好,CMD打以下 如果沒有error,有詳細訊息就是已經裝好了
git
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
[--super-prefix=<path>] [--config-env=<name>=<envvar>]
<command> [<args>]
heroku
VERSION
heroku/7.53.0 win32-x64 node-v12.21.0
:::
Heroku 專案
- Terminal中打上,即登入heroku的帳號密碼
Heroku login
- 初始化專案 (Terminal中) 只須做一次,未來不用在做
git init #回傳Initialized empty Git repository in F:/VS/Programs/FlaskWeb_py/.git/ heroku git:remote -a python-training-sunny
:::info
如果有出現更新需求,可以不用理他
» Warning: heroku update available from 7.53.0 to 7.54.0.
:::
- 部屬Heroku App
git add . git config --global user.email "XXX@gmail.com" git config --global user.name "NAME" git commit -m "First Deploy" git push heroku master # 會回傳目前雲端再發生的事情,需要等他一下 # remote: https://python-training-sunny.herokuapp.com/ deployed to Heroku
- 前往 https://python-training-sunny.herokuapp.com/
- 確認 https://python-training-sunny.herokuapp.com/test 也有出現
如果修改程式,還沒反應到線上
- 重新部屬
Terminal上git add . git commit -m "Second Deploy" git push heroku master
:::success
建議:
如果在本機端覺得測試OK了,差不多了
在用部屬命令 部屬到雲端會比較快
:::
argparse
Argparse 教學
Python实用模块(二十六)argparse
- nargs - 命令行参数应当消耗的数目,可选值为:?为0或1个参数;*为0或所有参数;+所有,并且至少一个参数
- name or flags - 一个命名或者一个选项字符串的列表,例如 weight 或 -w, --weight
- action - 当参数在命令行中出现时使用的动作基本类型。store_true和store_false是store_const分别用作存储True和False值的特殊用例,它们的默认值分别为False和True
- const - 被一些action和nargs选择所需求的常数
- default - 当参数未在命令行中出现时使用的默认值
- type - 命令行参数应当被转换成的类型
- choices - 参数值只能从几个选项里面选择
- required - 此命令行选项是否可省略,如果为True,不设置的话,会报错
- help - 一个此选项作用的简单描述
- metavar - 在使用方法消息中使用的参数值示例
- dest - 设置参数在代码中的变量名
tags: Python
Finish [name=Sunny] [time=Sun, Jun 10, 2021] [color=#907bf7]