介:
HTTP/1.1 協議規定的 HTTP 請求方法有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT 這幾種。其中 POST 一般用來向服務端提交數據,本文主要討論 POST 提交數據的幾種方式。
我們知道,HTTP 協議是以 ASCII 碼傳輸,建立在 TCP/IP 協議之上的應用層規范。規范把 HTTP 請求分為三個部分:狀態行、請求頭、消息主體。類似于下面這樣:
<method> <request-URL> <version><headers><entity-body>
協議規定 POST 提交的數據必須放在消息主體(entity-body)中,但協議并沒有規定數據必須使用什么編碼方式。實際上,開發者完全可以自己決定消息主體的格式,只要最后發送的 HTTP 請求滿足上面的格式就可以。
但是,數據發送出去,還要服務端解析成功才有意義。一般服務端語言如 php、python 等,以及它們的 framework,都內置了自動解析常見數據格式的功能。服務端通常是根據請求頭(headers)中的 Content-Type 字段來獲知請求中的消息主體是用何種方式編碼,再對主體進行解析。所以說到 POST 提交數據方案,包含了 Content-Type 和消息主體編碼方式兩部分。下面就正式開始介紹它們。
application/x-www-form-urlencoded
這應該是最常見的 POST 提交數據的方式了。瀏覽器的原生 <form> 表單,如果不設置 enctype 屬性,那么最終就會以 application/x-www-form-urlencoded 方式提交數據。請求類似于下面這樣(無關的請求頭在本文中都省略掉了):
POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3
首先,Content-Type 被指定為 application/x-www-form-urlencoded;其次,提交的數據按照 key1=val1&key2=val2 的方式進行編碼,key 和 val 都進行了 URL 轉碼。大部分服務端語言都對這種方式有很好的支持。例如 PHP 中,$_POST['title'] 可以獲取到 title 的值,$_POST['sub'] 可以得到 sub 數組。
很多時候,我們用 Ajax 提交數據時,也是使用這種方式。例如 JQuery 和 QWrap 的 Ajax,Content-Type 默認值都是「application/x-www-form-urlencoded;charset=utf-8」。
multipart/form-data
這又是一個常見的 POST 數據提交的方式。我們使用表單上傳文件時,必須讓 <form> 表單的 enctype 等于 multipart/form-data。直接來看一個請求示例:
POST http://www.example.com HTTP/1.1Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="text"title------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="file"; filename="chrome.png"Content-Type: image/pngPNG ... content of chrome.png ...------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
這個例子稍微復雜點。首先生成了一個 boundary 用于分割不同的字段,為了避免與正文內容重復,boundary 很長很復雜。然后 Content-Type 里指明了數據是以 multipart/form-data 來編碼,本次請求的 boundary 是什么內容。消息主體里按照字段個數又分為多個結構類似的部分,每部分都是以 --boundary 開始,緊接著是內容描述信息,然后是回車,最后是字段具體內容(文本或二進制)。如果傳輸的是文件,還要包含文件名和文件類型信息。消息主體最后以 --boundary-- 標示結束。關于 multipart/form-data 的詳細定義,請前往 rfc1867 查看。
這種方式一般用來上傳文件,各大服務端語言對它也有著良好的支持。
上面提到的這兩種 POST 數據的方式,都是瀏覽器原生支持的,而且現階段標準中原生 <form> 表單也只支持這兩種方式(通過 <form> 元素的 enctype 屬性指定,默認為 application/x-www-form-urlencoded。其實 enctype 還支持 text/plain,不過用得非常少)。
隨著越來越多的 Web 站點,尤其是 WebApp,全部使用 Ajax 進行數據交互之后,我們完全可以定義新的數據提交方式,給開發帶來更多便利。
application/json
application/json 這個 Content-Type 作為響應頭大家肯定不陌生。實際上,現在越來越多的人把它作為請求頭,用來告訴服務端消息主體是序列化后的 JSON 字符串。由于 JSON 規范的流行,除了低版本 IE 之外的各大瀏覽器都原生支持 JSON.stringify,服務端語言也都有處理 JSON 的函數,使用 JSON 不會遇上什么麻煩。
JSON 格式支持比鍵值對復雜得多的結構化數據,這一點也很有用。記得我幾年前做一個項目時,需要提交的數據層次非常深,我就是把數據 JSON 序列化之后來提交的。不過當時我是把 JSON 字符串作為 val,仍然放在鍵值對里,以 x-www-form-urlencoded 方式提交。
Google 的 AngularJS 中的 Ajax 功能,默認就是提交 JSON 字符串。例如下面這段代碼:
var data={'title':'test', 'sub' : [1,2,3]};$http.post(url, data).success(function(result) { ...});
最終發送的請求是:
POST http://www.example.com HTTP/1.1 Content-Type: application/json;charset=utf-8{"title":"test","sub":[1,2,3]}
這種方案,可以方便的提交復雜的結構化數據,特別適合 RESTful 的接口。各大抓包工具如 Chrome 自帶的開發者工具、Firebug、Fiddler,都會以樹形結構展示 JSON 數據,非常友好。但也有些服務端語言還沒有支持這種方式,例如 php 就無法通過 $_POST 對象從上面的請求中獲得內容。這時候,需要自己動手處理下:在請求頭中 Content-Type 為 application/json 時,從 php://input 里獲得原始輸入流,再 json_decode 成對象。一些 php 框架已經開始這么做了。
text/xml
我的博客之前提到過 XML-RPC(XML Remote Procedure Call)。它是一種使用 HTTP 作為傳輸協議,XML 作為編碼方式的遠程調用規范。典型的 XML-RPC 請求是這樣的:
HTML
POST http://www.example.com HTTP/1.1 Content-Type: text/xml<?xml version="1.0"?><methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params></methodCall>
XML-RPC 協議簡單、功能夠用,各種語言的實現都有。它的使用也很廣泛,如 WordPress 的 XML-RPC Api,搜索引擎的 ping 服務等等。JavaScript 中,也有現成的庫支持以這種方式進行數據交互,能很好的支持已有的 XML-RPC 服務。不過,我個人覺得 XML 結構還是過于臃腫,一般場景用 JSON 會更靈活方便。
1.安裝Python 環境
首先需要你的電腦安裝好了Python環境,并且安裝好了Python開發工具。
如果你還沒有安裝,可以參考以下文章:
如果僅用Python來處理數據、爬蟲、數據分析或者自動化腳本、機器學習等,建議使用Python基礎環境+jupyter即可,安裝使用參考Windows/Mac 安裝、使用Python環境+jupyter notebook
2.安裝selenium庫
pip install selenium
復制代碼
3.下載谷歌瀏覽器驅動chromedriver,下載地址:npm.taobao.org/mirrors/chr…
需要選擇對應的谷歌瀏覽器版本,(谷歌瀏覽器訪問:chrome://settings/help,即可查看版本)
下載好后,隨便發到一個路徑下即可(簡單點最好,記住路徑)。
我們想定位一個元素,可以通過 id、name、class、tag、鏈接上的全部文本、鏈接上的部分文本、XPath 或者 CSS 進行定位,在 Selenium Webdriver 中也提供了這 8 種方法方便我們定位元素。
1)通過 id 定位:我們可以使用 find_element_by_id() 函數。比如我們想定位 id=loginName 的元素,就可以使用browser.find_element_by_id(“loginName”)。
2)通過 name 定位:我們可以使用 find_element_by_name() 函數,比如我們想要對 name=key_word 的元素進行定位,就可以使用 browser.find_element_by_name(“key_word”)。
3)通過 class 定位:可以使用 find_element_by_class_name() 函數。
4)通過 tag 定位:使用 find_element_by_tag_name() 函數。
5)通過 link 上的完整文本定位:使用 find_element_by_link_text() 函數。
6)通過 link 上的部分文本定位:使用 find_element_by_partial_link_text() 函數。有時候超鏈接上的文本很長,我們通過查找部分文本內容就可以定位。
7)通過 XPath 定位:使用 find_element_by_xpath() 函數。使用 XPath 定位的通用性比較好,因為當 id、name、class 為多個,或者元素沒有這些屬性值的時候,XPath 定位可以幫我們完成任務。
8)通過 CSS 定位:使用 find_element_by_css_selector() 函數。CSS 定位也是常用的定位方法,相比于 XPath 來說更簡潔。
1)清空輸入框的內容:使用 clear() 函數;
2)在輸入框中輸入內容:使用 send_keys(content) 函數傳入要輸入的文本;
3)點擊按鈕:使用 click() 函數,如果元素是個按鈕或者鏈接的時候,可以點擊操作;
4)提交表單:使用 submit() 函數,元素對象為一個表單的時候,可以提交表單;
由于selenium打開的chrome是原始設置的,所以訪問微博首頁時一定會彈出來是否提示消息的彈窗,導致不能定位到輸入框。可使用如下方法關閉彈窗:
prefs={"profile.default_content_setting_values.notifications": 2}
復制代碼
點擊需要定位的元素,然后右鍵選擇檢查,可以調出谷歌開發者工具。
獲取xpath 路徑,點擊谷歌開發者工具左上角的小鍵頭(選擇元素),選擇自己要查看的地方的,開發者工具就會自動定位到對應元素的源碼位置,選中對應源碼,然后右鍵,選擇Copy-> Copy XPath即可獲取到xpath 路徑。
另外: 可以下載 XPath Helper插件,安裝后 在網頁上選取想要提取的元素, 點擊右鍵 選中 檢查 然后 開發者工具自動打開 你可以看到 HTML代碼 ,選中然后再次點擊右鍵,選中copy 里的 copy to xpath這樣就得到了xpath的值了。
實現思路: 其實和平時我們正常操作一樣,只不過這里,全程由selenium來實現,模擬點擊和輸入,所以整個過程為:打開登錄頁面->輸入賬號密碼->點擊登錄按鈕->在發微博框輸入發送內容->點擊發送按鈕->關閉瀏覽器(自選)。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time
'''
自動發布微博
content:發送內容
username:微博賬號
password:微博密碼
'''
def post_weibo(content, username, password):
# 加載谷歌瀏覽器驅動
path=r'C:/MyEnv/chromedriver.exe ' # 指定驅動存放目錄
ser=Service(path)
chrome_options=webdriver.ChromeOptions()
# 把允許提示這個彈窗關閉
prefs={"profile.default_content_setting_values.notifications": 2}
chrome_options.add_experimental_option("prefs", prefs)
driver=webdriver.Chrome(service=ser, options=chrome_options)
driver.maximize_window() # 設置頁面最大化,避免元素被隱藏
print('# get打開微博主頁')
url='http://weibo.com/login.php'
driver.get(url) # get打開微博主頁
time.sleep(5) # 頁面加載完全
print('找到用戶名 密碼輸入框')
input_account=driver.find_element_by_id('loginname') # 找到用戶名輸入框
input_psw=driver.find_element_by_css_selector('input[type="password"]') # 找到密碼輸入框
# 輸入用戶名和密碼
input_account.send_keys(username)
input_psw.send_keys(password)
print('# 找到登錄按鈕 //div[@node-type="normal_form"]//div[@class="info_list login_btn"]/a')
bt_logoin=driver.find_element_by_xpath('//div[@node-type="normal_form"]//div[@class="info_list login_btn"]/a') # 找到登錄按鈕
bt_logoin.click() # 點擊登錄
# 等待頁面加載完畢 #有的可能需要登錄保護,需掃碼確認下
time.sleep(40)
# 登錄后 默認到首頁,有微博發送框
print('# 找到文本輸入框 輸入內容 //*[@id="homeWrap"]/div[1]/div/div[1]/div/textarea')
weibo_content=driver.find_element_by_xpath('//*[@id="homeWrap"]/div[1]/div/div[1]/div/textarea')
weibo_content.send_keys(content)
print('# 點擊發送按鈕 //*[@id="homeWrap"]/div[1]/div/div[4]/div/button')
bt_push=driver.find_element_by_xpath('//*[@id="homeWrap"]/div[1]/div/div[4]/div/button')
bt_push.click() # 點擊發布
time.sleep(15)
driver.close() # 關閉瀏覽器
if __name__=='__main__':
username='微博用戶名'
password="微博密碼"
# 自動發微博
content='每天進步一點'
post_weibo(content, username, password)
復制代碼
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time
import requests
import json
復制代碼
這里主要利用了selenium的get_cookies函數獲取cookies。
# 獲取cookies 到本地
def get_cookies(driver):
driver.get('https://weibo.com/login.php')
time.sleep(20) # 留時間進行掃碼
Cookies=driver.get_cookies() # 獲取list的cookies
jsCookies=json.dumps(Cookies) # 轉換成字符串保存
with open('cookies.txt', 'w') as f:
f.write(jsCookies)
print('cookies已重新寫入!')
# 讀取本地的cookies
def read_cookies():
with open('cookies.txt', 'r', encoding='utf8') as f:
Cookies=json.loads(f.read())
cookies=[]
for cookie in Cookies:
cookie_dict={
'domain': '.weibo.com',
'name': cookie.get('name'),
'value': cookie.get('value'),
'expires': '',
'path': '/',
'httpOnly': False,
'HostOnly': False,
'Secure': False
}
cookies.append(cookie_dict)
return cookies
復制代碼
# 初始化瀏覽器 打開微博登錄頁面
def init_browser():
path=r'C:/MyEnv/chromedriver.exe ' # 指定驅動存放目錄
ser=Service(path)
chrome_options=webdriver.ChromeOptions()
# 把允許提示這個彈窗關閉
prefs={"profile.default_content_setting_values.notifications": 2}
chrome_options.add_experimental_option("prefs", prefs)
driver=webdriver.Chrome(service=ser, options=chrome_options)
driver.maximize_window()
driver.get('https://weibo.com/login.php')
return driver
# 讀取cookies 登錄微博
def login_weibo(driver):
cookies=read_cookies()
for cookie in cookies:
driver.add_cookie(cookie)
time.sleep(3)
driver.refresh() # 刷新網頁
# 發布微博
def post_weibo(content, driver):
time.sleep(5)
weibo_content=driver.find_element_by_xpath('//*[ @id="homeWrap"]/div[1]/div/div[1]/div/textarea')
weibo_content.send_keys(content)
bt_push=driver.find_element_by_xpath('//*[@id="homeWrap"]/div[1]/div/div[4]/div/button')
bt_push.click() # 點擊發布
time.sleep(5)
driver.close() # 關閉瀏覽器
if __name__=='__main__':
# cookie登錄微博
driver=init_browser()
login_weibo(driver)
# 自動發微博
content='今天的天氣真不錯~'
post_weibo(content, driver)
復制代碼
檢測方法:利用本地cookies向微博發送get請求,如果返回的頁面源碼中包含自己的微博昵稱,就說明cookies還有效,否則無效。
# 檢測cookies的有效性
def check_cookies():
# 讀取本地cookies
cookies=read_cookies()
s=requests.Session()
for cookie in cookies:
s.cookies.set(cookie['name'], cookie['value'])
response=s.get("https://weibo.com")
html_t=response.text
# 檢測頁面是否包含我的微博用戶名
if '老表max' in html_t:
return True
else:
return False
復制代碼
可以參考上一篇文章:如何用Python發送告警通知到釘釘?
包括如何設置守護進程,在上一篇文章中也有介紹。
from apscheduler.schedulers.blocking import BlockingSchedulera
'''
每天早上9:00 發送一條微博
'''
def every_day_nine():
# cookie登錄微博
driver=init_browser()
login_weibo(driver)
req=requests.get('https://hitokoto.open.beeapi.cn/random')
get_sentence=req.json()
content=f'【每日一言】{get_sentence["data"]} 來自:一言api'
# 自動發微博
post_weibo(content, driver)
# 選擇BlockingScheduler調度器
sched=BlockingScheduler(timezone='Asia/Shanghai')
# job_every_nine 每天早上9點運行一次 日常發送
sched.add_job(every_day_nine, 'cron', hour=9)
# 啟動定時任務
sched.start()
復制代碼
凡是自動化的東西,都可以發抖服務器上持續的去跑,當然,本地電腦也可以進行學習使用。
下一期,感覺有太多東西需要更新了,慢慢來吧,提前祝大家元旦快樂~2022,我準備好了!
前端開發的快速迭代中,Parcel以零配置的優勢,為迅速搭建原型提供了極大的便利。相對于Vite,Parcel在輕量級項目和快速原型開發中更顯手腳敏捷。Vite更適合需要細致構建優化的復雜場景。Parcel的易用性讓開發者能立刻投入創意實現,無需任何配置。
簡介
以迅速和便利的特性贏得開發者好評的前端打包工具Parcel,可以無需過多調整設置,自動地處理JavaScript、CSS和HTML等各類資源。這種簡易性和高效性,使Parcel在GitHub中獲得了43k star。
Parcel的特性
為什么需要Parcel?
目前已經有很多的打包工具了,包括webpack和browserify。那么為什么我們還需要另外一個呢?主要原因是因為開發者的經驗。
許多的打包工具都是圍繞著配置和插件構建的,而且為了讓應用正常的工作,超過500行的配置并不罕見。這些配置不僅繁瑣而且耗時。通常情況下,這可能導致次優化的應用發送到生產環境。parcel被設計成零配置的:只需要將它指向應用程序的入口點,它就能正常工作。
目前現存的打包工具都非常慢。擁有大量文件和依賴的大型應用可能需要花費幾分鐘的時間來構建,這在開發過程中隨著時間的變化而變得尤為痛苦。監聽文件變更能夠幫助重新構建,但初始的啟動仍然非常慢。parcel利用工作線程編譯你的代碼,利用現代的多核處理器能力。這導致了初始構建的速度大大提升。它還具有一個文件系統緩存,可以保存每一個文件的編譯結果,以便后續能夠更快的啟動。
最后,現有的打包工具都是圍繞字符串加載/轉換構建的,其中轉換需要一個字符串,解析它,進行一些轉換,然后再次生成代碼。通常這樣會導致許多的解析和代碼生成在單個文件上運行,這是非常低效的。相反,parcel的轉換工作在AST上,因此每個文件只有一個解析,多個轉換以及一個代碼生成。
Parcel如何工作的?
parcel將資源樹轉換為bundle樹。許多其它的打包工具基本上都是基于js資源,其它格式都是粘貼的-例如,默認情況下以字符串的形式內嵌到js中。parcel是文件類型無關的-它可以按照你期望的方式與任何類型的資源一起工作,無需配置。
parcel將一個入口點作為輸入,可以是任何類型的:JS文件,HTML,CSS,圖片等。在parcel中定義了各種資源類型,它們知道如何處理特定的資源類型。資源文件被解析,它的依賴關系被提取,并轉換成最終的編譯形式。這創建了一個資源樹。
一旦資源樹被構建,資源就被放入一個bundle樹中。為入口資源創建一個bundle,并為動態導入的資源創建子bundle,這回導致代碼拆分的發生。當導入不同類型的資源的時候就會創建子bundle,例如如果你在js中導入css文件,它就會打包成對應js的兄弟bundle。如果一個資源需要多個bundle,它會被打包到最近的共同祖先,因此它不會被包含多次。
開始使用Parcel
首先,我們需要安裝 Parcel。確保您已經安裝了 Node.js,然后通過以下命令安裝 Parcel:
接下來,讓我們創建一個簡單的 HTML 文件,命名為 index.html:
創建一個 src 目錄,并在其中添加一個名為 index.js 的 JavaScript 文件:
現在我們已經準備好運行 Parcel。在項目根目錄中,運行以下命令:
Parcel 會自動處理所有相關的資源,并在瀏覽器中打開一個服務器。當您對源文件進行更改時,Parcel 會自動重新構建項目并刷新瀏覽器。
配置Parcel
盡管 Parcel 的一個主要優勢是零配置,但有時您可能需要根據項目需求進行一些自定義。要在 Parcel 中配置項目,您可以創建一個名為 .parcelrc 的文件,并在其中指定您的配置。
例如,要更改輸出目錄,您可以在 .parcelrc 文件中添加以下內容:
這將使 Parcel 將構建的資源輸出到名為 custom-dist 的目錄中。
開源地址:點贊+轉發,關注,私信【Parcel】
*請認真填寫需求信息,我們會在24小時內與您取得聯系。