Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help


weight: 1 title: "登高"

平时看到的好的文章,学习的记录

学习笔记

笔记中主要记录个人的编程生涯的一些学习笔记;

rust note

字符串切片

#![allow(unused)]
fn main() {
let x = String::from("hello");

let y = &x[..2] // output: he  
let z = &x[..=2] // output: hel 包含x[2]
}
  • &x[..2] 这里是对字符串x的引用,必须要加&符号,否则会报错
  • 如果是获得所有权 则不能用切片

生命周期

语法: ' 单引号开头 :&'a

#![allow(unused)]

fn main() {
#[test]
fn live_test(){
    let x = String::from("hello");
    let y = "hi";
    let z = live(&x[..=2],y); //&[..=2]
    println!("{}",z);
}

fn live<'c>(x: &'c str,y: &'c str) -> &'c str{
    if x.len()>y.len(){
        x
    } else {
        y
    }
}
}

rust 引用类似java中的对象引用,如果是直接赋值某个变量,就意味着所有权转移

  • 引用|借用是指向被引用内存地址
  • 所有权转移,原变量失效不可再用
#![allow(unused)]
fn main() {
let x = String::from("hello");
let y = &x;
println!("x的内存地址:{:p}",y);
let z = &y;
println!("{:p}",*z);
println!("{:p}",&z);
println!("{:p}",&x);
}

所有权规则(Ownership Rules)

1.每个值都有一个所有者(Each value has one owner)

#![allow(unused)]
fn main() {
let s1 = String::from("hello"); // s1 是 "hello" 的所有者
let s2 = s1;                    // 所有权从 s1 移动到 s2
// println!("{}", s1);          // 错误!s1 已经不再有效
}

2. 同一时刻只能有一个所有者(Only one owner at a time)

#![allow(unused)]
fn main() {
let s1 = String::from("hello");
let s2 = s1;                    // s1 的所有权移动到 s2
// s1 在这里已经无效了
}

3. 当所有者离开作用域时,值会被丢弃(Value is dropped when owner goes out of scope)

#![allow(unused)]
fn main() {
{
    let s = String::from("hello"); // s 进入作用域
    // 使用 s
} // s 离开作用域,内存被自动释放
}

移动语义(Move Semantics) 基本类型(实现了 Copy trait)

#![allow(unused)]
fn main() {
let x = 5;
let y = x;        // x 被复制给 y,x 仍然有效
println!("{}", x); // 正常工作
}

复杂类型(未实现 Copy trait)

#![allow(unused)]
fn main() {
let s1 = String::from("hello");
let s2 = s1;        // s1 的所有权移动到 s2
// println!("{}", s1); // 编译错误!
}

借用规则(Borrowing Rules)

  1. 不可变借用规则
#![allow(unused)]
fn main() {
let mut s = String::from("hello");
let r1 = &s;    // 不可变借用
let r2 = &s;    // 另一个不可变借用
// let r3 = &mut s; // 错误!不能同时有可变和不可变借用
}
  1. 可变借用规则
#![allow(unused)]
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;    // 可变借用
// let r2 = &mut s; // 错误!不能同时有多个可变借用
// let r3 = &s;     // 错误!不能同时有可变和不可变借用
}
  1. 借用必须始终有效
fn main() {
    let r;                // 声明引用
    {
        let x = 5;        // x 进入作用域
        r = &x;           // r 引用 x
    } // x 离开作用域,r 变成了悬垂引用
    // println!("{}", r); // 错误!
}

智能指针解决方案 Rc - 引用计数

#![allow(unused)]
fn main() {
use std::rc::Rc;

let data = Rc::new(String::from("hello"));
let data_clone1 = Rc::clone(&data);    // 引用计数 +1
let data_clone2 = Rc::clone(&data);    // 引用计数 +1
// 当所有 Rc 都离开作用域时,数据被释放
}

RefCell - 运行时借用检查

#![allow(unused)]
fn main() {
use std::cell::RefCell;

let data = RefCell::new(5);
*data.borrow_mut() += 10;              // 可变借用
println!("{}", *data.borrow());        // 不可变借用
}

Rc - 组合使用

#![allow(unused)]
fn main() {
use std::rc::Rc;
use std::cell::RefCell;

let data = Rc::new(RefCell::new(5));
let data_clone = Rc::clone(&data);
*data_clone.borrow_mut() += 10;        // 修改共享数据
println!("{}", *data.borrow());        // 看到修改后的值
}

生命周期(Lifetimes)

基本语法

#![allow(unused)]
fn main() {
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
}

生命周期省略规则

#![allow(unused)]
fn main() {
// 编译器自动推断生命周期
fn first_word(s: &str) -> &str { ... }  // 等同于 fn first_word<'a>(s: &'a str) -> &'a str
}

实际应用示例 你的 CubeSat 项目中的使用

#![allow(unused)]
fn main() {
let base = Rc::new(RefCell::new(GroundStation { radio_freq: 87.65 }));
// Rc 允许多个所有者
// RefCell 允许运行时借用检查
// 结合使用可以安全地共享可变状态

let sat = base.borrow().connect(id);     // 不可变借用
base.borrow_mut().radio_freq += 1.0;    // 可变借用
}

Rust 的所有权系统通过以下方式保证内存安全:

  • 编译时检查 - 防止悬垂引用和数据竞争
  • 零成本抽象 - 运行时没有垃圾收集器开销
  • 明确的所有权语义 - 代码意图清晰,易于理解
  • 智能指针 - 在需要时提供额外的灵活性

以上转自:https://www.cnblogs.com/JohannaFeng/p/19050150

str和String区别:

通俗讲 String就像是笔记本,可以往里面写字符串;而str就像是一个标签,引用了笔记本里的一段内容;

  • String 是 Rust 标准库提供的可变、拥有所有权的字符串类型,底层是堆分配,可以动态增长和修改。
  • str 是不可变的字符串切片类型,通常以 &str 形式出现,指向某个字符串的一部分,不能直接修改内容。
  • String 可以通过 .as_str() 方法转换为 &str,而 &str 可以通过 .to_string() 或 String::from() 转换为 String。
#![allow(unused)]
fn main() {
let x = String::from("hi zouni");
let y:&str = &x[1..2];

//&str -> String
let x = "world";
let y:String = x.to_string();
}

trait 函数集合,类似java中的interface接口

#![allow(unused)]
fn main() {
struct User{
    name:String,
    age:i8
}

trait Hello{
    fn hello(&self) -> String;
    fn print(&self){
        print!("hello trait");
    }
}

impl Hello for User{
    fn hello(&self) -> String {
        format!("hello {}",self.name)
    }
}

#[test]
fn trait_test(){
    let u = User{name:String::from("zhangsan"),age:18};
    u.print();
}
}

模块 mod

mod 组织代码结构;

内部模块 外部调用模块内函数—> pub 修饰符

mod mod_test{
    pub fn hello(){
        println!("hi");
    }
    pub mod mod_inner{
        pub fn hello1(){
            println!("hi inner mod");
        }
    }
}

fn main(){
    //这里要调用模块 mod_test()
    crate::mod_test::hello();
}
  • main函数要调用模块mod_test中的函数hello() ,函数必须要有权限修饰符pub
  • 模块之间调用,被调用模块同一个包下可以随便调用,否则也要pub修饰符修饰。

crate::mod_test::hello();

注解

写了代码却不用,会被警告⚠️,加上#[allow(unused)]注解 警告消除。


bookCollapseSection: true weight: 2

CGO需要安装gcc
windows安装mingw-w64
下载
https://sourceforge.net/projects/mingw-w64/files/mingw-w64/
两种方式:

  1. 下载exe可执行文件,交给程序下载安装,网络不好很大概率会一直失败
  2. 直接下载压缩包解压,然后配置环境变量即可。(推荐)
mkdir /lib64
ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2

golang 配置环境

不通架构运行可能出现 不能运行的问题

-ash: ./alidns: cannot execute binary file: Exec format error

这种情况要根据当前运行环境决定修改 go env参数

GOARCH& GOOS

go env -w GOOS= linux 
# 这里GOARCH 分为  amd64  arm64  根据个人环境决定
go env -w GOARCH=amd64
go env -w GOARCh=arm64

Help->Edit Custom VM Options...在开打文件中添加参数-Dfile.encoding=UTF-8

socket.io 配置到外网 服务器后,就会出现如下问题

Error during WebSocket handshake: Unexpected response code: 400

,根据github的讨论,得到如下答案

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;

其中第一行是告诉nginx使用HTTP/1.1通信协议,这是websoket必须要使用的协议。 第二行和第三行告诉nginx,当它想要使用WebSocket时,响应http升级请求。

