網上找的一些示意圖

    Scriptable 使用 Apple 的JavaScriptCore,它默認就支持使用ECMAScript 6標準進行開發構建。這是一款可讓您使用 JavaScript 自動化構建 iOS 的應用程序(對于前端開發人員太友好了,直接上手操作)。這應用特別適合有一定動手能力的同學,可以利用現有很多API接口,自行構建各類應用。至于不想動手的同學,現在網上也有有很多免費提供使用的腳本可以直接拿來即用。不想動手的同學回頭可以在我的訂閱號上發送“Scriptable”,我將推送相關的網站和公眾號,大家可以直接拿來就用。個人認為應用的原理不復雜,Android上也是可以實現(我覺得Android應該是有現成類似的應用),回頭有空爭取做一個Android版。

具體特性如下:

這是官方的API文檔說明——Scriptable Docs – Scriptable Docs

基于上面的特性,可以看得出我們可以基礎這些API組合,來實現很多有趣的應用。

                                 這是我自己寫的兩個組件

接下來,我來給大家講解如何動手編寫自己的第一個桌面組件——百度熱搜。    

下面請大家先去AppStore下載「Scriptable」這個應用 

下載完后,打開Scriptable,進入主界面。

1、創建腳本,完成程序主體構造工作。

    在主界面的右上角點擊加號,創建一個新的腳本,先寫入下面這段代碼(這也是官方RandomAPI的基礎腳本),完成程序主體構造。

// This script shows a random Scriptable API in a widget. The script is meant to be used with a widget configured on the Home Screen.
// You can run the script in the app to preview the widget or you can go to the Home Screen, add a new Scriptable widget and configure the widget to run this script.
// You can also try creating a shortcut that runs this script. Running the shortcut will show widget.
let data = await getData()//獲取數據
let widget = await createWidget()//創建界面
if (config.runsInWidget) {
// The script runs inside a widget, so we pass our instance of ListWidget to be shown inside the widget on the Home Screen.
Script.setWidget(widget)
} else {
// The script runs inside the app, so we preview the widget.
widget.presentMedium()
}
// Calling Script.complete() signals to Scriptable that the script have finished running.
// This can speed up the execution, in particular when running the script from Shortcuts or using Siri.
Script.complete()

async function createWidget() {}
async function getData() {}

Scriptable有幾個重要的對象,如Script、config、widget(界面元素)這三個對象是構成組件運行的必要元素。在應用運行時,Scriptable會創建Script是一個全局變量,類似于微信小程度的App對象,屬于整體應用入口,這個主要就是設置好Widget對象。

config對象,主要是用于提供組件運行的基礎上下文環境配置信息,例如是否在組件運行還是在App上運行,或者基于Siri指令運行?是以大組件、中組件還是小組件展示?

Widget對象則是增加組件UI元素,常用的是ListWidget列表組件。

2、完善熱搜數據爬取。

    在網上找到百度熱搜一個老的API接口,與百度熱搜的網頁上的數據有點不同,但是妨礙我們使用。具體接口連接如下

https://top.baidu.com/api/board?platform=wise&tab=realtime

這個API接口返回的是JSON格式的報文,因此,我們只要實時拿到這份JSON報文,解析到data->cards[0]->content的前5個數據即可。

// This script shows a random Scriptable API in a widget. The script is meant to be used with a widget configured on the Home Screen.
// You can run the script in the app to preview the widget or you can go to the Home Screen, add a new Scriptable widget and configure the widget to run this script.
// You can also try creating a shortcut that runs this script. Running the shortcut will show widget.
let data = await getData()//獲取數據
let widget = await createWidget(data.data.cards[0].content)//創建界面
if (config.runsInWidget) {
// The script runs inside a widget, so we pass our instance of ListWidget to be shown inside the widget on the Home Screen.
Script.setWidget(widget)
} else {
// The script runs inside the app, so we preview the widget.
widget.presentMedium()
}
// Calling Script.complete() signals to Scriptable that the script have finished running.
// This can speed up the execution, in particular when running the script from Shortcuts or using Siri.
Script.complete()

async function createWidget(data) {
let widget = new ListWidget()//先創建ListWidget對象
widget.setPadding(20,10,20,10)//設置好組件與其他應用在桌面上的邊距

return widget
}

async function getData() {
let url = "https://top.baidu.com/api/board?platform=wise&tab=realtime"
let req = new Request(url)
return await req.loadJSON()
}

3、填充數據,完成組件開發。

前面兩步我們已經拿到了百度熱搜返回的數據,接下來要做的就是把數據裝載到桌面組件上展示。

