目錄一、背景介紹二、爬蟲代碼2.1 展示爬取結果2.2 爬蟲代碼講解三、可視化代碼3.1 讀取數據3.2 數據清洗3.3 可視化3.3.1 IP屬地分析-柱形圖3.3.2 評論時間分析-折線圖3.3.3 點贊數分佈-箱線圖3.3.4 評論內容-情感分佈餅圖3.3.5 評論內容-詞雲圖四、技術總結五、 ...
3 快速入門
第二章是python基礎,故不做介紹。
FastAPI是一個現代、快速(高性能)的網路框架,用於使用基於標準Python 類型提示的Python 3.6+構建API。
FastAPI的創建者是Sebastián Ramírez。
FastAPI由Sebastián Ramírez於2018年發佈。與大多數Python Web框架相比,它在很多方面都更加現代化--充分利用了過去幾年中添加到Python 3 中的功能。本章將快速介紹FastAPI的主要功能,重點是您首先需要瞭解的內容:如何處理Web請求和響應。
3.1 FastAPI簡介
與其他網路框架一樣,FastAPI 可以幫助您構建網路應用程式。每個框架的設計都是為了通過功能、遺漏和預設設置來簡化某些操作。顧名思義,FastAPI的目標是開髮網絡API,不過您也可以將其用於傳統的網路內容應用程式。
FastAPI具有以下優勢:
-
性能:在某些情況下與Node.js和Go一樣快,與Python框架不同。
-
快速開發:容易上手,簡單清晰。
-
代碼質量更高:類型提示和模型有助於減少錯誤。
-
自動生成文檔和測試頁面:比手工編輯OpenAPI說明容易得多。
FastAPI 使用以下功能:
- Python 類型提示
- 用於網路機制的 Starlette,包括非同步支持
- 用於數據定義和驗證的 Pydantic
- 用於利用和擴展其他工具的特殊集成
這種組合為網路應用程式(尤其是 RESTful 網路服務)提供了令人滿意的開發環境。
3.2 FastAPI應用
讓我們編寫一個小巧的 FastAPI 應用程式--只有一個端點的網路服務。現在,我們處於我所說的網路層,只處理網路請求和響應。首先,安裝我們要用到的基本 Python 包:
- FastAPI 框架: pip install fastapi
- Uvicorn 網路伺服器: pip install uvicorn
- HTTPie 文本網路客戶端: pip install httpie
- 請求同步網路客戶端軟體包: pip install requests
- HTTPX 同步/非同步網路客戶端軟體包: pip install httpx
雖然curl是最著名的文本網路客戶端,但我認為HTTPie更容易使用。此外,它預設使用JSON編碼和解碼,更適合 FastAPI。在本章後面,你會看到一張截圖,其中包含訪問特定端點所需的curl命令行語法。
hello.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi")
def greet():
return "Hello? World?"
- app是頂級FastAPI對象,代表整個網路應用程式。
- @app.get("/hi") 是一個路徑裝飾器。它告訴 FastAPI 以下內容:
- 在此伺服器上對URL"/hi"的請求應被指向以下函數。
- 此裝飾器僅適用於 HTTP GET verb。您也可以使用其他 HTTP 動詞(PUT、POST 等)來響應"/hi"URL,每個動詞都有一個單獨的函數。
- def greet()是一個路徑函數--HTTP 請求和響應的主要連接點。
下一步是在網路伺服器中運行該網路應用程式。FastAPI本身不包含網路伺服器,但推薦使用Uvicorn。您可以通過兩種方式啟動Uvicorn和FastAPI 網路應用程式:外部啟動或內部啟動。
從外部通過命令行啟動Uvicorn 的方法:
$ uvicorn hello:app --reload
其中hello指的是hello.py文件,app是其中的FastAPI變數名。內部啟動如下:
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi")
def greet():
return "Hello? World?"
if __name__ == "__main__":
import uvicorn
uvicorn.run("hello:app", reload=True, host=r'0.0.0.0', port=8008)
無論哪種情況,如果hello.py發生變化,reload都會告訴 Uvicorn 重新啟動網路伺服器。在本章中,我們將經常使用自動重載功能。預設情況下都會使用你機器上的 8000 埠(名為 localhost)。如果你想使用其他方法,外部方法和內部方法都有主機和埠參數。
現在伺服器有了一個端點(/hi),可以隨時接受請求了。讓我們用多個網路客戶端進行測試:
- 對於瀏覽器,在頂部位置欄鍵入URL。
- HTTPie
$ http localhost:8008/hi
HTTP/1.1 200 OK
content-length: 15
content-type: application/json
date: Thu, 06 Jun 2024 01:41:28 GMT
server: uvicorn
"Hello? World?"
# 使用 -b 參數跳過響應頭,只列印正文。
$ http -b localhost:8008/hi
"Hello? World?"
# 使用 -v 獲取完整的請求頭和響應。
# http -v localhost:8008/hi
GET /hi HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8008
User-Agent: HTTPie/3.2.2
HTTP/1.1 200 OK
content-length: 15
content-type: application/json
date: Thu, 06 Jun 2024 01:51:23 GMT
server: uvicorn
"Hello? World?"
- Requests 或 HTTPX
>>> import requests
>>> r = requests.get("http://localhost:8008/hi")
>>> r.json()
'Hello? World?'
>>> import httpx
>>> r = httpx.get("http://localhost:8008/hi")
>>> r.json()
'Hello? World?'
3.3 HTTP請求
上面第一個HTTPie請求包含以下內容:
- 動詞 (GET) 和路徑 (/hi)
- 查詢參數(此處為"無",有的話跟在問號後面)
- 其他 HTTP 頭信息
- 正文內容(無請求)
FastAPI 將這些信息歸納為方便的定義:Header、Path、Query、Body。
FastAPI從HTTP請求的各個部分提供數據的方式是其最大的特點之一,也是對大多數Python Web框架的改進。您需要的所有參數都可以直接在path函數中聲明和提供,使用前面列表中的定義 (Path、Query 等),以及您編寫的函數。這使用了一種名為"依賴註入"的技術,我們將繼續討論這種技術,併在第 6 章中進一步闡述。
3.3.1 URL Path
讓我們在前面的應用程式中添加一個名為 who 的參數,向某人發送 Hello? 我們將嘗試不同的方法來傳遞這個新參數:
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi/{who}")
def greet(who):
return f"Hello? {who}?"
if __name__ == "__main__":
import uvicorn
uvicorn.run("hello:app", reload=True, host=r'0.0.0.0', port=8008)
在 URL 中(@app.get 之後)添加 {who},會告訴 FastAPI 在 URL 中的該位置期待一個名為 who 的變數。然後,FastAPI 將其賦值給下麵 greet() 函數中的 who 參數。這顯示了路徑裝飾器和路徑函數之間的協調。
註意不要在此處使用 Python f-string 來表示修改後的 URL 字元串 ("/hi/{who}")。
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi/{who}")
def greet(who):
return f"Hello? {who}?"
if __name__ == "__main__":
import uvicorn
uvicorn.run("hello:app", reload=True, host=r'0.0.0.0', port=8008)
執行:
# http -b localhost:8008/hi/test
"Hello? test?"
# http -b localhost:8008/hi
{
"detail": "Not Found"
}
參考資料
- 軟體測試精品書籍文檔下載持續更新 https://github.com/china-testing/python-testing-examples 請點贊,謝謝!
- 本文涉及的python測試開發庫 謝謝點贊! https://github.com/china-testing/python_cn_resouce
- python精品書籍下載 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
- Linux精品書籍下載 https://www.cnblogs.com/testing-/p/17438558.html
3.3.2查詢參數
查詢參數是 URL中?後面的name=value字元串,用&字元分隔。
from fastapi import FastAPI
app = FastAPI()
@app.get("/hi")
def greet(who):
return f"Hello? {who}?"
if __name__ == "__main__":
import uvicorn
uvicorn.run("hello:app", reload=True, host=r'0.0.0.0', port=8008)
執行:
# http localhost:8008/hi?who=Mom
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Thu, 06 Jun 2024 02:58:24 GMT
server: uvicorn
"Hello? Mom?"
# http localhost:8008/hi
HTTP/1.1 422 Unprocessable Entity
content-length: 89
content-type: application/json
date: Thu, 06 Jun 2024 02:58:31 GMT
server: uvicorn
{
"detail": [
{
"input": null,
"loc": [
"query",
"who"
],
"msg": "Field required",
"type": "missing"
}
]
}
# 註意兩個等號為GET, 冒號表示HTTP頭,一個等號為POST,
# http -b localhost:8008/hi who==Mom
"Hello? Mom?"
3.3.3 POST
from fastapi import FastAPI, Body
app = FastAPI()
@app.post("/hi")
def greet(who:str = Body(embed=True)):
return f"Hello? {who}?"
if __name__ == "__main__":
import uvicorn
uvicorn.run("hello:app", reload=True, host=r'0.0.0.0', port=8000)
執行:
# /root/code/fastapi/example^C
root@www:~/code/fastapi/example# http -v localhost:8000/hi who=Mom
POST /hi HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 14
Content-Type: application/json
Host: localhost:8000
User-Agent: HTTPie/3.2.2
{
"who": "Mom"
}
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Thu, 06 Jun 2024 03:25:34 GMT
server: uvicorn
"Hello? Mom?"
需要使用 Body(embed=True) 來告 FastAPI,這次我們從JSON格式的請求正文中獲取who的值。embed 部分意味著它看起來應該像 {"who": "Mom"},而不僅僅是 "Mom"。
3.3.4 HTTP 頭信息
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/hi")
def greet(who:str = Header()):
return f"Hello? {who}?"
if __name__ == "__main__":
import uvicorn
uvicorn.run("hello:app", reload=True, host=r'0.0.0.0', port=8000)
執行:
# http -v localhost:8000/hi who:Mom
GET /hi HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8000
User-Agent: HTTPie/3.2.2
who: Mom
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Thu, 06 Jun 2024 06:09:55 GMT
server: uvicorn
"Hello? Mom?"
3.3.5 多重請求數據
你可以在同一個路徑函數中使用多個方法。也就是說,你可以從 URL、查詢參數、HTTP 主體、HTTP 頭文件、cookie等獲取數據。你還可以編寫自己的依賴函數,以特殊方式處理和組合這些數據,例如用於分頁或身份驗證。
3.3.6哪種方法最好?
以下是一些建議:
- 在URL中傳遞參數時,遵循 RESTful 準則是標準做法。
- 查詢字元串通常用於提供可選參數,如分頁。
- body通常用於較大的輸入,如整個或部分模型。
在每種情況下,如果您在數據定義中提供了類型提示,Pydantic就會自動對您的參數進行類型檢查。這將確保參數的存在和正確性。
3.4 HTTP響應
預設情況下FastAPI會將返回的內容轉換為JSON;HTTP響應的頭行內容類型為:application/json。
3.4.1 狀態代碼
FastAPI預設返回200狀態代碼;異常會產生4xx代碼。在路徑裝飾器中,指定在一切順利的情況下應返回的 HTTP 狀態代碼可以改寫返回(異常會生成自己的代碼並覆蓋它)。
from fastapi import FastAPI, Body
app = FastAPI()
@app.post("/hi")
def greet(who:str = Body(embed=True)):
return f"Hello? {who}?"
if __name__ == "__main__":
import uvicorn
uvicorn.run("hello:app", reload=True, host=r'0.0.0.0', port=8000)
執行:
~# http localhost:8000/happy
HTTP/1.1 200 OK
content-length: 4
content-type: application/json
date: Thu, 06 Jun 2024 06:39:00 GMT
server: uvicorn
":)"
# http localhost:8000/happy3
HTTP/1.1 404 Not Found
content-length: 22
content-type: application/json
date: Thu, 06 Jun 2024 06:40:32 GMT
server: uvicorn
{
"detail": "Not Found"
}
3.4.2 Headers
from fastapi import FastAPI, Body, Response
app = FastAPI()
@app.get("/header/{name}/{value}")
def header(name: str, value: str, response:Response):
response.headers[name] = value
return "normal body"
if __name__ == "__main__":
import uvicorn
uvicorn.run("hello:app", reload=True, host=r'0.0.0.0', port=8000)
執行:
# http localhost:8000/header/marco/polo
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Thu, 06 Jun 2024 06:48:10 GMT
marco: polo
server: uvicorn
"normal body"
3.4.3 響應類型
響應類型(從 fastapi.responses 中導入這些類)包括以下內容:
- JSONResponse(預設值)
- HTMLResponse
- PlainTextResponse
- RedirectResponse
- FileResponse
- StreamingResponse
關於後兩種,我將在第15章中詳細介紹。對於其他輸出格式(也稱為 MIME 類型),可以使用通用的 Response 類,它需要以下內容:
-
content:字元串或位元組
-
media_type:字元串 MIME 類型
-
status_code:HTTP 整數狀態代碼
-
headers:字元串
3.4.4 類型轉換
路徑函數可以返回任何內容,預設情況下(使用 JSONResponse),FastAPI 會將其轉換為 JSON 字元串並返回,同時返回與之匹配的 HTTP 響應頭 Content-Length 和 Content-Type。這包括任何 Pydantic 模型類。
但它是如何做到這一點的呢?如果您使用過 Python json 庫,您可能會發現,當給定某些數據類型(如日期時間)時,它會引發異常。FastAPI 使用名為 jsonable_encoder() 的內部函數將任何數據結構轉換為 "JSONable" Python 數據結構,然後調用 json.dumps() 將其轉換為 JSON 字元串。
import datetime
import json
import pytest
from fastapi.encoders import jsonable_encoder
@pytest.fixture
def data():
return datetime.datetime.now()
def test_json_dump(data):
with pytest.raises(Exception):
_ = json.dumps(data)
def test_encoder(data):
out = jsonable_encoder(data)
assert out
json_out = json.dumps(out)
assert json_out
3.4.4 模型類型和響應模型
不同的類可能有許多相同的欄位,只是一個專門用於用戶輸入,一個用於輸出,還有一個用於內部使用。產生這些變體的原因可能包括以下幾點:
- 從輸出中移除一些敏感信息,比如去識別個人醫療數據,如果你遇到《健康保險可攜性和責任法案》(HIPAA)的要求。
- 為用戶輸入添加欄位(如創建日期和時間)。
下列展示了一個假定案例中的三個相關類:
- TagIn 是定義用戶需要提供的內容(在本例中,只是一個名為 tag 的字元串)的類。
- Tag 是由 TagIn 生成的,並增加了兩個欄位:created(創建時間)和 secret(內部字元串,可能存儲在資料庫中,但永遠不會向外界公開)。
- TagOut 是定義可以返回給用戶(通過查詢或搜索終端)的內容的類。它包含來自原始 TagIn 對象及其派生 Tag 對象的標簽欄位,以及為 Tag 生成的創建欄位,但不是秘密欄位。
from datetime import datetime
from pydantic import BaseClass
class TagIn(BaseClass):
tag: str
class Tag(BaseClass):
tag: str
created: datetime
secret: str
class TagOut(BaseClass):
tag: str
created: datetime
您可以通過不同方式從FastAPI路徑函數返回預設JSON以外的數據類型。其中一種方法是在路徑裝飾器中使用 response_model 參數,讓 FastAPI 返回其他類型的數據。FastAPI 將放棄任何在返回對象中存在但不在 response_model 指定的對象中的欄位。
假設你編寫了一個名為 service/tag.py 的新服務模塊,其中包含 create() 和 get() 函數,為該網路模塊提供了調用的內容。這些低層堆棧細節在這裡並不重要。重要的是底部的 get_one() 路徑函數,以及路徑裝飾器中的 response_model=TagOut。這會自動將內部 Tag 對象轉換為經過消毒的 TagOut 對象。
import datetime
from fastapi import FastAPI
from model.tag import TagIn, Tag, TagOut
import service.tag as service
app = FastAPI()
@app.post('/')
def create(tag_in: TagIn) -> TagIn:
tag: Tag = Tag(tag=tag_in.tag, created=datetime.utcnow(),
secret="shhhh")
service.create(tag)
return tag_in
@app.get('/{tag_str}', response_model=TagOut)
def get_one(tag_str: str) -> TagOut:
tag: Tag = service.get(tag_str)
return tag
儘管我們返回的是 Tag,但response_model會將其轉換為 TagOut。
3.5 自動化文檔
上面3.3.3 POST的自動文檔http://localhost:8000/docs。
FastAPI會根據您的代碼生成一個OpenAPI規範,並包含這個頁面來顯示和測試您的所有端點。這隻是其秘訣之一。
單擊綠色框右側的向下箭頭,打開它進行測試。
點擊右側的 "試用 "按鈕。現在你會看到一個區域,可以在正文部分輸入數值(。
點擊 "字元串"。將其更改為 "Cousin Eddie"(保留雙引號)。然後單擊底部的藍色 "執行 "按鈕。
3.6複雜數據
這些示例只展示瞭如何向端點傳遞單個字元串。許多端點,尤其是 GET 或 DELETE 端點,可能根本不需要參數,或者只需要幾個簡單的參數,如字元串和數字。但在創建(POST)或修改(PUT 或 PATCH)資源時,我們通常需要更複雜的數據結構。第 5 章展示了 FastAPI 如何使用 Pydantic 和數據模型來簡潔地實現這些功能。
3.7小結
在本章中,我們使用 FastAPI 創建了一個只有一個端點的網站。多個網路客戶端對其進行了測試:網路瀏覽器、HTTPie 文本程式、Requests Python 包和 HTTPX Python 包。從簡單的 GET 調用開始,請求參數通過 URL 路徑、查詢參數和 HTTP 標頭進入伺服器。然後,HTTP 主體被用來向 POST 端點發送數據。隨後,本章展示瞭如何返回各種 HTTP 響應類型。最後,自動生成的表單頁面為第四個測試客戶端提供了文檔和實時表單。