安装

1. 配置好环境变量:GOPATH GOROOT

GOROOT: GO安装目录
GOPATH: 项目目录 src pkg bin目录

2. 安装go 开发工具集

ctrl + shift + P 打开命令面板,选择 GO:Install/Update Tools

调试

go run test 不打印 详情

go test -v # 正常命令应该是这样的,vscode 默认运行 不带-v 修改工作空间设置

{
    "go.inferGopath": false,
    "go.testFlags": ["-v"],  //增加这一行
}

再运行就正常了。

golang内网环境配置

goroot: 就是golang的安装地址
gopath: 一般存放项目的目录 exp: d:/workproject/go

pkg: 第三方依赖
src: 源代码,旧的项目管理必须源码放这里,modules没这个要求了
bin: 编译好的二进制可执行文件

配置好gopath之后 GOPROXY=file:///D:/WorkProject/pkg/mod/cache/download;GOSUMDB=off

内网电脑必须关闭GOSUMDB,否则会联网校验包

go env -w GOSUMDB=off

都设置好之后,go get命令就会从本地获取配置的proxy仓库项目依赖包

函数计算需要设置时区

添加环境变量即可:

TZ = Asia/Shanghai

地址说明
gogshttps://gogs.io/docs/git代码管理,轻量,部署方便
memoshttps://github.com/usememos/memos备忘录
cobrahttps://github.com/spf13/cobra用于创建CLI命令行应用程序的工具
viperhttps://github.com/spf13/viper
bubbleteahttps://github.com/charmbracelet/bubbletea一个go来构建TUI应用程序的工具,类似工具还有tview,termui
tviewhttps://github.com/rivo/tview一个go来构建TUI应用程序的工具
termuihttps://github.com/gizak/termui一个go来构建TUI应用程序的工具
cronhttps://github.com/robfig/crongo定时器实现,支持cron表达式
pocketbase"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"后端框架,包含了嵌入式SQLITE数据库,支持RESTful API

安装环境,配置环境变量

GOROOT:go安装路径
GOPATH: 项目目录
PATH :%GOROOT%\bin;%GOPATH%\bin

GOland 配置环境

https://github.com/goproxyio/goproxy go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.io,direct

# 设置不走 proxy 的私有仓库,多个用逗号相隔(可选)
go env -w GOPRIVATE=*.corp.example.com

# 设置不走 proxy 的私有组织(可选)
go env -w GOPRIVATE=example.com/org_name

go moudle 初始化

go mod project_name
go build

1. main 放哪里?

除了library库以外,main.go放在项目根目录下; 既有库又有二进制,可以把main放进cmd目录,非必须;

建议:主程序(CLI / server)放在仓库根目录的 main(或直接 main.go)是最简单也最常用的。

优势之一是安装/运行最短路径。

便于 go install:

最短最干净的安装方式(root 有 main)

go install github.com/eddycjy/project@latest

示例(根目录有 main):

/project
  go.mod
  main.go      // 程序入口
  lib/         // 如果你还有对外库
  internal/    // 可选
  README.md

或者把可复用代码放到一个清晰命名的包,而不是把 main 挪得过远。

2. internal/ 是特性不是仪式

internal/ 的机制是 Go 工具链强制的:internal 下的代码只能被父目录(和子级树)里的包导入。

好处是可以阻止外部依赖,但不是每个项目都需要它。

这时候就涉及到什么时候用?

当你真的有很多对外不暴露但跨包复用的代码,并且项目会被大量第三方使用时才考虑 internal/。

对绝大多数小中型项目来说,不用 internal/ 更简单、更灵活。

示例(使用场景):

/project
internal/
    secrets/   // 只有 project 内部可以 import
pkg/         // 对外可用库(慎用 pkg_/)
main.go

3. 别盲目使用 pkg/

pkg/ 是历史遗留的惯例,在现代 Go 里没必要把所有对外包都塞进 pkg/。

包名与路径应以可读性与语义为核心。把包放到顶层(/auth、/db、/storage)通常更直观,且导入路径更短。

示例对比:

不推荐(多一层 pkg):

import "github.com/you/project/pkg/storage"
更推荐(语义清晰):

import "github.com/you/project/storage"

4. 不要乱建 util、common 等

“工具类” 包看起来方便,但会变成随手塞东西的垃圾仓库。把函数/类型放到语义化更强的包里,或放在最常用的使用位置邻近代码,而不是一个笼统的 util。

反面示例:

// util/strings.go
package util
func Reverse(s string) string { ... }
更好写法(语义化):

// text/reverse.go
package text
func Reverse(s string) string { ... }

// 或者直接放在使用它的包里,例如 handler/text_helpers.go

5. 包不要太多(也别千行一包)

Go 可以在一个包里有多个文件,这一点要善用。每新增一个包,就可能增加依赖、回环风险和迁移成本。相反,也不要把完全不相关的代码塞成一个冗长的包——保持“以用途/语义分包”。

经验规则:

如果一组代码有同一语义与同一生命周期,放到同一个包。 每个包最好能在 200–1000 行范围内(这不是硬性规则,只是可读性提醒)。 切包优先按“用途”而不是“文件大小”。

6. 文件别太细碎

许多人喜欢把每个小函数放不同文件,结果翻代码像翻书页。合理把相关函数聚合到同一文件,便于阅读。

避免把每个 tiny helper 分成独立文件。

7. 语义化命名优先于目录深度

库名、包名与目录名应体现用途。例如 applog 比 util/log 更有意义。

这样看代码的同学通过 import 一眼能看出大致的用途。

8. 版本管理和 semver 建议

建议尽量使用 0.x 阶段语义化版本(保守上 v0.x),在你要打破 API 时给出明确变更说明,而不是过早把版本固定为 v2/v3,导致用户为小改动分叉仓库。

换句话说:先发布、后演进,记录变更而不是封闭。

推荐的最小仓库模板(实战) 下面给出一个适合多数小中型项目的极简布局,能覆盖 CLI / library 混合场景:

/project
go.mod
main.go             // 如果是二进制,把入口放这里
README.md
config/              // 配置相关包
storage/             // 存储逻辑
api/                 // HTTP handler / grpc / rpc
tools/               // 非构建、非导出的脚本 & 工具(可忽略 go build)
docs/

如果你确实需要多个可发布包,再考虑增加清晰命名的子包,而不是 pkg/ 通用层。

Go 官方建议,要关注细节 Go 官方确实给了一份指南:go.dev/doc/modules/layout[1]。

里面有句话经常被曲解:

Larger packages or commands may benefit from splitting… Initially, put them in internal/.

这里的重点其实是 larger 和 may。

结果很多人一上来就机械套用:不管项目大小,先建个 internal/;

现实是要知道目录不是 “一步到位” 的事,需要阶段性调整和设计。

总结 我们要回归 Go 的哲学:简单优先,先能跑,再优雅。多数团队在项目初期做的过度工程(把 internal/、pkg/、cmd/ 都直接套上)更多是为了 “看起来成熟”,但长期结果往往是维护负担增加。

把注意力放在清晰的包命名、合理的功能边界、良好的 README 上,必要时再重构目录结构和演进会比较好。

私有仓库配置

将gitee.com 设置为私有仓库

  1. 首先配置https请求转换为ssh:
git config --global url."git@gitee.com:".insteadOf "https://gitee.com/"

 git config --global http.extraheader "PRIVATE-TOKEN: B9TWLqsLdh1-513os1Pm"

git config --global url."git@192.168.200.47".insteadOf "http://192.168.200.47:8083/"
git config --global url."git@192.168.200.47".insteadOf "http://git.zhongzhuoxin.com:9000/"
  1. 配置环境变量 go env -w GOPRIVATE=gitee.com

  2. 新建的仓库名字一定要改成和仓库地址一致

  • 新建仓库 gitee.com/zouni88/util
  • 项目目录下 go.mod 修改modulegitee.com/zouni88/util

bookCollapseSection: true weight: 3

安装环境,配置环境变量

GOROOT:go安装路径
GOPATH: 项目目录
PATH :%GOROOT%\bin;%GOPATH%\bin

GOland 配置环境

https://github.com/goproxyio/goproxy go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.io,direct

# 设置不走 proxy 的私有仓库,多个用逗号相隔(可选)
go env -w GOPRIVATE=*.corp.example.com

# 设置不走 proxy 的私有组织(可选)
go env -w GOPRIVATE=example.com/org_name

go moudle 初始化

go mod project_name
go build

go mod 引用本地项目

  • go mod edit -replace 项目名称=项目路径
go mod edit -replace testttt=/home/small/gopath/src/testttt

1. int

2. string

3. float

4. map

5.array,slice

进制转换:

//十进制转二进制 八进制 十六进制
func TestBase2(t *testing.T) {
	a := strconv.FormatInt(15, 2)
	b := strconv.FormatInt(15, 8)
	c := strconv.FormatInt(15, 10)
	d := strconv.FormatInt(15, 16)
	log.Printf("base2 = %s,base8 = %s,base10 = %s,base16 = %s", a, b, c, d)
}

output:

2022/03/23 15:05:26 base2 = 1111,base8 = 17,base10 = 15,base16 = f
--- PASS: TestBase2 (0.01s)

fmt.printf

常规格式化

  • %v : 打印变量值
  • %T : 类型

整型

  • %+d 带符号的整型,fmt.Printf("%+d", 255)输出+255
  • %q 打印单引号
  • %o 不带零的八进制
  • %#o 带零的八进制
  • %x 小写的十六进制
  • %X 大写的十六进制
  • %#x 带0x的十六进制
  • %U 打印Unicode字符
  • %#U 打印带字符的Unicode
  • %b 打印整型的二进制

float 浮点型

string 字符串

go中的异常处理机制

为了保证程序不会因为一个异常而导致停摆,同时又不像其它语言那么啰嗦,对go语言的异常机制的简单理解实践一下吧

package main

import "fmt"

func divide(a int,b int,isPanic bool) int  {
	//,类似于 java try catch ,,通过panic抛出异常 recover 捕获,
	if isPanic{
		defer func() {
			if err := recover(); err != nil{
				fmt.Println(err)
			}
		}()
	}
  //可以自行捕获异常,自定义异常信息
	/*if b==0{
		panic("除数不能等于0")
	}*/
	c := a/b
	return c
}

func main() {
	divide(3,0,true)
	fmt.Println("我的天,一切运转正常")
}

Out1:

runtime error: integer divide by zero
我的天,一切运转正常

Out2:

自定义异常信息
除数不能等于0
我的天,一切运转正常

如果没有recover,程序不能正常往下执行,就此打住了;

json编码

type Person struct {
	Name string `json:"name"`
	Age int `json:"age,omitempty"`
}

type Toys struct {
	Person
	Toys []string `json:"toys"`
}

func main(){
    p := Person{Name:"cao",Age:12}
    toys := []string{"a","b"}
    toy := Toys{Person:p,Toys: toys}
    resbyte,err := json.Marshal(toy)
    if err != nil{
      log.Fatal(err)
    }
    // 返回byte切片,转成string类型
    resstring := string(resbyte)
    fmt.Println(resstring)
}

Out:

{"name":"cao","toys":["a","b"]}

json:"age" : 可以理解成别名,

oemiempty : 值为空,就忽略此字段

解码json Unmarshal()

将json字符串转成结构体变量,json.Unmarshal()必须传入byte切片

unsafe.pointer

  1. 可支持操作内存;
  2. 可以转换到任意类型指针
  3. 非安全的,不建议使用
  4. unsafe.Pointer() 任意转换指针类型,并可进行指针运算
  5. 其他类型的指针只能转化为unsafe.Pointer,也只有unsafe.Pointer才能转化成任意类型的指针
  6. 只有uintptr才支持加减操作,而uintptr是一个非负整数,表示地址值,没有类型信息,以字节为单位

实际操作:获取一个字符串第二个字符的地址和值

    var s = "abc"
	var strHeader = (*reflect.StringHeader)(unsafe.Pointer(&s))
	var res = (*byte)(unsafe.Pointer(strHeader.Data)) //获取字符串第一个字符的地址
	log.Printf("strHeader.Data = %c", *res)
	var ress = (*byte)(unsafe.Pointer(strHeader.Data + uintptr(1))) //获取字符串第一个字符的地址
	log.Printf("strHeader.Data = %c", *ress)
	//var ssss uintptr = 0xc000088fc0
	//log.Println(*(*byte)(unsafe.Pointer(ssss)))

golang 1.18 正式版本,支持泛型


func TestGeneric(t *testing.T) {
	x := map[string]int{"tom": 12, "jelly": 7}
	res := Sums[string, int](x)
	log.Println(res)

    stuC := StuCross[string]{
		score: "成绩",
	}
	log.Println(stuC)
}

//指定函数参数类型约束
func Sums[K string, V int | float32](m map[K]V) V {
	var x V
	for _, v := range m {
		x += v
	}
	return x
}

type StuCross[K string] struct {
	score K
}

map

map中元素为结构体,不能直接修改,因为map元素无法取地址;

type Student struct {
	name string
}

修改map中struct元素的值,错误示例:

func TestMap(t *testing.T)  {
	m := map[string]Student11{"people": {"zhoujielun"}}
  //编译错误 Cannot assign to m["people"].name
	m["people"].name = "wuyanzu"
}

解决办法:

func TestMap(t *testing.T)  {
  //Student => *Student
	m := map[string]*Student{"people": {"zhoujielun"}}
  //编译错误 Cannot assign to m["people"].name
	m["people"].name = "wuyanzu"
}

闭包

func TestCloser(t *testing.T) {
	x := func() func() {
		var i = 1
		return func() {
			i++
			log.Println("i = ", i)
		}
	}()
	x()
	x()
}

输出结果:

=== RUN   TestCloser
2022/04/10 21:42:09 2
2022/04/10 21:42:09 3
--- PASS: TestCloser (0.01s)

闭包持有外部变量i

//GenerateKey函数使用随机数据生成器random生成一对具有指定字位数的RSA密钥
	//Reader是一个全局、共享的密码用强随机数生成器
	var bits = 2048
	priKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		panic(err)
	}
	x509Key := x509.MarshalPKCS1PrivateKey(priKey)
	priKeyFile, err := os.Create("pk.pem")
	if err != nil {
		panic(err)
	}
	defer priKeyFile.Close()
	priKeyBlock := pem.Block{Type: "RSA Private Key", Bytes: x509Key}

	pem.Encode(priKeyFile, &priKeyBlock)

	x509PubKey := x509.MarshalPKCS1PublicKey(&priKey.PublicKey)

	pubKeyFile, err := os.Create("pubKey.pem")
	if err != nil {
		panic(err)
	}
	defer pubKeyFile.Close()
	pubKeyBlock := pem.Block{Type: "RSA Public Key", Bytes: x509PubKey}
	// block 输出到文件
	pem.Encode(pubKeyFile, &pubKeyBlock)
	x := pem.EncodeToMemory(&pubKeyBlock)
	log.Println(string(x))

context 基本使用

func TestTimeout(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	//1. 传递上下文
	go RequestA(ctx)

	time.Sleep(time.Second * 4)
	//#cancel 1.取消
	cancel()
	time.Sleep(time.Second * 10)
}

func RequestA(ctx context.Context) {
	// Do Something ...
	ctx = context.WithValue(ctx, "A", "RequestA")
	tic := time.NewTicker(time.Second * 2)
	// 2. 传递上下文
	go RequestB(ctx)
	for {
		select {
		case <-tic.C:
			//3. 开始转圈圈
			log.Println("A转圈圈")
		case <-ctx.Done():
			//#cancel. A结束
			log.Println("请求A ctx.Done")
			return
		}
	}
}

func RequestB(ctx context.Context) {
	// Do Something ...
	val := ctx.Value("A")
	log.Println("from A : ", val)
	ctx = context.WithValue(ctx, "B", "RequestB")
	tic := time.NewTicker(time.Second * 1)
	for {
		select {
		case <-tic.C:
			//4. 开始转圈圈 1秒1次
			log.Println("B转圈圈")
		case <-ctx.Done():
			//#cancel. B 结束
			log.Println("请求B ctx.Done")
			return
		}
	}
}

bookCollapseSection: true weight: 3

app.conf
runmode = dev //有4中
// dev 开发
//test 测试
//sprod 准生产环境
//prod 生产环境

go get github.com/astaxie/beego/orm

go get github.com/go-sql-driver/mysql

安装beego管理工具bee

go get github.com/beego/bee
go get github.com/astaxie/beego

装好了bee管理工具之后,运用bee创建项目

创建项目

bee new会自动在GOPATH路径src目录下创建项目

bee new project_name

运行项目

定位到项目目录 GOPATH/src/project_name


go build project_name
go run
或者
bee run

部署

编译打包

windows平台打包linux可执行文件

package main

import (
	"fmt"
	"os"
	"os/exec"
)

//filepath: 要编译的文件的路径
func build(filepath string){
	_ = os.Setenv("CGO_ENABLED", "0")
	_ = os.Setenv("GOARCH", "amd64")
	_ = os.Setenv("GOOS", "linux")

	arg := []string{"build", filepath}
	if err := exec.Command("go", arg...).Run(); err!=nil {
		fmt.Println("编译失败:", err)
	} else{
		fmt.Println("编译成功")
	}
}