// This script shows a random Scriptable API in a widget. The script is meant to be used with a widget configured on the Home Screen.
// You can run the script in the app to preview the widget or you can go to the Home Screen, add a new Scriptable widget and configure the widget to run this script.
// You can also try creating a shortcut that runs this script. Running the shortcut will show widget.
let data = await getData()//獲取數據
let widget = await createWidget(data.data.cards[0].content)//創建界面
if (config.runsInWidget) {
// The script runs inside a widget, so we pass our instance of ListWidget to be shown inside the widget on the Home Screen.
Script.setWidget(widget)
} else {
// The script runs inside the app, so we preview the widget.
widget.presentMedium()
}
// Calling Script.complete() signals to Scriptable that the script have finished running.
// This can speed up the execution, in particular when running the script from Shortcuts or using Siri.
Script.complete()

async function createWidget(data){
let widget = new ListWidget()//先創建ListWidget對象
widget.refreshAfterDate = new Date(Date.now()+1000*60*5)//這個是指定多長時間后臺重新執行一遍腳本,當前是設置每5分鐘刷新一次,至于刷新的速率很大程度上取決于操作系統,如你的電量、內存、網絡環境等。
widget.setPadding(20,10,20,10)//設置好組件與其他應用在桌面上的邊距
//創建頭部展示區域
const header = widget.addStack()//WidgetStack組件相當于HTMl的div
header.size = new Size(0,24)
const headerIcon = header.addImage(await loadImg('https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=3779990328,1416553241&fm=179&app=35&f=PNG?w=108&h=108&s=E7951B62A4639D153293A4E90300401B'))
headerIcon.imageSize = new Size(22,22)
header.addSpacer(2)
const headerTitle = header.addText('百度熱搜')
headerTitle.textColor = new Color('#0080FF')
headerTitle.font = Font.boldSystemFont(18)
// 當組件設置為2X2時,僅能顯示Logo,直接返回頭部圖標就可以了
if(config.widgetFamily === 'small') {
return widget
}
header.addSpacer()//增加空隙
//創建身體展示區域
const content = widget.addStack()
content.layoutVertically()
content.addSpacer(5)
for(var i=0;i<5;i++){
let d = data[i]
const row = content.addStack()
row.size = new Size(0,22)
const rowSeqText = row.addText(String(i+1))
rowSeqText.font = Font.boldSystemFont(14)
rowSeqText.lineLimit = 1
row.addSpacer(10)
const rowText = row.addText(d.desc==''?d.word:d.desc)
rowText.font = Font.boldSystemFont(14)
rowText.lineLimit = 1
row.addSpacer()
const url = d.appUrl
row.url = url
}
widget.addSpacer()
// 創建底部展示區域,增加一個時間展示,方便我們知道當前的熱搜結果是什么時間點的結果
const footer = widget.addStack()
footer.size = new Size(0, 16)
footer.addSpacer()
const DF = new DateFormatter()
DF.dateFormat = 'yyyy-MM-dd HH:mm:ss'
const now = DF.string(new Date())
const footerText = footer.addText(now)
footerText.font = Font.regularSystemFont(14)
footerText.lineLimit = 1
return widget
}

async function getData() {
let url = "https://top.baidu.com/api/board?platform=wise&tab=realtime"
let req = new Request(url)
return await req.loadJSON()
}
async function loadImg (url) {//獲取網絡圖片或者小圖標
const req = new Request(url)
return await req.loadImage()
}

4、完成發布及應用。

    短短70行不到的代碼,完成一個組件的編寫工作。點擊右下腳的三角形,試運行一遍,看看是否正常。如果正常,則開始在桌面測試組件,選擇對應的腳本。完成組件的開發及應用工作。具體如下圖操作即可。

添加組件的方法是在桌面空白位長按屏幕,直到出現圖3,點擊+號,選擇Scriptable組件。

添加完桌面組件后,點擊新增加的組件,如圖3設置,選擇剛剛編寫的腳本(默認腳本名叫Untitled Script),至此完成,將實時顯示百度熱搜消息,并且將在約5分鐘左右自動刷新最新消息。

1、查看百度熱搜頁面結構

通過分析頁面結構,發現熱搜項均包含在class=”item-wrap_2oCLZ”的a標簽中,十分簡單的結構,直接通過document.querySelectorAll(“.item-wrap_2oCLZ”)取到這些熱搜項的數據,然后直接獲取outerText,取得相應的文本內容即可。

下面的代碼為上一篇的代碼。

