Skip to content

Commit

Permalink
Feat: XPath selector
Browse files Browse the repository at this point in the history
  • Loading branch information
MiaoMint committed Aug 24, 2023
1 parent 153f210 commit 85f2e69
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 117 deletions.
285 changes: 168 additions & 117 deletions lib/utils/extension_runtime.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:html/parser.dart';
import 'package:xpath_selector_html_parser/xpath_selector_html_parser.dart';
import 'dart:io';

import 'package:cookie_jar/cookie_jar.dart';
Expand Down Expand Up @@ -40,7 +40,7 @@ class ExtensionRuntime {
// 注册方法
// 日志
runtime.onMessage('log', (dynamic args) {
debugPrint(args[0]);
// debugPrint(args[0]);
ExtensionUtils.addLog(
extension,
ExtensionLogLevel.info,
Expand Down Expand Up @@ -110,13 +110,34 @@ class ExtensionRuntime {

switch (fun) {
case 'text':
return doc?.text;
return doc?.text ?? '';
case 'outerHTML':
return doc?.outerHtml;
return doc?.outerHtml ?? '';
case 'innerHTML':
return doc?.innerHtml;
return doc?.innerHtml ?? '';
default:
return doc?.outerHtml;
return doc?.outerHtml ?? '';
}
});

// xpath 选择器
runtime.onMessage('queryXPath', (args) {
final content = args[0];
final selector = args[1];
final fun = args[2];

final xpath = HtmlXPath.html(content);
final result = xpath.queryXPath(selector);

switch (fun) {
case 'attr':
return result.attr ?? '';
case 'attrs':
return jsonEncode(result.attrs);
case 'text':
return result.node?.text;
default:
return result.node?.text;
}
});

Expand Down Expand Up @@ -156,129 +177,159 @@ class ExtensionRuntime {

_initRunExtension(String extScript) async {
runtime.evaluate('''
class Element {
constructor(content, selector) {
this.content = content;
this.selector = selector || "";
}
class Element {
constructor(content, selector) {
this.content = content;
this.selector = selector || "";
}
async querySelector(selector) {
return new Element(await excute(), selector);
}
async querySelector(selector) {
return new Element(await this.excute(), selector);
}
async excute(fun) {
return await sendMessage(
"querySelector",
JSON.stringify([this.content, this.selector, fun])
);
}
async excute(fun) {
return await sendMessage(
"querySelector",
JSON.stringify([this.content, this.selector, fun])
);
}
async removeSelector(selector) {
this.content = await sendMessage(
"removeSelector",
JSON.stringify([await this.outerHTML, selector])
);
return this;
}
async removeSelector(selector) {
this.content = await sendMessage(
"removeSelector",
JSON.stringify([await this.outerHTML, selector])
);
return this;
}
async getAttributeText(attr) {
return await sendMessage(
"getAttributeText",
JSON.stringify([await this.outerHTML, this.selector, attr])
);
}
async getAttributeText(attr) {
return await sendMessage(
"getAttributeText",
JSON.stringify([await this.outerHTML, this.selector, attr])
);
}
get text() {
return this.excute("text");
}
get text() {
return this.excute("text");
}
get outerHTML() {
return this.excute("outerHTML");
}
get outerHTML() {
return this.excute("outerHTML");
}
get innerHTML() {
return this.excute("innerHTML");
}
}
// 重写 console.log
var window = (global = globalThis);
console.log = function (message) {
if (typeof message === "object") {
message = JSON.stringify(message);
}
sendMessage("log", JSON.stringify([message.toString()]));
};
class Extension {
package = "${extension.package}";
name = "${extension.name}";
// 在 load 中注册的 keys
settingKeys = [];
async request(url, options) {
options = options || {};
options.headers = options.headers || {};
const miruUrl = options.headers["Miru-Url"] || "${extension.webSite}";
options.method = options.method || "get";
const res = await sendMessage(
"request",
JSON.stringify([miruUrl + url, options])
);
try {
return JSON.parse(res);
} catch (e) {
return res;
get innerHTML() {
return this.excute("innerHTML");
}
}
querySelector(content, selector) {
return new Element(content, selector);
}
async querySelectorAll(content, selector) {
let elements = [];
JSON.parse(
await sendMessage("querySelectorAll", JSON.stringify([content, selector]))
).forEach((e) => {
elements.push(new Element(e));
});
return elements;
}
async getAttributeText(content, selector, attr) {
return await sendMessage(
"getAttributeText",
JSON.stringify([content, selector, attr])
);
}
popular(page) {
throw new Error("not implement popular");
}
latest(page) {
throw new Error("not implement latest");
}
search(kw, page, screening) {
throw new Error("not implement search");
}
detail(url) {
throw new Error("not implement detail");
}
watch(url) {
throw new Error("not implement watch");
}
checkUpdate(url) {
throw new Error("not implement checkUpdate");
}
async getSetting(key) {
return sendMessage("getSetting", JSON.stringify([key]));
class XPathNode {
constructor(content, selector) {
this.content = content;
this.selector = selector;
}
async excute(fun) {
return await sendMessage(
"queryXPath",
JSON.stringify([this.content, this.selector, fun])
);
}
get attr() {
return this.excute("attr");
}
get attrs() {
return this.excute("attrs");
}
get text() {
return this.excute("text");
}
}
async registerSetting(settings) {
console.log(JSON.stringify([settings]));
this.settingKeys.push(settings.key);
return sendMessage("registerSetting", JSON.stringify([settings]));
// 重写 console.log
var window = (global = globalThis);
console.log = function (message) {
if (typeof message === "object") {
message = JSON.stringify(message);
}
sendMessage("log", JSON.stringify([message.toString()]));
};
class Extension {
package = "${extension.package}";
name = "${extension.name}";
// 在 load 中注册的 keys
settingKeys = [];
async request(url, options) {
options = options || {};
options.headers = options.headers || {};
const miruUrl = options.headers["Miru-Url"] || "${extension.webSite}";
options.method = options.method || "get";
const res = await sendMessage(
"request",
JSON.stringify([miruUrl + url, options])
);
try {
return JSON.parse(res);
} catch (e) {
return res;
}
}
querySelector(content, selector) {
return new Element(content, selector);
}
queryXPath(content, selector) {
return new XPathNode(content, selector);
}
async querySelectorAll(content, selector) {
let elements = [];
JSON.parse(
await sendMessage("querySelectorAll", JSON.stringify([content, selector]))
).forEach((e) => {
elements.push(new Element(e, selector));
});
return elements;
}
async getAttributeText(content, selector, attr) {
return await sendMessage(
"getAttributeText",
JSON.stringify([content, selector, attr])
);
}
popular(page) {
throw new Error("not implement popular");
}
latest(page) {
throw new Error("not implement latest");
}
search(kw, page, screening) {
throw new Error("not implement search");
}
detail(url) {
throw new Error("not implement detail");
}
watch(url) {
throw new Error("not implement watch");
}
checkUpdate(url) {
throw new Error("not implement checkUpdate");
}
async getSetting(key) {
return sendMessage("getSetting", JSON.stringify([key]));
}
async registerSetting(settings) {
console.log(JSON.stringify([settings]));
this.settingKeys.push(settings.key);
return sendMessage("registerSetting", JSON.stringify([settings]));
}
async load() {}
}
async load() {}
}
async function stringify(callback) {
const data = await callback();
return typeof data === "object" ? JSON.stringify(data) : data;
}
async function stringify(callback) {
const data = await callback();
return typeof data === "object" ? JSON.stringify(data) : data;
}
''');

Expand Down
32 changes: 32 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.5"
expressions:
dependency: transitive
description:
name: expressions
sha256: "75ca4343f9f8a38087bea130cf51395d737d87c6947cc19cbb8fb2732cae1a27"
url: "https://pub.dev"
source: hosted
version: "0.2.5"
fake_async:
dependency: transitive
description:
Expand Down Expand Up @@ -949,6 +957,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.3"
quiver:
dependency: transitive
description:
name: quiver
sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47
url: "https://pub.dev"
source: hosted
version: "3.2.1"
recase:
dependency: transitive
description:
Expand Down Expand Up @@ -1482,6 +1498,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.3.6"
xpath_selector:
dependency: transitive
description:
name: xpath_selector
sha256: "6d8295565a34a6e2821a0721592e584c70421e52a0f54955e0c8e41963db7c90"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
xpath_selector_html_parser:
dependency: "direct main"
description:
name: xpath_selector_html_parser
sha256: ebc562a07832a4062a2bc1c238fd59a81d31d841f15460caeed412ce078af9e0
url: "https://pub.dev"
source: hosted
version: "3.0.1"
xxh3:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ dependencies:
desktop_webview_window: ^0.2.1
http_parser: ^4.0.2
html: ^0.15.4
xpath_selector_html_parser: ^3.0.1

dev_dependencies:
flutter_test:
Expand Down

0 comments on commit 85f2e69

Please sign in to comment.