func main() {
	build(`D:\WorkProject\go\src\beego_first\main.go`)
}

go 编译之后是二进制文件,beego:打包需要自行copy > (views,static,conf) 几个文件夹到部署应用目录下


bookCollapseSection: true weight: 3

gorm使用

跨域请求在网上找了很多文章都是下面这种做法:

func cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		fmt.Println("啥玩意儿的")
		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
		c.Header("Access-Control-Allow-Credentials", "true")
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		c.Next()
	}
}

但是并不好使用,所以又找到下面这种方法,总算是搞明白了啥意思了!

func cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		origin := c.Request.Header.Get("origin")
		if len(origin) == 0 {
			origin = c.Request.Header.Get("Origin")
		}
		c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
		c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST")
		c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")
		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
			return
		}
		c.Next()
	}
}

跨域请求字段说明
Access-Control-Allow-Origin

首先,客户端请求时要带上一个Origin,用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。然后服务端在返回时需要带上这个字段,并把对方传过来的值返回去。告知客户端,允许这次请求。 这个字段也可以设置为*,即允许所有客户端访问。但是这样做会和Access-Control-Allow-Credentials 起冲突。可能导致跨域请求失败。

Access-Control-Allow-Credentials

这个字段是一个BOOL值,可以允许客户端携带一些校验信息,比如cookie等。如果设置为Access-Control-Allow-Origin:*,而该字段是true,并且客户端开启了withCredentials, 仍然不能正确访问。需要把Access-Control-Allow-Origin的值设置为客户端传过来的值。

main

程序入口

routers

路由配置

controllers

控制器

dao

数据库操作

static

静态文件

models

数据模型

services

逻辑业务操作

config

4.go-kit

go-kit 代码生成工具 https://github.com/metaverse/truss

  • transport : 协议传输层
  • service : 业务层
  • endpoint : 中间结点
    newServer 调用流程:transport dec --> endpoint --> service --> transport enc

bookCollapseSection: true weight: 3

1. 定义proto文件

//声明protobuf版本
syntax = "proto3";

自动生成pb的时候,会提示要有go_package

option go_package='.;grpc';
  • .: 表示生成pb文件在哪个位置
  • ;grpc: 表示生成的pb文件所属包名
package grpc;

service Greeter{
  rpc SayHello (HelloRequest) returns(HelloReply){}
}

message HelloRequest{
  string name = 1;
}

message HelloReply{
  string message = 1;
}

2. 生成pb文件

//go:generate protoc -I . --go_out=plugins=grpc:./ ./helloworld.proto

配置基本信息

  1. 获取grpc
go get -u google.golang.org/grpc

go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

bookCollapseSection: true weight: 3

创建运行容器

docker run -itd --name small-mq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 5672:5672 -p 15672:15672 rabbitmq

我是分割线

docker run -dit --name Myrabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq:managemen


bookCollapseSection: true weight: 3

consul 实例1

docker run -itd --name=consule1 -p 8500:8500 --restart=always -e consul_bind_interface='eth0' --privileged=true --name=consul1 consul agent -server -bootstrap-expect=2  -ui -node=consul1 -client='0.0.0.0' -data-dir /consul/data -config-dir /consul/config -datacenter=consul_dc

参数说明:

docker run -itd --name=consul -p 8500:8500 consul agent -server -bootstrap -ui  -client 0.0.0.0

-server : 以服务端方式启动 -bootstrap : 指定自己为leader ,而不需要选举 -ui : 启动一个内置管理web界面 -client : 指定客户端可以访问的IP. 设置为0.0.0.0 则任意访问,否则默认本机可以访问

实例1 ip : 172.17.0.2

consul 实例2

docker run -itd --name=consule1 -p 8200:8500 --restart=always -e consul_bind_interface='eth0' --privileged=true --name=consul2 consul agent -server -bootstrap-expect=2  -ui -node=consul2 -client='0.0.0.0' -data-dir /consul/data -config-dir /consul/config -datacenter=consul_dc -join=172.17.0.2

bookCollapseSection: true weight: 3

go 关键字 就定义了一个协程


bookCollapseSection: true weight: 3

dlv debug main.go

单元测试

1. 测试指定文件名 name_test.go

package nsqm

import (
    "testing"
)

func TestNsq(t *testing.T){

}

2. 运行

# -test.run funName   指定执行测试方法名
go test -v nsqm -test.run TestNsq

chromedp

chromedp 常用方法

chromedp.NewContext() 初始化chromedp的上下文,后续这个页面都使用这个上下文进行操作

chromedp.Run() 运行一个chrome的一系列操作

chromedp.Navigate() 将浏览器导航到某个页面

chromedp.WaitVisible() 等候某个元素可见,再继续执行。

chromedp.Click() 模拟鼠标点击某个元素

chromedp.Value() 获取某个元素的value值

chromedp.ActionFunc() 再当前页面执行某些自定义函数

chromedp.Text() 读取某个元素的text值

chromedp.Evaluate() 执行某个js,相当于控制台输入js

network.SetExtraHTTPHeaders() 截取请求,额外增加header头

chromedp.SendKeys() 模拟键盘操作,输入字符

chromedp.Nodes() 根据xpath获取某些元素,并存储进入数组

chromedp.NewRemoteAllocator

chromedp.OuterHTML() 获取元素的outer html

chromedp.Screenshot() 根据某个元素截图

page.CaptureScreenshot() 截取整个页面的元素

chromedp.Submit() 提交某个表单

chromedp.WaitNotPresent() 等候某个元素不存在,比如“正在搜索。。。”

chromedp.Tasks{} 一系列Action组成的任务

安装docker 镜像

  1. 下载docker image : docker pull chromedp/headless-shell

  2. 运行docker : docker run -d -p 9222:9222 --rm --name headless-shell chromedp/headless-shell

爬虫

当涉及到网页抓取和解析HTML/XML文档时,XPath是一种强大的定位和提取数据的工具。XPath(XML Path Language)是一种在XML文档中定位节点的语言。下面是一些关于XPath的详细解释和案例:

基本介绍

  1. XPath基础 XPath的基本语法如下:
/         # 从根节点开始
//        # 选择匹配的任何位置
.         # 当前节点
..        # 父节点
@         # 选择属性
[node]    # 选取所有node子元素
[@attr]   # 选取带有attr属性的所有元素
  1. 选取节点 使用XPath选取节点,例如:
//div          # 选择所有div元素
//div[@class]  # 选择带有class属性的div元素
//div[@id='myId']  # 选择id属性为'myId'的div元素
  1. 路径表达式 XPath使用路径表达式来选取节点。例如:
//div/p   # 选择所有div下的p元素
//div//p  # 选择所有div下的所有p元素
  1. 谓词 XPath中的谓词用于过滤节点。例如:
//div[@class='highlight']  # 选择class属性为'highlight'的div元素
//ul/li[position()<3]      # 选择ul下的前两个li元素
  1. 通配符 使用通配符匹配元素,例如:
    //*        # 选择所有元素
    //div/*    # 选择所有div下的所有子元素
  1. 文本提取 使用XPath提取文本内容,例如:
//p/text()   # 提取p元素的文本内容

XPath 简介

什么是 XPath

XPath(XML Path Language)即 XML 路径语言,是一种用于在 XML 和 HTML 文档中查找信息的语言 。它基于 XML 文档的树状结构,提供了在数据结构树中找寻节点的能力。通过 XPath,你可以使用路径表达式来定位和选择文档中的节点或节点集,这些节点可以是元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点等。比如,在一个图书管理系统的 XML 文档中,你可以利用 XPath 快速定位到所有价格高于 50 元的图书节点,获取它们的书名、作者等信息。

XPath 的重要性

XPath 在多个领域都有着举足轻重的地位:

  • 数据提取:在从 XML 或 HTML 文档中提取数据时,XPath 提供了一种简洁且强大的方式。比如从一个电商网站的产品列表页面提取商品名称、价格、评论数等信息,XPath 可以帮助我们精准定位到包含这些数据的 HTML 节点。 网页爬虫:在爬虫开发中,XPath 是常用的解析工具之一。通过编写 XPath 表达式,爬虫可以高效地从网页中提取所需的数据,为后续的数据分析、信息挖掘等提供数据支持 。以爬取新闻网站的文章为例,使用 XPath 可以轻松定位到文章的标题、正文、发布时间等关键信息。 XML 处理:在处理 XML 文档时,无论是验证文档结构、修改节点内容还是进行数据转换,XPath 都能发挥重要作用。例如,在一个企业的订单管理系统中,使用 XML 来存储订单信息,XPath 可以用于查询特定订单、更新订单状态等操作。