// This script shows a random Scriptable API in a widget. The script is meant to be used with a widget configured on the Home Screen.
// You can run the script in the app to preview the widget or you can go to the Home Screen, add a new Scriptable widget and configure the widget to run this script.
// You can also try creating a shortcut that runs this script. Running the shortcut will show widget.
let data = await getData()//獲取數據
let widget = await createWidget(data.data.cards[0].content)//創建界面
if (config.runsInWidget) {
// The script runs inside a widget, so we pass our instance of ListWidget to be shown inside the widget on the Home Screen.
Script.setWidget(widget)
} else {
// The script runs inside the app, so we preview the widget.
widget.presentMedium()
}
// Calling Script.complete() signals to Scriptable that the script have finished running.
// This can speed up the execution, in particular when running the script from Shortcuts or using Siri.
Script.complete()

async function createWidget(data) {
let widget = new ListWidget()//先創建ListWidget對象
widget.refreshAfterDate = new Date(Date.now()+1000*60*5)//這個是指定多長時間后臺重新執行一遍腳本,當前是設置每5分鐘刷新一次,至于刷新的速率很大程度上取決于操作系統,如你的電量、內存、網絡環境等。
widget.setPadding(20,10,20,10)//設置好組件與其他應用在桌面上的邊距
//創建頭部展示區域
const header = widget.addStack();//WidgetStack組件相當于HTMl的div
header.size = new Size(0,24);
const headerIcon = header.addImage(await loadImg('https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=3779990328,1416553241&fm=179&app=35&f=PNG?w=108&h=108&s=E7951B62A4639D153293A4E90300401B'))
headerIcon.imageSize = new Size(22,22);
header.addSpacer(2);
const headerTitle = header.addText('百度熱搜');
headerTitle.textColor = new Color('#0080FF');
headerTitle.font = Font.boldSystemFont(18);

// 當組件設置為2X2時,僅能顯示Logo,直接返回頭部圖標就可以了
if(config.widgetFamily === 'small') {
return widget
}
header.addSpacer();//增加空隙

//創建身體展示區域
const content = widget.addStack();
content.layoutVertically();
content.addSpacer(5);

for(var i=0;i<5;i++){
let d = data[i];
const row = content.addStack();
row.size = new Size(0,22);

const rowSeqText = row.addText(String(i+1));
rowSeqText.font = Font.boldSystemFont(14)
rowSeqText.lineLimit = 1;
row.addSpacer(10);

const rowText = row.addText(d.desc==''?d.word:d.desc);
rowText.font = Font.boldSystemFont(14)
rowText.lineLimit = 1;

row.addSpacer();
const url = d.appUrl;
row.url = url;
}
widget.addSpacer();

// 創建底部展示區域,增加一個時間展示,方便我們知道當前的熱搜結果是什么時間點的結果
const footer = widget.addStack()
footer.size = new Size(0, 16)
footer.addSpacer()
const DF = new DateFormatter()
DF.dateFormat = 'yyyy-MM-dd HH:mm:ss'
const now = DF.string(new Date())
const footerText = footer.addText(now)
footerText.font = Font.regularSystemFont(14)
footerText.lineLimit = 1

return widget
}

async function getData() {
let url = "https://top.baidu.com/api/board?platform=wise&tab=realtime"
let req = new Request(url)
return await req.loadJSON()
}

async function loadImg (url) {//獲取網絡圖片或者小圖標
const req = new Request(url)
return await req.loadImage()
}

大部份代碼不需要改動,我們只是對數據源獲取模塊進行修改,調整一下對應的數據結構就可以了。

2、修改取數邏輯

由于Scriptable自帶WebView瀏覽器組件,是基于蘋果系統自帶的WKWebkit內核實現的,支持加載遠程頁面、本地文件加載,支持JavaScript腳本注入以及請求攔截(高級功能,可以方便的突破很多前端限制,例如自動化腳本、繞過防御、模擬人機交互等)

async function getData(url) {
let wv = new WebView()//自帶內置瀏覽器組件,默認不展示頁面
await wv.loadURL(url)//加載遠程頁面
//定義注入的JS
let js = `
function _amethod(){
var _result = [];
var _a = document.querySelectorAll(".item-wrap_2oCLZ");
for(var i=0;i<_a.length;i++){
_result.push({"title":_a[i].outerText,"href":_a[i].href});
}
return _result;
}
_amethod();
`
let datas = await wv.evaluateJavaScript(js)//執行注入并接收返回結果
return datas
}

3、修改對應的數據結構邏輯。

let widget = await createWidget(data)
。。。。

//數據結構變化,調整對應的組數邏輯k
for(var i=0;i<5;i++){
let d = data[i];
const row = content.addStack();
row.size = new Size(0,22);

var _rows = d.title.split('\n');//百度返回的文本結構問題,簡單點處理

const rowSeqText = row.addText(_rows[0]);
rowSeqText.font = Font.boldSystemFont(14)
rowSeqText.lineLimit = 1;
// rowSeqText.shadowRadius = 1;
// rowSeqText.shadowOffset = new Point(1,1);
row.addSpacer(10);

const rowText = row.addText(_rows[1]);
rowText.font = Font.boldSystemFont(14)
rowText.lineLimit = 1;
// rowText.shadowRadius = 2;
// rowText.shadowOffset = new Point(1,1);

row.addSpacer();
const url = d.href;
row.url = url;
}

