基于 Node.js 的声明式可监控爬虫网络从属于笔者的,记述了笔者重构我司简单爬虫过程中构建简单的爬虫框架的思想与实现,代码参考这里
基于 Node.js 的声明式可监控爬虫网络
爬虫是数据抓取的重要手段之一,而以 Scrapy、Crawler4j、Nutch 为代表的开源框架能够帮我们快速构建分布式爬虫系统;就笔者浅见,我们在开发大规模爬虫系统时可能会面临以下挑战:
网页抓取:最简单的抓取就是使用 HTTPClient 或者 fetch 或者 request 这样的 HTTP 客户端。现在随着单页应用这样富客户端应用的流行,我们可以使用 Selenium、PhantomJS 这样的 Headless Brwoser 来动态执行脚本进行渲染。
网页解析:对于网页内容的抽取与解析是个很麻烦的问题,DOM4j、Cherrio、beautifulsoup 这些为我们提供了基本的解析功能。笔者也尝试过构建全配置型的爬虫,类似于 Web-Scraper,然而还是输给了复杂多变,多层嵌套的 iFrame 页面。这里笔者秉持代码即配置的理念,对于使用配置来声明的内建复杂度比较低,但是对于那些业务复杂度较高的网页,整体复杂度会以几何倍数增长。而使用代码来声明其内建复杂度与门槛相对较高,但是能较好地处理业务复杂度较高的网页。笔者在构思未来的交互式爬虫生成界面时,也是希望借鉴 FaaS 的思路,直接使用代码声明整个解析流程,而不是使用配置。
反爬虫对抗:类似于淘宝这样的主流网站基本上都有反爬虫机制,它们会对于请求频次、请求地址、请求行为与目标的连贯性等多个维度进行分析,从而判断请求者是爬虫还是真实用户。我们常见的方式就是使用多 IP 或者多代理来避免同一源的频繁请求,或者可以借鉴 GAN 或者增强学习的思路,让爬虫自动地针对目标网站的反爬虫策略进行自我升级与改造。另一个常见的反爬虫方式就是验证码,从最初的混淆图片到现在常见的拖动式验证码都是不小的障碍,我们可以使用图片中文字提取、模拟用户行为等方式来尝试绕过。
分布式调度:单机的吞吐量和性能总是有瓶颈的,而分布式爬虫与其他分布式系统一样,需要考虑分布式治理、数据一致性、任务调度等多个方面的问题。笔者个人的感觉是应该将爬虫的工作节点尽可能地无状态化,以 Redis 或者 Consul 这样的能保证高可用性的中心存储存放整个爬虫集群的状态。
在线有价值页面预判:Google 经典的 PageRank 能够基于网络中的连接信息判断某个 URL 的有价值程度,从而优先索引或者抓取有价值的页面。而像 Anthelion 这样的智能解析工具能够基于之前的页面提取内容的有价值程度来预判某个 URL 是否有抓取的必要。
页面内容提取与存储:对于网页中的结构化或者非结构化的内容实体提取是自然语言处理中的常见任务之一,而自动从海量数据中提取出有意义的内容也涉及到机器学习、大数据处理等多个领域的知识。我们可以使用 Hadoop MapReduce、Spark、Flink 等离线或者流式计算引擎来处理海量数据,使用词嵌入、主题模型、LSTM 等等机器学习技术来分析文本,可以使用 HBase、ElasticSearch 来存储或者对文本建立索引。
笔者本意并非想重新造个轮子,不过在改造我司某个简单的命令式爬虫的过程中发现,很多的调度与监控操作应该交由框架完成。Node.js 在开发大规模分布式应用程序的一致性(JavaScript 的不规范)与性能可能不如 Java 或者 Go。但是正如笔者在上文中提及,JavaScript 的优势在于能够通过同构代码同时运行在客户端与服务端,那么未来对于解析这一步完全可以在客户端调试完毕然后直接将代码运行在服务端,这对于构建灵活多变的解析可能有一定意义。
总而言之,我只是想有一个可扩展、能监控、简单易用的爬虫框架,所以我快速撸了一个 declarative-crawler,目前只是处于原型阶段,尚未发布到 npm 中;希望有兴趣的大大不吝赐教,特别是发现了有同类型的框架可以吱一声,我看看能不能拿来主义,多多学习。
设计思想与架构概览
当笔者几年前编写第一个爬虫时,整体思路是典型的命令式编程,即先抓取再解析,最后持久化存储,就如下述代码:
await fetchListAndContentThenIndex(
'jsgc',
section.name,
section.menuCode,
section.category
).then(() => {
}).catch(error => {
console.log(error);
});
不过就好像笔者在 2016-我的前端之路:工具化与工程化 与 2015-我的前端之路:数据流驱动的界面 中讨论的,命令式编程相较于声明式编程耦合度更高,可测试性与可控性更低;就好像从 jQuery 切换到 React、Angular、Vue.js 这样的框架,我们应该尽可能将业务之外的事情交由工具,交由框架去管理与解决,这样也会方便我们进行自定义地监控。总结而言,笔者的设计思想主要包含以下几点:
关注点分离,整个架构分为了爬虫调度 CrawlerScheduler、Crawler、Spider、dcEmitter、Store、KoaServer、MonitorUI 等几个部分,尽可能地分离职责。
声明式编程,每个蜘蛛的生命周期包含抓取、抽取、解析与持久化存储这几个部分;开发者应该独立地声明这几个部分,而完整的调用与调度应该由框架去完成。
分层独立可测试,以爬虫的生命周期为例,抽取与解析应当声明为纯函数,而抓取与持久化存储更多的是面向业务,可以进行 Mock 或者包含副作用进行测试。
整个爬虫网络架构如下所示,目前全部代码参考这里。
自定义蜘蛛与爬虫
我们以抓取某个在线列表与详情页为例,首先我们需要针对两个页面构建蜘蛛,注意,每个蜘蛛负责针对某个 URL 进行抓取与解析,用户应该首先编写列表爬虫,其需要声明 model 属性、复写 before_extract、parse 与 persist 方法,各个方法会被串行调用。另一个需要注意的是,我们爬虫可能会外部传入一些配置信息,统一的声明在了 extra 属性内,这样在持久化时也能用到。
type ExtraType = {
module?: string,
name?: string,
menuCode?: string,
category?: string
};
export default class UAListSpider extends Spider {
displayName = "通用公告列表蜘蛛";
extra: ExtraType = {};
model = {
$announcements: 'tr[height="25"]'
};
constructor(extra: ExtraType) {
super();
this.extra = extra;
}
before_extract(pageHTML: string) {
return pageHTML.replace(//gim, " ");
}
parse(pageElements: Object) {
let announcements = [];
let announcementsLength = pageElements.$announcements.length;
for (let i = 0; i < announcementsLength; i++) {
let $announcement = $(pageElements.$announcements[i]);
let $a = $announcement.find("a");
let title = $a.text();
let href = $a.attr("href");
let date = $announcement.find('td[align="right"]').text();
announcements.push({ title: title, date: date, href: href });
}
return announcements;
}
/**
* @function 对采集到的数据进行持久化更新
* @param pageObject
*/
async persist(announcements): Promise {
let flag = true;
// 这里每个 URL 对应一个公告数组
for (let announcement of announcements) {
try {
await insertOrUpdateAnnouncement({
...this.extra,
...announcement,
infoID: href2infoID(announcement.href)
});
} catch (err) {
flag = false;
}
}
return flag;
}
}
我们可以针对这个蜘蛛进行单独测试,这里使用 Jest。注意,这里为了方便描述没有对抽取、解析等进行单元测试,在大型项目中我们是建议要加上这些纯函数的测试用例。
var expect = require("chai").expect;
import UAListSpider from "../../src/universal_announcements/UAListSpider.js";
let uaListSpider: UAListSpider = new UAListSpider({
module: "jsgc",
name: "房建市政招标公告-服务类",
menuCode: "001001/001001001/00100100100",
category: "1"
}).setRequest(
"http://ggzy.njzwfw.gov.cn/njggzy/jsgc/001001/001001001/001001001001/?Paging=1",
{}
);
test("抓取公共列表", async () => {
let announcements = await uaListSpider.run(false);
expect(announcements, "返回数据为列表并且长度大于10").to.have.length.above(2);
});
test("抓取公共列表 并且进行持久化操作", async () => {
let announcements = await uaListSpider.run(true);
expect(announcements, "返回数据为列表并且长度大于10").to.have.length.above(2);
});
同理,我们可以定义对于详情页的蜘蛛:
export default class UAContentSpider extends Spider {
displayName = "通用公告内容蜘蛛";
model = {
// 标题
$title: "#tblInfo #tdTitle b",
// 时间
$time: "#tblInfo #tdTitle font",
// 内容
$content: "#tblInfo #TDContent"
};
parse(pageElements: Object) {
...
}
async persist(announcement: Object) {
...
}
}
在定义完蜘蛛之后,我们可以定义负责爬取整个系列任务的 Crawler,注意,Spider 仅负责爬取单个页面,而分页等操作是由 Crawler 进行:
/**
* @function 通用的爬虫
*/
export default class UACrawler extends Crawler {
displayName = "通用公告爬虫";
/**
* @构造函数
* @param config
* @param extra
*/
constructor(extra: ExtraType) {
super();
extra && (this.extra = extra);
}
initialize() {
// 构建所有的爬虫
let requests = [];
for (let i = startPage; i < endPage + 1; i++) {
requests.push(
buildRequest({
...this.extra,
page: i
})
);
}
this.setRequests(requests)
.setSpider(new UAListSpider(this.extra))
.transform(announcements => {
if (!Array.isArray(announcements)) {
throw new Error("爬虫连接失败!");
}
return announcements.map(announcement => ({
url: `http://ggzy.njzwfw.gov.cn/${announcement.href}`
}));
})
.setSpider(new UAContentSpider(this.extra));
}
}
一个 Crawler 最关键的就是 initialize 函数,需要在其中完成爬虫的初始化。首先我们需要构造所有的种子链接,这里既是多个列表页;然后通过 setSpider 方法加入对应的蜘蛛。不同蜘蛛之间通过自定义的 Transformer 函数来从上一个结果中抽取出所需要的链接传入到下一个蜘蛛中。至此我们爬虫网络的关键组件定义完毕。
本地运行
定义完 Crawler 之后,我们可以通过将爬虫注册到 CrawlerScheduler 来运行爬虫:
const crawlerScheduler: CrawlerScheduler = new CrawlerScheduler();
let uaCrawler = new UACrawler({
module: "jsgc",
name: "房建市政招标公告-服务类",
menuCode: "001001/001001001/00100100100",
category: "1"
});
crawlerScheduler.register(uaCrawler);
dcEmitter.on("StoreChange", () => {
console.log("-----------" + new Date() + "-----------");
console.log(store.crawlerStatisticsMap);
});
crawlerScheduler.run().then(() => {});
这里的 dcEmitter 是整个状态的中转站,如果选择使用本地运行,可以自己监听 dcEmitter 中的事件:
-----------Wed Apr 19 2017 22:12:54 GMT+0800 (CST)-----------
{ UACrawler:
CrawlerStatistics {
isRunning: true,
spiderStatisticsList: { UAListSpider: [Object], UAContentSpider: [Object] },
instance:
UACrawler {
name: 'UACrawler',
displayName: '通用公告爬虫',
spiders: [Object],
transforms: [Object],
requests: [Object],
isRunning: true,
extra: [Object] },
lastStartTime: 2017-04-19T14:12:51.373Z } }
服务端运行
我们也可以以服务的方式运行爬虫:
const crawlerScheduler: CrawlerScheduler = new CrawlerScheduler();
let uaCrawler = new UACrawler({
module: "jsgc",
name: "房建市政招标公告-服务类",
menuCode: "001001/001001001/00100100100",
category: "1"
});
crawlerScheduler.register(uaCrawler);
new CrawlerServer(crawlerScheduler).run().then(()=>{},(error)=>{console.log(error)});
此时会启动框架内置的 Koa 服务器,允许用户通过 RESTful 接口来控制爬虫网络与获取当前状态。
接口说明
关键字段
// 判断爬虫是否正在运行
isRunning: boolean = false;
// 爬虫最后一次激活时间
lastStartTime: Date;
// 爬虫最后一次运行结束时间
lastFinishTime: Date;
// 爬虫最后的异常信息
lastError: Error;
// 最后一次运行时间
lastActiveTime: Date;
// 平均总执行时间 / ms
executeDuration: number = 0;
// 爬虫次数统计
count: number = 0;
// 异常次数统计
errorCount: number = 0;
countByTime: { [number]: number } = {};
http://localhost:3001/ 获取当前爬虫运行状态
[
{
name: "UACrawler",
displayName: "通用公告爬虫",
isRunning: false,
}
]
[
{
name: "UACrawler",
displayName: "通用公告爬虫",
isRunning: true,
lastStartTime: "2017-04-19T06:41:55.407Z"
}
]
[
{
name: "UACrawler",
displayName: "通用公告爬虫",
isRunning: true,
lastStartTime: "2017-04-19T06:46:05.410Z",
lastError: {
spiderName: "UAListSpider",
message: "抓取超时",
url: "http://ggzy.njzwfw.gov.cn/njggzy/jsgc/001001/001001001/001001001001?Paging=1",
time: "2017-04-19T06:47:05.414Z"
}
}
]
http://localhost:3001/start 启动爬虫
{
message:"OK"
}
http://localhost:3001/status 返回当前系统状态
{
"cpu":0,
"memory":0.9945211410522461
}
http://localhost:3001/UACrawler 根据爬虫名查看爬虫运行状态
[
{
"name":"UAListSpider",
"displayName":"通用公告列表蜘蛛",
"count":6,
"countByTime":{
"0":0,
"1":0,
"2":0,
"3":0,
...
"58":0,
"59":0
},
"lastActiveTime":"2017-04-19T06:50:06.935Z",
"executeDuration":1207.4375,
"errorCount":0
},
{
"name":"UAContentSpider",
"displayName":"通用公告内容蜘蛛",
"count":120,
"countByTime":{
"0":0,
...
"59":0
},
"lastActiveTime":"2017-04-19T06:51:11.072Z",
"executeDuration":1000.1596102359835,
"errorCount":0
}
]
自定义监控界面
CrawlerServer 提供了 RESTful API 来返回当前爬虫的状态信息,我们可以利用 React 或者其他框架来快速搭建监控界面。
你可能感兴趣的:(crawler,node.js)
探秘CommonJS:Node.js模块化核心解析
CommonJS是JavaScript的模块化规范,主要应用于服务器端环境(尤其是Node.js),其核心目标是解决代码组织、依赖管理和作用域隔离问题。以下是其核心要点:一、核心特性同步加载模块通过require()同步加载并执行,后续代码需等待模块加载完成后执行,适用于I/O快速的服务器环境(如本地文件读取)。作用域隔离每个文件视为独立模块,模块内定义的变量、函数默认私有(不污染全局作用域),仅
Vue3中Axios的使用-附完整代码
【本人】
Vue 前端框架 vue.js
前言首先介绍一下什么是axiosAxios是一个基于promise网络请求库,作用于node.js和浏览器中。它是isomorphic的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生node.jshttp模块,而在客户端(浏览端)则使用XMLHttpRequests官方网站:Axios中文文档|Axios中文网目前官方最新版本1.8.4一、Axios优势1.简单易用Axio
Python网络爬虫技术深度解析:从入门到高级实战
Python爬虫项目
2025年爬虫实战项目 python 爬虫 开发语言 easyui scrapy
1.爬虫技术概述网络爬虫(WebCrawler)是一种自动化程序,通过模拟人类浏览行为从互联网上抓取、解析和存储数据。根据应用场景可分为:通用爬虫:如搜索引擎的蜘蛛程序聚焦爬虫:针对特定领域的数据采集增量式爬虫:只抓取更新内容深层网络爬虫:处理需要交互的动态内容2.2024年Python爬虫技术栈技术分类推荐工具适用场景基础请求库requests,httpx静态页面请求解析库BeautifulSo
探讨 Node.js 中微服务架构的实践,包括服务注册与发现、负载均衡、API Gateway 和消息队列的应用。
各位观众老爷们,大家好!今天咱们来聊聊Node.js在微服务架构里头的那些事儿。别害怕,虽然听起来高大上,其实没那么玄乎,咱们争取用大白话把这玩意儿给整明白。开场白:为啥要搞微服务?想象一下,你开了一家小饭馆,一开始生意不错,就只有一个厨房,一个厨师(也就是你的单体应用)。后来生意火爆了,顾客越来越多,厨师一个人忙不过来了,炒菜慢,上菜慢,顾客抱怨声不断。怎么办?这时候,你灵机一动,把厨房拆分成几
深入分析 Node.js 的 V8 引擎如何在内部处理 JavaScript 代码,包括内存管理和垃圾回收机制。
海派程序猿
node.js javascript 开发语言
各位观众老爷们,晚上好!今天咱们就来聊聊Node.js的大心脏——V8引擎,看看它到底是怎么把咱们写的JavaScript代码给“消化”掉的。别害怕,今天咱不搞那些生涩难懂的学院派理论,尽量用大白话,外加一些“栗子”,保证让你听得津津有味。V8引擎:JavaScript的超级翻译官首先,简单介绍一下V8。V8是Google开发的高性能JavaScript和WebAssembly引擎,用C++写的。
【前端】JavaScript 的事件循环 (Event Loop)
不懂可否
前端 前端 javascript 开发语言
JavaScript的事件循环(EventLoop)是其实现异步编程的核心机制,即使JS是单线程语言,它也能高效处理I/O、网络请求、计时器等非阻塞操作。以下是其工作原理的精要解析:核心概念单线程执行JS引擎(如V8)只能顺序执行一个任务。异步行为需要靠宿主环境(浏览器/Node.js)提供的事件循环调度。任务队列(TaskQueue)所有异步操作完成后对应的回调函数会进入队列等待执行。队列类型包
JavaScript中this的5大核心规则详解
代码的余温
javascript 开发语言 ecmascript
在JavaScript中,this是一个特殊关键字,其值取决于函数的调用方式而非定义位置。它的行为遵循一套明确的规则,以下是核心规则和示例:1.默认绑定(独立函数调用)当函数独立调用时(不作为方法、构造函数等),this指向全局对象:浏览器中:windowNode.js中:globalfunctionshow(){console.log(this);//浏览器:Window/Node.js:glo
前端node.js入门
小周不想卷
# 前端node.js入门 前端 node.js
(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)目录Node.js入门概览什么是Node.js?为什么选择Node.js?基础安装与环境配置安装Node.js第一个Node.js应用创建一个简单的HTTP服务器核心模块与API文件系统(fs)模块路径(path)模块异步编程回调函数PromisesAsync/AwaitNode.js框架与中间件Expres
Node.js 手册:详细介绍及使用指南
江一破
node.js javascript 后端
Node.js手册:详细介绍及使用指南1.Node.js的本质与设计演进Node.js是一个基于ChromeV8JavaScript引擎构建的异步事件驱动型JavaScript运行时(AsynchronousEvent-DrivenJavaScriptRuntime),其核心是利用非阻塞I/O和事件驱动模型解决高并发场景的性能瓶颈。历史背景:Web服务器的"并发之殇"线程模型的代价:2000年代主
揭秘Electron多进程架构:性能与安全的完美平衡
止观止
# Electron 技术深入解析 electron 架构 安全
引言在跨平台桌面应用开发领域,Electron凭借其独特的多进程架构和强大的功能集成能力,已成为最流行的解决方案之一。本文将深入剖析Electron的多进程架构设计,揭示其如何结合Chromium的渲染能力与Node.js的系统级访问能力。通过阅读本文,您将掌握:Electron多进程模型的实现原理与核心组件主进程与渲染进程的职责划分与协作机制进程间通信(IPC)的多种模式与安全实践安全加固方案与
Github Actions CI/CD
alfalfaw
GithubActions是Github提供的一套CI/CD解决方案,允许开发者创建能自动构建、测试、发布和部署代码的工作流程。本文主要介绍如何使用GithubActions持续集成前端应用(演示项目代码地址在会在文末给出)。配置workflowActions->选择新建Node.js的工作流BW35XF.png.github/workflows/node.js.yml#Thisworkfloww
Node.js 中的内置模板path
Maybyy
node.js
1.path的作用:path是Node.js中的一个内置模块,用于处理文件和目录路径。它提供了一些工具来处理路径字符串,确保路径操作跨平台兼容(Windows和Unix风格的路径分隔符)2.path的常用方法path.join()和数组的join方法相似,它也可以起到拼接作用,它用于将多个路径片段连接起来,并正确地处理路径分隔符。即使有错误的分隔符,它也可以自动修正constpath=requir
PM2使用
使用进程管理器PM2PM2是一个为Node.js应用设计的、带有负载均衡功能的生产环境进程管理器。用它来管理npx执行的命令是最佳实践。优点:✅进程守护:程序崩溃后会自动重启。✅开机自启:可以配置,让服务器重启后自动运行你的服务。✅日志管理:自动分割和管理日志,方便查看。✅性能监控:可以监控CPU和内存占用。✅跨平台:在Linux,macOS和Windows上都能用。操作步骤:1.全局安装PM2如
Ubuntu 22.04 安装 NodeJS
吴晓布
ubuntu vim linux 运维 服务器
在Ubuntu上安装Node.js可以通过多种方法来实现,以下是三种常用的方法:使用Ubuntu存储库、通过NodeSourcePPA、使用nvm(NodeVersionManager)。方法1:使用Ubuntu存储库这种方法适合不需要最新版本Node.js的用户。更新包列表:登录后复制sudoaptupdate1.安装Node.js和npm(Node.js的包管理器):登录后复制sudoapti
Express 前端起一个后端服务 实现低代码生成自定义模板页面
CDwenhuohuo
express
Express是一个基于Node.js的轻量级Web服务框架让你快速启动一个本地HTTP服务(比如POST/GET接口),响应浏览器或其他前端发来的请求。①下载wxpressnpminstallexpress下不了就-D-W根目录下就是src同目录,创建一个js或者cjs的node脚本文件crud-generator-server.cjs②执行脚本:nodecrud-generator-serve
图文手把手教你如何把Node.js接入企业微信群机器人,实现恐贪指标自动触发微信消息提醒功能!Node.js 企业微信 群机器人 消息提醒 Webhook axios 企业微信接口
代码简单说
2025开发必备(限时特惠) node.js 企业微信 机器人 群机器人消息通知 企业微信消息提醒接口 消息自动推送 企业微信群机器人接入
图文手把手教你如何把Node.js接入企业微信群机器人,实现恐贪指标自动触发微信消息提醒功能!Node.js企业微信群机器人消息提醒Webhookaxios企业微信接口标签:Node.js企业微信群机器人消息提醒Webhookaxios企业微信接口最近股市大涨,我想到一个需求:当恐贪指数更新的时候,需要第一时间收到提醒,方便在市场走极端的时候逃顶。想了几个方案,最终选择接入企业微信群聊机器人,效果
vue项目入门
太阳伞下的阿呆
前端 vue.js 前端 javascript
入门1.创建Vue项目首先,确保你已经安装了Node.js和VueCLI(如前面所提到的)。然后,你可以创建一个新的Vue项目:vuecreatevue-crud-project选择Vue3和默认配置(或者根据需要自定义配置)。2.推荐的Vue.js项目目录结构以下是一个常见的、适合中小型Vue.js项目的目录结构:vue-crud-project/├──public/#存放静态资源│├──ind
揭秘前端 Electron 的底层运行原理
AI架构全栈开发实战笔记
前端艺匠馆 前端大数据与AI人工智能 前端 electron javascript ai
揭秘前端Electron的底层运行原理关键词:Electron、Chromium、Node.js、进程模型、IPC、V8引擎、跨平台摘要:本文将深入剖析Electron框架的底层运行原理,从Chromium和Node.js的集成机制开始,详细讲解Electron的多进程架构、进程间通信(IPC)实现、以及如何利用V8引擎实现JavaScript的跨平台执行。通过生活化的比喻和清晰的架构图,帮助读者
npm开箱
花霏花
安装Node.jsNode.js®是一个基于ChromeV8引擎的JavaScript运行时npm在正式开始Node.js学习之前,我们先认识一下npm。npm是什么东东?npm其实是Node.js的包管理工具(packagemanager)。为啥我们需要一个包管理工具呢?因为我们在Node.js上开发时,会用到很多别人写的JavaScript代码。如果我们要使用别人写的某个包,每次都根据名称搜索
零依赖Python爬虫代码,已通过100%黑盒测试,保证复制即用:
黑科技Python
爬虫 python
#-*-coding:utf-8-*-"""终极零依赖爬虫解决方案验证结果:已通过32种异常场景测试最后更新:2025-7-26"""importurllib.requestimporturllib.errorimporthashlibimporttimeimportrandomclassUltraCrawler:"""
electron的webView通信和如何在Electron中使用webView打开网页并获取DOM元素
张续栋
electron electron javascript webview
electron的webView通信如何在Electron中使用webView打开网页并获取DOM元素Electron是一个基于Chromium和Node.js的开源框架,用于构建跨平台的桌面应用程序。其中,Electron的webView是用来嵌入网页的组件。在本文中,我们将介绍如何使用Electron的webView打开网页并获取DOM元素的值。步骤1:创建Electron应用首先,我们需要创
从零构建 Node20+pnpm+pm2 环境镜像:基于 Dockerfile 的两种方案及持久化配置指南
Linux运维技术栈
docker 运维 容器
前言:在Node.js项目部署中,环境一致性和服务自动恢复是运维的核心需求。无论是本地开发还是生产部署,使用Docker封装Node20、pnpm(高效包管理)和pm2(进程守护)环境,能避免“本地能跑、线上崩了”的问题。但实际构建中,常遇到“pnpm命令找不到”“pm2无法自动启动”等问题。本文将提供两种Dockerfile方案(在线拉取pnpm和本地文件导入pnpm),并解决“环境变量持久化”
最新免费使用Claude Code指南(Windows & macOS/Linux)
i建模
AI windows macos linux
免费使用ClaudeCode指南(Windows&macOS/Linux)原创|本文已参与「新人创作礼」活动,一起开启技术创作之路标签:AI编程Claude开发工具效率优化文章目录注册并获取API密钥安装Node.js环境安装ClaudeCodeCLI配置环境变量开始使用常见问题排查1.注册并获取API密钥步骤详解:访问注册页面打开浏览器→进入[https://www.aicodemirror.c
JavaScript 书写规范详解 —— 编写高质量、可维护的代码
玖程
JavaScript基础入门 JavaScript javascript 开发语言 ecmascript
在现代前端开发中,JavaScript不仅是构建交互式网页的核心语言,还广泛应用于后端(Node.js)、移动端(ReactNative)、桌面应用(Electron)等多个领域。随着项目规模的增长,良好的代码规范成为团队协作和长期维护的关键。本文将详细介绍JavaScript的常见书写规范,帮助你写出结构清晰、可读性强、易于维护的代码。一、命名规范类型命名规则示例变量名小驼峰(camelCase
Promise 和axios有什么联系
yiyibaba.
前端
Promise&axiosPromise是JavaScript中用于异步编程的一个对象,而axios是一个基于Promise的HTTP客户端,用于在浏览器和node.js中发送HTTP请求。Promise是JavaScript中用于异步编程的一个对象:Promise是JavaScript的一个内置对象,它代表了一个异步操作的最终完成(或失败)及其结果值。异步编程是一种编程范式,它允许程序在等待异步
掌握 Node.js,提升前端开发的竞争力
AI实战架构笔记
AI架构开发实战 node.js ai
掌握Node.js,提升前端开发的竞争力关键词:Node.js、前端开发、JavaScript、全栈开发、npm、后端服务、竞争力提升摘要:本文深入探讨Node.js如何成为现代前端开发者提升竞争力的关键工具。我们将从基础概念入手,逐步分析Node.js的核心优势,并通过实际案例展示如何利用Node.js构建全栈应用。文章还将提供学习路径、工具推荐和未来趋势分析,帮助前端开发者全面掌握这一重要技术
构建你的第一个 Node.js 微服务
1024译站
微服务是一个自包含的独立单元,跟其他的微服务共同组成一个大型应用。通过把应用拆分成小单元,每个单元都能独立部署和扩展,也能由不同的团队用不同的编程语言开发,还能独立测试。micro是一个很小的(大约100行代码)模块,它让我们用Node.js写微服务变得轻松有趣。它很容易使用,而且非常快。无论你之前是否用过Node.js,看完这篇文章你就能写自己的微服务了!上手准备上手操作仅需两个小步骤,首先需要
Node.js特训专栏-实战进阶:17.会话管理与安全存储
爱分享的程序员
Node.js node.js javascript 前端
欢迎来到Node.js实战专栏!在这里,每一行代码都是解锁高性能应用的钥匙,让我们一起开启Node.js的奇妙开发之旅!Node.js特训专栏主页专栏内容规划详情会话管理与安全存储:从原理到实战的Web安全实践在Web应用中,会话(Session)是维持用户状态的核心机制——从用户登录到退出的整个过程中,会话管理负责跟踪用户身份、权限及操作状态。然而,会话管理一旦出现漏洞,可能导致用户身份被盗用、
Mocha 使用
比特森林探险记
Js&NodeJs javascript
Mocha是一个功能丰富、灵活的JavaScript测试框架,主要用于在Node.js和浏览器环境中运行测试用例。它以简单性、异步支持强大和可扩展性著称,常与断言库(如Chai、Node.js内置的assert)结合使用。以下是Mocha的核心用法和关键概念详解:1.安装使用npm或yarn安装Mocha:npminstall--save-devmocha#或yarnadd--devmocha2.
UE制作的 AI 交互数字人嵌入到 Vue 开发的信息系统中的方法和步骤
人工智能训练师
AI人工智能 人工智能 交互 vue.js
要将UE(UnrealEngine,虚幻引擎)制作的AI交互数字人嵌入到Vue开发的信息系统首页中运行,可以参考以下方法步骤以及涉及的软件工具:准备工作软件工具UnrealEngine:用于创建和编辑AI交互数字人,需要在UE中完成数字人的建模、绑定骨骼、添加AI交互逻辑等工作。VisualStudioCode:一款常用的代码编辑器,用于编写和修改Vue项目代码以及后续集成相关代码。Node.js
C/C++Win32编程基础详解视频下载
择善Zach
编程 C++ Win32
课题视频:C/C++Win32编程基础详解
视频知识:win32窗口的创建
windows事件机制
主讲:择善Uncle老师
学习交流群:386620625
验证码:625
--
Guava Cache使用笔记
bylijinnan
java guava cache
1.Guava Cache的get/getIfPresent方法当参数为null时会抛空指针异常
我刚开始使用时还以为Guava Cache跟HashMap一样,get(null)返回null。
实际上Guava整体设计思想就是拒绝null的,很多地方都会执行com.google.common.base.Preconditions.checkNotNull的检查。
2.Guava
解决ora-01652无法通过128(在temp表空间中)
0624chenhong
oracle
解决ora-01652无法通过128(在temp表空间中)扩展temp段的过程
一个sql语句后,大约花了10分钟,好不容易有一个结果,但是报了一个ora-01652错误,查阅了oracle的错误代码说明:意思是指temp表空间无法自动扩展temp段。这种问题一般有两种原因:一是临时表空间空间太小,二是不能自动扩展。
分析过程:
既然是temp表空间有问题,那当
Struct在jsp标签
不懂事的小屁孩
struct
非UI标签介绍:
控制类标签:
1:程序流程控制标签 if elseif else
<s:if test="isUsed">
<span class="label label-success">True</span>
</
按对象属性排序
换个号韩国红果果
JavaScript 对象排序
利用JavaScript进行对象排序,根据用户的年龄排序展示
<script>
var bob={
name;bob,
age:30
}
var peter={
name;peter,
age:30
}
var amy={
name;amy,
age:24
}
var mike={
name;mike,
age:29
}
var john={
大数据分析让个性化的客户体验不再遥远
蓝儿唯美
数据分析
顾客通过多种渠道制造大量数据,企业则热衷于利用这些信息来实现更为个性化的体验。
分析公司Gartner表示,高级分析会成为客户服务的关键,但是大数据分析的采用目前仅局限于不到一成的企业。 挑战在于企业还在努力适应结构化数据,疲于根据自身的客户关系管理(CRM)系统部署有效的分析框架,以及集成不同的内外部信息源。
然而,面对顾客通过数字技术参与而产生的快速变化的信息,企业需要及时作出反应。要想实
java笔记4
a-john
java
操作符
1,使用java操作符
操作符接受一个或多个参数,并生成一个新值。参数的形式与普通的方法调用不用,但是效果是相同的。加号和一元的正号(+)、减号和一元的负号(-)、乘号(*)、除号(/)以及赋值号(=)的用法与其他编程语言类似。
操作符作用于操作数,生成一个新值。另外,有些操作符可能会改变操作数自身的
从裸机编程到嵌入式Linux编程思想的转变------分而治之:驱动和应用程序
aijuans
嵌入式学习
笔者学习嵌入式Linux也有一段时间了,很奇怪的是很多书讲驱动编程方面的知识,也有很多书将ARM9方面的知识,但是从以前51形式的(对寄存器直接操作,初始化芯片的功能模块)编程方法,和思维模式,变换为基于Linux操作系统编程,讲这个思想转变的书几乎没有,让初学者走了很多弯路,撞了很多难墙。
笔者因此写上自己的学习心得,希望能给和我一样转变
在springmvc中解决FastJson循环引用的问题
asialee
循环引用 fastjson
我们先来看一个例子:
package com.elong.bms;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import co
ArrayAdapter和SimpleAdapter技术总结
百合不是茶
android SimpleAdapter ArrayAdapter 高级组件基础
ArrayAdapter比较简单,但它只能用于显示文字。而SimpleAdapter则有很强的扩展性,可以自定义出各种效果
ArrayAdapter;的数据可以是数组或者是队列
// 获得下拉框对象
AutoCompleteTextView textview = (AutoCompleteTextView) this
九封信
bijian1013
人生 励志
有时候,莫名的心情不好,不想和任何人说话,只想一个人静静的发呆。有时候,想一个人躲起来脆弱,不愿别人看到自己的伤口。有时候,走过熟悉的街角,看到熟悉的背影,突然想起一个人的脸。有时候,发现自己一夜之间就长大了。 2014,写给人
Linux下安装MySQL Web 管理工具phpMyAdmin
sunjing
PHP Install phpMyAdmin
PHP http://php.net/
phpMyAdmin http://www.phpmyadmin.net
Error compiling PHP on CentOS x64
一、安装Apache
请参阅http://billben.iteye.com/admin/blogs/1985244
二、安装依赖包
sudo yum install gd
分布式系统理论
bit1129
分布式
FLP
One famous theory in distributed computing, known as FLP after the authors Fischer, Lynch, and Patterson, proved that in a distributed system with asynchronous communication and process crashes,
ssh2整合(spring+struts2+hibernate)-附源码
白糖_
eclipse spring Hibernate mysql 项目管理
最近抽空又整理了一套ssh2框架,主要使用的技术如下:
spring做容器,管理了三层(dao,service,actioin)的对象
struts2实现与页面交互(MVC),自己做了一个异常拦截器,能拦截Action层抛出的异常
hibernate与数据库交互
BoneCp数据库连接池,据说比其它数据库连接池快20倍,仅仅是据说
MySql数据库
项目用eclipse
treetable bug记录
braveCS
table
// 插入子节点删除再插入时不能正常显示。修改:
//不知改后有没有错,先做个备忘
Tree.prototype.removeNode = function(node) {
// Recursively remove all descendants of +node+
this.unloadBranch(node);
// Remove
编程之美-电话号码对应英语单词
bylijinnan
java 算法 编程之美
import java.util.Arrays;
public class NumberToWord {
/**
* 编程之美 电话号码对应英语单词
* 题目:
* 手机上的拨号盘,每个数字都对应一些字母,比如2对应ABC,3对应DEF.........,8对应TUV,9对应WXYZ,
* 要求对一段数字,输出其代表的所有可能的字母组合
jquery ajax读书笔记
chengxuyuancsdn
jQuery ajax
1、jsp页面
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()
JWFD工作流拓扑结构解析伪码描述算法
comsci
数据结构 算法 工作 活动 J#
对工作流拓扑结构解析感兴趣的朋友可以下载附件,或者下载JWFD的全部代码进行分析
/* 流程图拓扑结构解析伪码描述算法
public java.util.ArrayList DFS(String graphid, String stepid, int j)
oracle I/O 从属进程
daizj
oracle
I/O 从属进程
I/O从属进程用于为不支持异步I/O的系统或设备模拟异步I/O.例如,磁带设备(相当慢)就不支持异步I/O.通过使用I/O 从属进程,可以让磁带机模仿通常只为磁盘驱动器提供的功能。就好像支持真正的异步I/O 一样,写设备的进程(调用者)会收集大量数据,并交由写入器写出。数据成功地写出时,写入器(此时写入器是I/O 从属进程,而不是操作系统)会通知原来的调用者,调用者则会
高级排序:希尔排序
dieslrae
希尔排序
public void shellSort(int[] array){
int limit = 1;
int temp;
int index;
while(limit <= array.length/3){
limit = limit * 3 + 1;
初二下学期难记忆单词
dcj3sjt126com
english word
kitchen 厨房
cupboard 厨柜
salt 盐
sugar 糖
oil 油
fork 叉;餐叉
spoon 匙;调羹
chopsticks 筷子
cabbage 卷心菜;洋白菜
soup 汤
Italian 意大利的
Indian 印度的
workplace 工作场所
even 甚至;更
Italy 意大利
laugh 笑
m
Go语言使用MySQL数据库进行增删改查
dcj3sjt126com
mysql
目前Internet上流行的网站构架方式是LAMP,其中的M即MySQL, 作为数据库,MySQL以免费、开源、使用方便为优势成为了很多Web开发的后端数据库存储引擎。MySQL驱动Go中支持MySQL的驱动目前比较多,有如下几种,有些是支持database/sql标准,而有些是采用了自己的实现接口,常用的有如下几种:
http://code.google.c...o-mysql-dri
git命令
shuizhaosi888
git
---------------设置全局用户名:
git config --global user.name "HanShuliang" //设置用户名
git config --global user.email "
[email protected] " //设置邮箱
---------------查看环境配置
git config --li
qemu-kvm 网络 nat模式 (四)
haoningabc
kvm qemu
qemu-ifup-NAT
#!/bin/bash
BRIDGE=virbr0
NETWORK=192.168.122.0
GATEWAY=192.168.122.1
NETMASK=255.255.255.0
DHCPRANGE=192.168.122.2,192.168.122.254
TFTPROOT=
BOOTP=
function check_bridge()
不要让未来的你,讨厌现在的自己
jingjing0907
生活 奋斗 工作 梦想
故事one
23岁,他大学毕业,放弃了父母安排的稳定工作,独闯京城,在家小公司混个小职位,工作还算顺手,月薪三千,混了混,混走了一年的光阴。 24岁,有了女朋友,从二环12人的集体宿舍搬到香山民居,一间平房,二人世界,爱爱爱。偶然约三朋四友,打扑克搓麻将,日子快乐似神仙; 25岁,出了几次差,调了两次岗,薪水涨了不过百,生猛狂飙的物价让现实血淋淋,无力为心爱银儿购件大牌
枚举类型详解
一路欢笑一路走
enum 枚举详解 enumset enumMap
枚举类型详解
一.Enum详解
1.1枚举类型的介绍
JDK1.5加入了一个全新的类型的”类”—枚举类型,为此JDK1.5引入了一个新的关键字enum,我们可以这样定义一个枚举类型。
Demo:一个最简单的枚举类
public enum ColorType {
RED
第11章 动画效果(上)
onestopweb
动画
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/
Eclipse中jsp、js文件编辑时,卡死现象解决汇总
ljf_home
eclipse jsp卡死 js卡死
使用Eclipse编辑jsp、js文件时,经常出现卡死现象,在网上百度了N次,经过N次优化调整后,卡死现象逐步好转,具体那个方法起到作用,不太好讲。将所有用过的方法罗列如下:
1、取消验证
windows–>perferences–>validation
把 除了manual 下面的全部点掉,build下只留 classpath dependency Valida
MySQL编程中的6个重要的实用技巧
tomcat_oracle
mysql
每一行命令都是用分号(;)作为结束
对于MySQL,第一件你必须牢记的是它的每一行命令都是用分号(;)作为结束的,但当一行MySQL被插入在PHP代码中时,最好把后面的分号省略掉,例如:
mysql_query("INSERT INTO tablename(first_name,last_name)VALUES('$first_name',$last_name')");
zoj 3820 Building Fire Stations(二分+bfs)
阿尔萨斯
Build
题目链接:zoj 3820 Building Fire Stations
题目大意:给定一棵树,选取两个建立加油站,问说所有点距离加油站距离的最大值的最小值是多少,并且任意输出一种建立加油站的方式。
解题思路:二分距离判断,判断函数的复杂度是o(n),这样的复杂度应该是o(nlogn),即使常数系数偏大,但是居然跑了4.5s,也是醉了。 判断函数里面做了3次bfs,但是每次bfs节点最多