准备工作
工具推荐
在学习和使用 XPath 的过程中,选择合适的工具可以事半功倍。以下为大家推荐几款常用工具 :

Chrome 开发者工具:作为 Chrome 浏览器自带的强大工具,按下 F12 键即可呼出。在 “Elements” 面板中,通过鼠标悬停和点击,可以快速定位到网页的 HTML 元素,右键点击元素还能直接复制 XPath 表达式,方便验证和测试。比如在分析一个电商产品页面时,利用 Chrome 开发者工具能迅速获取商品名称、价格等元素的 XPath。
Firefox 开发者工具:同样是浏览器自带工具,功能与 Chrome 开发者工具类似。它也能让你在页面中轻松定位元素,并查看和测试 XPath 表达式。对于习惯使用 Firefox 浏览器的开发者来说,这是一个不错的选择。
在线 XPath 测试工具:如 “XPath Tester” 等在线工具,无需安装,打开网页即可使用。你只需将 XML 或 HTML 文档内容粘贴进去,输入 XPath 表达式,就能实时查看匹配结果。这种工具特别适合初学者快速上手,进行简单的 XPath 练习。
XPath Helper 插件:以 Chrome 浏览器为例,安装 XPath Helper 插件后,在浏览网页时,它会在浏览器界面中添加一个浮动窗口,显示当前鼠标悬停元素的 XPath 路径,并且可以直接在窗口中编辑和测试 XPath 表达式,大大提高了开发效率。
示例 HTML 文档
为了更直观地讲解 XPath 语法,我们先准备一个简单的 HTML 文档示例:

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <title>XPath示例页面</title>
</head>
 
<body>
    <div id="content">
        <h1>欢迎来到XPath学习页面</h1>
        <p class="intro">这是一个用于学习XPath的示例页面。</p>
        <ul id="book-list">
            <li class="book-item">
                <a href="book1.html">《Python编程从入门到实践》</a>
                <p class="author">Eric Matthes</p>
                <p class="price">79.00元</p>
            </li>
            <li class="book-item">
                <a href="book2.html">《Effective Java》</a>
                <p class="author">Joshua Bloch</p>
                <p class="price">99.00元</p>
            </li>
        </ul>
        <a href="about.html">关于我们</a>
    </div>
</body>
 
</html>

在后续的 XPath 语法讲解中,我们将基于这个示例文档进行演示,通过实际操作来深入理解 XPath 的各种用法。

XPath 基础知识 基本路径表达式 XPath 使用路径表达式来选取 XML 或 HTML 文档中的节点或节点集。以下是一些常用的路径表达式符号及其含义 :