完整的代碼如下:

let data = await getData('https://top.baidu.com/board?platform=pc&sa=pcindex_entry')
let widget = await createWidget(data)
if (config.runsInWidget) {
// The script runs inside a widget, so we pass our instance of ListWidget to be shown inside the widget on the Home Screen.
Script.setWidget(widget)
} else {
// The script runs inside the app, so we preview the widget.
widget.presentMedium()
}
// Calling Script.complete() signals to Scriptable that the script have finished running.
// This can speed up the execution, in particular when running the script from Shortcuts or using Siri.
Script.complete()

async function createWidget(data) {
let widget = new ListWidget();
widget.setPadding(20,10,20,10);

const header = widget.addStack();
header.size = new Size(0,24);
const headerIcon = header.addImage(await loadImg('https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=3779990328,1416553241&fm=179&app=35&f=PNG?w=108&h=108&s=E7951B62A4639D153293A4E90300401B'))
headerIcon.imageSize = new Size(22,22);
header.addSpacer(2);
const headerTitle = header.addText('百度熱搜');
headerTitle.textColor = new Color('#0080FF');
headerTitle.font = Font.boldSystemFont(18);
widget.refreshAfterDate = new Date(Date.now()+1000*5)
// S: 當組件設置為2X2時,僅能顯示Logo,直接Return
if(config.widgetFamily === 'small') {
return widget
}
header.addSpacer();

const content = widget.addStack();
content.layoutVertically();
content.addSpacer(5);

for(var i=0;i<5;i++){
let d = data[i];
const row = content.addStack();
row.size = new Size(0,22);

var _rows = d.title.split('\n');

const rowSeqText = row.addText(_rows[0]);
rowSeqText.font = Font.boldSystemFont(14)
rowSeqText.lineLimit = 1;
// rowSeqText.shadowRadius = 1;
// rowSeqText.shadowOffset = new Point(1,1);
row.addSpacer(10);

const rowText = row.addText(_rows[1]);
rowText.font = Font.boldSystemFont(14)
rowText.lineLimit = 1;
// rowText.shadowRadius = 2;
// rowText.shadowOffset = new Point(1,1);

row.addSpacer();
const url = d.href;
row.url = url;
}
widget.addSpacer();

// 底部容器
const footer = widget.addStack()
footer.size = new Size(0, 16)
footer.addSpacer()
const DF = new DateFormatter()
DF.dateFormat = 'yyyy-MM-dd HH:mm:ss'
const now = DF.string(new Date())
const footerText = footer.addText(now)
footerText.font = Font.regularSystemFont(14)
footerText.lineLimit = 1

return widget
}

async function getData(url) {
let wv = new WebView()
await wv.loadURL(url)
let js = `
function _amethod(){
var _result = [];
var _a = document.querySelectorAll(".item-wrap_2oCLZ");
for(var i=0;i<_a.length;i++){
_result.push({"title":_a[i].outerText,"href":_a[i].href});
}
return _result;
}
_amethod();
`
let datas = await wv.evaluateJavaScript(js)
return datas
}

async function alert(text){
// 創建一個彈窗組件
let alert = new Alert();
// 設置彈窗中顯示的content
alert.message = text;
// 向彈窗中加入一個按鈕-確定,索引為0
alert.addAction('確定');
// 向彈窗中加入一個按鈕-取消,所以為1
alert.addAction('取消');
// 獲取彈窗按鈕被觸發后拿到用戶點擊的具體某個按鈕索引,如果點擊確定,response === 0 否則 response === 1
let response = await alert.presentAlert();
}

async function loadImg (url) {
const req = new Request(url)
return await req.loadImage()
}

拷貝代碼到Scriptable,執行效果如下。

發現直接在推文上拷貝腳本是無法直接運行的,因為代碼塊會對空行增加一個空白字符,導致腳本無法運行。大家有需要的話,請在公眾號回復「百度熱搜」獲取可直接使用的腳本。

    Scriptable真是一個超級棒的應用,基于開放的API,真的可以簡單快速的做出很多有趣的應用出來。近期看著疫情比較緊張,于是動手做了一個疫情實時監控的組件,可以切換城市,實時更新。先上圖,下一篇再介紹如何實現。

本文章轉載微信公眾號@飆豬狂

上一篇:

Go:基于 MongoDB 構建 REST API — Fiber 版

下一篇:

代碼詳解:構建一個簡單的Keras+深度學習REST API
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費