介:
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小时内与您取得联系。