nodename:选取此节点的所有子节点。例如,在我们的示例 HTML 文档中,ul 会选取所有的

    元素节点及其子节点。 /:从根节点选取。/html 表示从 HTML 文档的根节点 开始选取,它是一个绝对路径表达式 。 //:从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。//li 可以选取文档中所有的
  • 元素节点,无论它们在文档的哪个层级 。 .:选取当前节点。假设我们已经定位到了某个
    元素节点,使用 . 就表示当前这个
    节点本身。 ..:选取当前节点的父节点。如果当前节点是

    元素节点,且它是某个

    元素节点的子节点,那么 .. 就可以选取到这个
    父节点。 @:选取属性。//a[@href] 会选取所有带有 href 属性的 元素节点;//@class 则会选取文档中所有的 class 属性 。 下面通过具体的示例来进一步理解这些路径表达式的用法:

    选取所有的

    元素节点://h1。在示例文档中,这个表达式会找到

    欢迎来到XPath学习页面

    这一节点。 选取根节点下的 元素节点的直接子节点
    :/html/body/div。这是一个绝对路径,从根节点 开始,依次经过 节点,找到其直接子节点
    。 选取所有带有 id 属性的
  • 元素节点://li[@id]。在示例文档中,虽然没有这样的
  • 节点,但如果有,就可以通过这个表达式找到。 节点选择 在 XPath 中,可以根据节点的类型和属性来选择节点 :

    元素节点:直接使用元素名称即可选取。如 //book 选取所有的 元素节点;/html/body/div/ul/li 可以选取到示例文档中

      下的所有
    • 元素节点,这是从根节点开始的相对路径选择。 属性节点:使用 @ 符号加上属性名称。//a[@href] 选取所有带有 href 属性的 元素节点;//li[@class='book-item'] 选取所有 class 属性值为 book-item 的
    • 元素节点 。 文本节点:使用 text() 函数。//p[@class='intro']/text() 可以选取到

      这是一个用于学习XPath的示例页面。

      中的文本内容 “这是一个用于学习 XPath 的示例页面。”;//a/text() 则会选取所有
      元素节点的文本内容 ,如 “《Python 编程从入门到实践》”“《Effective Java》” 等。 通过灵活运用这些节点选择方法,结合路径表达式,我们能够在复杂的 XML 或 HTML 文档中准确地定位到所需的节点,为数据提取和处理打下坚实的基础。

      基本选择器 选择特定元素 在 XPath 中,通过标签名可以直接选择文档中的特定元素。例如,在我们的示例 HTML 文档中,要选择所有的

    • 元素,可以使用以下 XPath 表达式:

      //li

      这个表达式会选取文档中所有的

    • 元素节点,因为//表示从当前节点开始,无论在文档的哪个层级,都查找所有匹配的节点 ,而li就是我们要选择的标签名。在 Python 中,使用lxml库结合 XPath 来选择这些元素的代码示例如下:

      from lxml import etree

      假设html为读取的示例HTML文档内容

      html = """

      XPath示例页面

      欢迎来到XPath学习页面

      这是一个用于学习XPath的示例页面。

      关于我们
      """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择所有的
    • 元素 lis = root.xpath('//li') for li in lis: print(etree.tostring(li, encoding='utf-8').decode('utf-8')) 上述代码中,首先使用etree.HTML()方法将 HTML 字符串转换为Element对象,然后通过xpath('//li')选择所有的
    • 元素,并将其打印输出。

      选择特定路径上的元素 XPath 中可以使用绝对路径和相对路径来选择特定路径上的元素 。

      绝对路径:从根节点开始,通过指定每个节点的层级关系来定位目标元素,以正斜杠(/)开头 。例如,在示例 HTML 文档中,要选择元素下的

      元素下的

      元素,可以使用绝对路径: /html/body/div/h1

      相对路径:相对于当前节点的路径,通常使用./表示从当前节点开始查找子节点,../表示查找父节点,//表示跨越多层级查找节点 。假设当前节点是

      元素,要选择其下的
        元素,可以使用相对路径: ./ul

        或者使用//跨层级查找:

        //ul

        绝对路径和相对路径的主要区别在于:绝对路径的定位非常精确,从根节点开始,路径表达明确,能够准确地定位到目标节点 ,但当文档结构发生变化时,路径可能会失效;相对路径更为灵活、简洁,并且具有良好的可维护性,当文档结构发生变化时,相对路径的调整相对简单,但可能无法像绝对路径那样精确定位某些节点,尤其是当文档结构比较复杂或存在多个相同节点名称时 。

        通配符选择 XPath 中的通配符可以用于匹配未知的元素节点或属性节点,为我们在选择节点时提供了更大的灵活性 。

        通配符:匹配任何元素节点。例如,要选择

        元素下的所有子元素,无论它们是什么标签,可以使用: //div/

        在示例 HTML 文档中,这个表达式会选取

        下的

          等所有子元素 。

          @通配符:匹配任何属性节点。如果要选择文档中所有元素的所有属性,可以使用: //@

          这将返回所有元素的属性,如

          的id属性、的href属性、
        • 的class属性等 。通配符在实际应用中非常有用,特别是当我们不确定文档中某些节点的具体标签名或属性名时,可以借助通配符来进行更宽泛的选择 。

          属性选择器 选择具有特定属性的元素 在 XPath 中,通过@符号选择具有特定属性的元素。例如,在我们的示例 HTML 文档中,要选择所有带有href属性的元素,可以使用以下 XPath 表达式:

          //a[@href]

          这个表达式中,//a表示选择所有的元素,[@href]则表示筛选出带有href属性的元素。在 Python 中,使用lxml库结合 XPath 来选择这些元素的代码示例如下:

          from lxml import etree

          假设html为读取的示例HTML文档内容

          html = """

          XPath示例页面

          欢迎来到XPath学习页面

          这是一个用于学习XPath的示例页面。

          关于我们
          """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择所有带有href属性的元素 as_with_href = root.xpath('//a[@href]') for a in as_with_href: print(etree.tostring(a, encoding='utf-8').decode('utf-8'))

          上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//a[@href]')选择所有带有href属性的元素,并将其打印输出。

          选择特定属性值的元素 使用方括号筛选具有特定属性值的元素。在示例 HTML 文档中,若要选择class属性值为book-item的

        • 元素,XPath 表达式为:

          //li[@class='book-item']

          在这个表达式里,//li表示选择所有的

        • 元素,[@class='book-item']用于筛选出class属性值为book-item的元素 。Python 代码实现如下:

          from lxml import etree

          假设html为读取的示例HTML文档内容

          html = """

          XPath示例页面

          欢迎来到XPath学习页面

          这是一个用于学习XPath的示例页面。

          关于我们
          """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择class属性值为book-item的
        • 元素 lis = root.xpath('//li[@class="book-item"]') for li in lis: print(etree.tostring(li, encoding='utf-8').decode('utf-8'))

          运行这段代码,会输出所有class属性值为book-item的

        • 元素内容。

          选择具有特定属性的元素(无论值是什么) 使用@*选择具有任意属性的元素。比如,要选择示例 HTML 文档中所有具有属性的元素,可以使用:

          //[@]

          这个表达式中,//表示选择所有元素,[@]表示筛选出具有任意属性的元素 。在 Python 中,实现代码如下:

          from lxml import etree

          假设html为读取的示例HTML文档内容

          html = """

          XPath示例页面

          欢迎来到XPath学习页面

          这是一个用于学习XPath的示例页面。

          关于我们
          """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择具有任意属性的元素 elements_with_attr = root.xpath('//*[@*]') for element in elements_with_attr: print(etree.tostring(element, encoding='utf-8').decode('utf-8'))

          执行代码后,会打印出文档中所有具有属性的元素内容。

          条件表达式 使用条件选择元素 在 XPath 中,方括号([])用于包含条件表达式,以此筛选出符合特定条件的元素。例如,在我们的示例 HTML 文档中,要选择

            下的第一个
          • 元素,可以使用以下 XPath 表达式:

            //ul/li[1]

            这里的[1]表示选择列表中的第一个元素。需要注意的是,在 XPath 中,索引从 1 开始,而不是 0。在 Python 中,使用lxml库结合 XPath 来选择这个元素的代码示例如下:

            from lxml import etree

            假设html为读取的示例HTML文档内容

            html = """

            XPath示例页面

            欢迎来到XPath学习页面

            这是一个用于学习XPath的示例页面。

            关于我们
            """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择
              下的第一个
            • 元素 first_li = root.xpath('//ul/li[1]') for li in first_li: print(etree.tostring(li, encoding='utf-8').decode('utf-8'))

              上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//ul/li[1]')选择

                下的第一个
              • 元素,并将其打印输出。

                使用 or 条件 在 XPath 中,or运算符用于实现多条件选择,只要满足其中一个条件的元素就会被选中。例如,在示例 HTML 文档中,若要选择class属性值为book-item或者href属性值为about.html的元素,可以使用以下 XPath 表达式:

                //*[@class='book-item' or @href='about.html']

                在这个表达式中,//*表示选择所有元素,[@class='book-item' or @href='about.html']表示筛选出class属性值为book-item或者href属性值为about.html的元素 。在 Python 中,使用lxml库结合 XPath 来选择这些元素的代码示例如下:

                from lxml import etree

                假设html为读取的示例HTML文档内容

                html = """

                XPath示例页面

                欢迎来到XPath学习页面

                这是一个用于学习XPath的示例页面。

                关于我们
                """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择class属性值为book-item或者href属性值为about.html的元素 elements = root.xpath('//*[@class="book-item" or @href="about.html"]') for element in elements: print(etree.tostring(element, encoding='utf-8').decode('utf-8'))

                上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//*[@class="book-item" or @href="about.html"]')选择符合条件的元素,并将其打印输出。

                使用 not 函数 not()函数用于排除特定条件的元素。例如,在示例 HTML 文档中,要选择所有class属性值不为book-item的

              • 元素,可以使用以下 XPath 表达式:

                //li[not(@class='book-item')]

                在这个表达式中,//li表示选择所有的

              • 元素,not(@class='book-item')表示排除class属性值为book-item的元素 。在 Python 中,使用lxml库结合 XPath 来选择这些元素的代码示例如下:

                from lxml import etree

                假设html为读取的示例HTML文档内容

                html = """

                XPath示例页面

                欢迎来到XPath学习页面

                这是一个用于学习XPath的示例页面。

                关于我们
                """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择class属性值不为book-item的
              • 元素 lis = root.xpath('//li[not(@class="book-item")]') for li in lis: print(etree.tostring(li, encoding='utf-8').decode('utf-8'))

                上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//li[not(@class="book-item")]')选择符合条件的元素,并将其打印输出。

                位置选择器 选择第一个元素 在 XPath 中,使用[1]可以选择列表中的第一个元素 。例如,在我们的示例 HTML 文档中,要选择

                  下的第一个
                • 元素,可以使用以下 XPath 表达式:

                  //ul/li[1]

                  在这个表达式中,//ul表示选择所有的

                    元素,li[1]表示在
                      元素下选择第一个
                    • 元素 。在 Python 中,使用lxml库结合 XPath 来选择这个元素的代码示例如下:

                      from lxml import etree

                      假设html为读取的示例HTML文档内容

                      html = """

                      XPath示例页面

                      欢迎来到XPath学习页面

                      这是一个用于学习XPath的示例页面。

                      关于我们
                      """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择
                        下的第一个
                      • 元素 first_li = root.xpath('//ul/li[1]') for li in first_li: print(etree.tostring(li, encoding='utf-8').decode('utf-8'))

                        上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//ul/li[1]')选择

                          下的第一个
                        • 元素,并将其打印输出 。

                          选择最后一个元素 使用last()函数选择列表中的最后一个元素 。例如,在示例 HTML 文档中,要选择

                            下的最后一个
                          • 元素,可以使用以下 XPath 表达式:

                            //ul/li[last()]

                            在这个表达式中,//ul表示选择所有的

                              元素,li[last()]表示在
                                元素下选择最后一个
                              • 元素 。在 Python 中,使用lxml库结合 XPath 来选择这个元素的代码示例如下:

                                from lxml import etree

                                假设html为读取的示例HTML文档内容

                                html = """

                                XPath示例页面

                                欢迎来到XPath学习页面

                                这是一个用于学习XPath的示例页面。

                                关于我们
                                """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择
                                  下的最后一个
                                • 元素 last_li = root.xpath('//ul/li[last()]') for li in last_li: print(etree.tostring(li, encoding='utf-8').decode('utf-8'))

                                  上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//ul/li[last()]')选择

                                    下的最后一个
                                  • 元素,并将其打印输出 。

                                    选择特定位置范围的元素 使用position()函数结合条件表达式来选择特定位置范围的元素 。例如,在示例 HTML 文档中,要选择

                                      下的第二个和第三个
                                    • 元素,可以使用以下 XPath 表达式:

                                      //ul/li[position() > 1 and position() < 4]

                                      在这个表达式中,//ul表示选择所有的

                                        元素,li[position() > 1 and position() < 4]表示在
                                          元素下选择位置大于 1 且小于 4 的
                                        • 元素,即第二个和第三个
                                        • 元素 。在 Python 中,使用lxml库结合 XPath 来选择这些元素的代码示例如下:

                                          from lxml import etree

                                          假设html为读取的示例HTML文档内容

                                          html = """

                                          XPath示例页面

                                          欢迎来到XPath学习页面

                                          这是一个用于学习XPath的示例页面。

                                          关于我们
                                          """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择
                                            下的第二个和第三个
                                          • 元素 lis = root.xpath('//ul/li[position() > 1 and position() < 4]') for li in lis: print(etree.tostring(li, encoding='utf-8').decode('utf-8'))

                                            上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//ul/li[position() > 1 and position() < 4]')选择

                                              下的第二个和第三个
                                            • 元素,并将其打印输出 。

                                              文本内容选择 选择包含特定文本的元素 在 XPath 中,使用contains()函数可以选择包含特定文本的元素 。例如,在我们的示例 HTML 文档中,要选择包含 “Python” 文本的元素,可以使用以下 XPath 表达式:

                                              //a[contains(text(), 'Python')]

                                              在这个表达式中,//a表示选择所有的元素,contains(text(), 'Python')表示筛选出文本内容中包含 “Python” 的元素 。在 Python 中,使用lxml库结合 XPath 来选择这个元素的代码示例如下:

                                              from lxml import etree

                                              假设html为读取的示例HTML文档内容

                                              html = """

                                              XPath示例页面

                                              欢迎来到XPath学习页面

                                              这是一个用于学习XPath的示例页面。

                                              关于我们
                                              """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择包含“Python”文本的元素 as_with_python = root.xpath('//a[contains(text(), "Python")]') for a in as_with_python: print(etree.tostring(a, encoding='utf-8').decode('utf-8'))

                                              上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//a[contains(text(), "Python")]')选择包含 “Python” 文本的元素,并将其打印输出 。

                                              选择完全匹配文本的元素 使用text()函数结合条件表达式选择完全匹配文本的元素 。例如,在示例 HTML 文档中,要选择文本内容为 “关于我们” 的元素,可以使用以下 XPath 表达式:

                                              //a[text()='关于我们']

                                              在这个表达式中,//a表示选择所有的元素,text()='关于我们'表示筛选出文本内容完全为 “关于我们” 的元素 。在 Python 中,使用lxml库结合 XPath 来选择这个元素的代码示例如下:

                                              from lxml import etree

                                              假设html为读取的示例HTML文档内容

                                              html = """

                                              XPath示例页面

                                              欢迎来到XPath学习页面

                                              这是一个用于学习XPath的示例页面。

                                              关于我们
                                              """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择文本内容为“关于我们”的元素 a_with_text = root.xpath('//a[text()="关于我们"]') for a in a_with_text: print(etree.tostring(a, encoding='utf-8').decode('utf-8'))

                                              上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//a[text()="关于我们"]')选择文本内容为 “关于我们” 的元素,并将其打印输出 。

                                              XPath 轴 子节点轴 使用child::轴选择当前节点的子节点,它用于定位某个节点的直接子元素。例如,在我们的示例 HTML 文档中,要选择

                                              下的所有直接子节点

                                              ,可以使用以下 XPath 表达式:

                                              //div[@id='content']/child::h1

                                              在这个表达式中,//div[@id='content']用于定位id为content的

                                              元素,child::h1则表示选择该
                                              元素的直接子节点

                                              。在 Python 中,使用lxml库结合 XPath 来选择这个元素的代码示例如下:

                                              from lxml import etree

                                              假设html为读取的示例HTML文档内容

                                              html = """

                                              XPath示例页面

                                              欢迎来到XPath学习页面

                                              这是一个用于学习XPath的示例页面。

                                              关于我们
                                              """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择
                                              下的所有直接子节点

                                              h1s = root.xpath('//div[@id="content"]/child::h1') for h1 in h1s: print(etree.tostring(h1, encoding='utf-8').decode('utf-8'))

                                              上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//div[@id="content"]/child::h1')选择

                                              下的所有直接子节点

                                              ,并将其打印输出 。实际上,在 XPath 中,child::轴是默认轴,所以上述表达式也可以简写为:

                                              //div[@id='content']/h1

                                              父节点轴 使用parent::轴选择当前节点的父节点 。例如,在示例 HTML 文档中,要选择

                                              元素的父节点

                                            • ,可以使用以下 XPath 表达式:

                                              //p[@class='author']/parent::li

                                              在这个表达式中,//p[@class='author']用于定位class为author的

                                              元素,parent::li表示选择该

                                              元素的父节点

                                            • 。在 Python 中,使用lxml库结合 XPath 来选择这个元素的代码示例如下:

                                              from lxml import etree

                                              假设html为读取的示例HTML文档内容

                                              html = """

                                              XPath示例页面

                                              欢迎来到XPath学习页面

                                              这是一个用于学习XPath的示例页面。

                                              关于我们
                                              """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择

                                              元素的父节点

                                            • lis = root.xpath('//p[@class="author"]/parent::li') for li in lis: print(etree.tostring(li, encoding='utf-8').decode('utf-8'))

                                              上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//p[@class="author"]/parent::li')选择

                                              元素的父节点

                                            • ,并将其打印输出 。

                                              兄弟节点轴 使用following-sibling::轴选择当前节点之后的兄弟节点,使用preceding-sibling::轴选择当前节点之前的兄弟节点 。例如,在示例 HTML 文档中,要选择

                                                之后的兄弟节点,可以使用以下 XPath 表达式:

                                                //ul[@id='book-list']/following-sibling::a

                                                在这个表达式中,//ul[@id='book-list']用于定位id为book-list的

                                                  元素,following-sibling::a表示选择该
                                                    元素之后的兄弟节点 。在 Python 中,使用lxml库结合 XPath 来选择这个元素的代码示例如下:

                                                    from lxml import etree

                                                    假设html为读取的示例HTML文档内容

                                                    html = """

                                                    XPath示例页面

                                                    欢迎来到XPath学习页面

                                                    这是一个用于学习XPath的示例页面。

                                                    关于我们
                                                    """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择
                                                      之后的兄弟节点 as_after_ul = root.xpath('//ul[@id="book-list"]/following-sibling::a') for a in as_after_ul: print(etree.tostring(a, encoding='utf-8').decode('utf-8'))

                                                      上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//ul[@id="book-list"]/following-sibling::a')选择

                                                        之后的兄弟节点,并将其打印输出 。

                                                        若要选择

                                                      • 中第一个

                                                        元素之前的兄弟节点,可以使用以下 XPath 表达式:

                                                        //li[@class='book-item']/p[1]/preceding-sibling::a

                                                        在这个表达式中,//li[@class='book-item']/p[1]用于定位class为book-item的

                                                      • 元素中的第一个

                                                        元素,preceding-sibling::a表示选择该

                                                        元素之前的兄弟节点 。在 Python 中,使用lxml库结合 XPath 来选择这个元素的代码示例如下:

                                                        from lxml import etree

                                                        假设html为读取的示例HTML文档内容

                                                        html = """

                                                        XPath示例页面

                                                        欢迎来到XPath学习页面

                                                        这是一个用于学习XPath的示例页面。

                                                        关于我们
                                                        """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择
                                                      • 中第一个

                                                        元素之前的兄弟节点 as_before_p = root.xpath('//li[@class="book-item"]/p[1]/preceding-sibling::a') for a in as_before_p: print(etree.tostring(a, encoding='utf-8').decode('utf-8'))

                                                        上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//li[@class="book-item"]/p[1]/preceding-sibling::a')选择

                                                      • 中第一个

                                                        元素之前的兄弟节点,并将其打印输出 。

                                                        祖先节点轴 使用ancestor::轴选择当前节点的祖先节点,它包括当前节点的父节点以及父节点的父节点等 。例如,在示例 HTML 文档中,要选择元素的所有祖先节点

                                                        ,可以使用以下 XPath 表达式:

                                                        //a[@href='book1.html']/ancestor::div

                                                        在这个表达式中,//a[@href='book1.html']用于定位href为book1.html的元素,ancestor::div表示选择该元素的所有祖先节点

                                                        。在 Python 中,使用lxml库结合 XPath 来选择这些元素的代码示例如下:

                                                        from lxml import etree

                                                        假设html为读取的示例HTML文档内容

                                                        html = """

                                                        XPath示例页面

                                                        欢迎来到XPath学习页面

                                                        这是一个用于学习XPath的示例页面。

                                                        关于我们
                                                        """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择元素的所有祖先节点
                                                        divs = root.xpath('//a[@href="book1.html"]/ancestor::div') for div in divs: print(etree.tostring(div, encoding='utf-8').decode('utf-8'))

                                                        上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//a[@href="book1.html"]/ancestor::div')选择元素的所有祖先节点

                                                        ,并将其打印输出 。

                                                        后代节点轴 使用descendant::轴选择当前节点的后代节点,它包括当前节点的子节点以及子节点的子节点等 。例如,在示例 HTML 文档中,要选择

                                                        的所有后代节点

                                                        ,可以使用以下 XPath 表达式:

                                                        //div[@id='content']/descendant::p

                                                        在这个表达式中,//div[@id='content']用于定位id为content的

                                                        元素,descendant::p表示选择该
                                                        元素的所有后代节点

                                                        。在 Python 中,使用lxml库结合 XPath 来选择这些元素的代码示例如下:

                                                        from lxml import etree

                                                        假设html为读取的示例HTML文档内容

                                                        html = """

                                                        XPath示例页面

                                                        欢迎来到XPath学习页面

                                                        这是一个用于学习XPath的示例页面。

                                                        关于我们
                                                        """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择
                                                        的所有后代节点

                                                        ps = root.xpath('//div[@id="content"]/descendant::p') for p in ps: print(etree.tostring(p, encoding='utf-8').decode('utf-8'))

                                                        上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//div[@id="content"]/descendant::p')选择

                                                        的所有后代节点

                                                        ,并将其打印输出 。

                                                        XPath 函数 字符串函数 XPath 提供了一系列字符串函数,用于处理和操作字符串。例如,contains()函数用于判断一个字符串是否包含另一个子字符串 ,starts-with()函数用于判断一个字符串是否以另一个子字符串开头 。在我们的示例 HTML 文档中,若要选择href属性值以 “book” 开头的元素,可以使用以下 XPath 表达式:

                                                        //a[starts-with(@href, 'book')]

                                                        在这个表达式中,//a表示选择所有的元素,starts-with(@href, 'book')表示筛选出href属性值以 “book” 开头的元素 。在 Python 中,使用lxml库结合 XPath 来选择这些元素的代码示例如下:

                                                        from lxml import etree

                                                        假设html为读取的示例HTML文档内容

                                                        html = """

                                                        XPath示例页面

                                                        欢迎来到XPath学习页面

                                                        这是一个用于学习XPath的示例页面。

                                                        关于我们
                                                        """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择href属性值以“book”开头的元素 as_start_with_book = root.xpath('//a[starts-with(@href, "book")]') for a in as_start_with_book: print(etree.tostring(a, encoding='utf-8').decode('utf-8'))

                                                        上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//a[starts-with(@href, "book")]')选择href属性值以 “book” 开头的元素,并将其打印输出 。

                                                        数值函数 数值函数在 XPath 中用于处理数值类型的数据,比如sum()函数可以计算节点集合中数值的总和 ,floor()函数返回小于或等于给定数值的最大整数 。假设在一个电商订单的 XML 文档中,每个元素都有一个子元素表示商品价格,我们要计算所有商品的总价格,可以使用以下 XPath 表达式:

                                                        sum(//item/price)

                                                        在 Python 中,使用lxml库结合 XPath 来计算这个总价格的代码示例如下:

                                                        from lxml import etree

                                                        假设xml为读取的电商订单XML文档内容

                                                        xml = """ 29.9 49.5 19.8 """

                                                        将XML字符串解析为Element对象

                                                        root = etree.XML(xml)

                                                        使用XPath计算所有商品的总价格

                                                        total_price = root.xpath('sum(//item/price)') print(f"所有商品的总价格为: {total_price}")

                                                        上述代码中,首先将 XML 字符串解析为Element对象,然后通过xpath('sum(//item/price)')计算所有元素下元素数值的总和,并将结果打印输出 。

                                                        组合函数 在实际应用中,常常需要组合使用多个函数来实现复杂的筛选逻辑 。例如,在一个新闻网站的 HTML 页面中,每个新闻

                                                        元素包含一个

                                                        标题元素和一个

                                                        摘要元素,摘要元素的class属性值可能不同。如果我们要选择标题中包含 “人工智能” 且摘要长度大于 100 个字符的新闻

                                                        元素,可以使用以下 XPath 表达式:

                                                        //div[contains(.//h2/text(), '人工智能') and string-length(.//p/text()) > 100]

                                                        在这个表达式中,//div表示选择所有的

                                                        元素,contains(.//h2/text(), '人工智能')用于筛选出

                                                        标题元素文本中包含 “人工智能” 的
                                                        元素,string-length(.//p/text()) > 100用于筛选出

                                                        摘要元素文本长度大于 100 个字符的

                                                        元素 ,通过and连接两个条件,实现了复杂的筛选逻辑 。在 Python 中,使用lxml库结合 XPath 来选择这些元素的代码示例如下:

                                                        from lxml import etree

                                                        假设html为读取的新闻网站HTML页面内容

                                                        html = """

                                                        人工智能在医疗领域的新突破

                                                        人工智能技术近年来在医疗领域取得了显著进展,它正逐渐改变着疾病诊断、治疗方案制定等多个方面。随着大数据和机器学习算法的不断发展,人工智能能够处理海量的医疗数据,为医生提供更准确的诊断建议……

                                                        体育赛事精彩回顾

                                                        昨天的体育赛事精彩纷呈,各支队伍展开了激烈的角逐……

                                                        """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath选择标题中包含“人工智能”且摘要长度大于100个字符的新闻
                                                        元素 divs = root.xpath('//div[contains(.//h2/text(), "人工智能") and string-length(.//p/text()) > 100]') for div in divs: print(etree.tostring(div, encoding='utf-8').decode('utf-8'))

                                                        上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//div[contains(.//h2/text(), "人工智能") and string-length(.//p/text()) > 100]')选择符合条件的

                                                        元素,并将其打印输出 。

                                                        实际应用场景 为了更直观地展示 XPath 在实际中的应用,我们以一个电商网站的 HTML 页面为例,展示如何使用 XPath 提取各种信息。假设我们有如下 HTML 代码:

                                                        电商商品列表

                                                        商品1

                                                        59.99元

                                                        有货

                                                        商品2

                                                        79.99元

                                                        有货

                                                        商品3

                                                        49.99元

                                                        缺货

                                                        提取所有商品名称 使用 XPath 表达式提取所有商品名称:

                                                        //div[@class='product']/h3[@class='product-name']/text()

                                                        这个表达式首先通过//div[@class='product']定位到所有的商品

                                                        元素,然后在这些元素内部,通过h3[@class='product-name']定位到商品名称的

                                                        元素,最后使用text()获取其文本内容 。在 Python 中,使用lxml库结合 XPath 来提取这些商品名称的代码示例如下:

                                                        from lxml import etree

                                                        假设html为读取的电商网站HTML页面内容

                                                        html = """

                                                        电商商品列表

                                                        商品1

                                                        59.99元

                                                        有货

                                                        商品2

                                                        79.99元

                                                        有货

                                                        商品3

                                                        49.99元

                                                        缺货

                                                        """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath提取所有商品名称 product_names = root.xpath('//div[@class="product"]/h3[@class="product-name"]/text()') for name in product_names: print(name)

                                                        上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//div[@class="product"]/h3[@class="product-name"]/text()')提取所有商品名称,并将其打印输出 。

                                                        提取所有有货商品的价格 提取所有有货商品的价格:

                                                        //div[@class='product'][.//p[@class='stock' and text()='有货']]/p[@class='price']/text()

                                                        这个表达式中,//div[@class='product']先定位到所有商品

                                                        元素,然后通过[.//p[@class='stock' and text()='有货']]筛选出其中库存

                                                        元素文本为 “有货” 的商品

                                                        元素,最后通过p[@class='price']/text()获取这些有货商品的价格文本 。在 Python 中,使用lxml库结合 XPath 来提取这些有货商品价格的代码示例如下:

                                                        from lxml import etree

                                                        假设html为读取的电商网站HTML页面内容

                                                        html = """

                                                        电商商品列表

                                                        商品1

                                                        59.99元

                                                        有货

                                                        商品2

                                                        79.99元

                                                        有货

                                                        商品3

                                                        49.99元

                                                        缺货

                                                        """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath提取所有有货商品的价格 prices = root.xpath('//div[@class="product"][.//p[@class="stock" and text()="有货"]]/p[@class="price"]/text()') for price in prices: print(price)

                                                        上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//div[@class="product"][.//p[@class="stock" and text()="有货"]]/p[@class="price"]/text()')提取所有有货商品的价格,并将其打印输出 。

                                                        找出所有缺货商品 找出所有缺货商品:

                                                        //div[@class='product'][.//p[@class='stock' and text()='缺货']]

                                                        这个表达式通过//div[@class='product']定位到所有商品

                                                        元素,再通过[.//p[@class='stock' and text()='缺货']]筛选出其中库存

                                                        元素文本为 “缺货” 的商品

                                                        元素 。在 Python 中,使用lxml库结合 XPath 来找出这些缺货商品的代码示例如下:

                                                        from lxml import etree

                                                        假设html为读取的电商网站HTML页面内容

                                                        html = """

                                                        电商商品列表

                                                        商品1

                                                        59.99元

                                                        有货

                                                        商品2

                                                        79.99元

                                                        有货

                                                        商品3

                                                        49.99元

                                                        缺货

                                                        """ # 将HTML字符串解析为Element对象 root = etree.HTML(html) # 使用XPath找出所有缺货商品 out_of_stock_products = root.xpath('//div[@class="product"][.//p[@class="stock" and text()="缺货"]]') for product in out_of_stock_products: print(etree.tostring(product, encoding='utf-8').decode('utf-8'))

                                                        上述代码中,首先将 HTML 字符串解析为Element对象,然后通过xpath('//div[@class="product"][.//p[@class="stock" and text()="缺货"]]')找出所有缺货商品,并将其打印输出 。

                                                        获取导航菜单项 获取导航菜单项:

                                                        //nav[@class='main-nav']//a/text()

                                                        这个表达式通过//nav[@class='main-nav']定位到导航栏的