爬虫最快框架collyx,今天开源了...

2023-11-13

​作者:TheWeiJun

工欲善其事,必先利其器。大家好,我是TheWeiJun。之前接触colly时,写过一篇关于colly框架的文章。由于当时能力有限加上时间不够充足,一直没能够去研究这个框架。后来经过3个多月的不断尝试完善,基于colly迭代的collyx框架今天准备开源给各位粉丝朋友。希望各位朋友能够多多提出宝贵意见,在阅读的同时记得给我一个star!

特别声明:本公众号文章只作为学术研究,不用于其它不法用途;如有侵权请联系作者删除。

立即加星标

每天看好文

 目录

一、前言介绍

二、collyx框架

三、源码分享

四、实战展示

五、心得分享


开源展望

不知不觉写公众号已经1年了,记得当时和好朋友大壮兄开始写文章时,幻想着以后成为什么样的人。我一直信奉终身成长,今天我决定把自己研究多个月的框架开源分享给各位读者朋友们。希望大家能够一起成长、共同进步。技术总是在不停的迭代更新,我们不能止步不前。在不久的将来,希望能创造更多有价值的文章给各位读者朋友。


一、前言介绍

前言:colly 是 Go 实现的比较有名的一款爬虫框架,而且 Go 在高并发和分布式场景的优势也正是爬虫技术所需要的。它的主要特点是轻量、快速,设计非常优雅,并且分布式的支持也非常简单,易于扩展。

github地址: github.com/gocolly/colly

colly官网地址:http://go-colly.org/

从上图中,我们可以看出colly在github社区有着超高的人气。今天我们即将引出collyx爬虫框架,下面我将通过源码分享介绍这个框架给各位读者。


二、collyx框架介绍

框架简介:基于colly框架及net/http进行封装,实现的一款可配置分布式爬虫架构。使用者只需要配置解析、并发数、入库topic、请求方式、请求url等参数即可,其他代码类似于scrapy,不需要单独编写。

框架优势:实现了重试机制,各个功能可插拔,自定义解析模块、结构体模块等,抽象了调度模块,大大减少代码冗余,快速提高开发能力;其中对于feed流并发的爬虫也能够生效,不止基于深度优先爬虫;也可以用于广度优先。

collyx架构图预览:


三、源码分享

根据上面的架构图,我们可将框架分为6个组件,分别为:spiders、engine、items、downloader、pipelines、scheduler。下面,我们将从这几个部分逐一讲解collyx的整个源码,同时也将展示一部分extensions源码。完整目录如下:

1、spiders模块分享,自定义代码结构,代码如下所示:

// Package spiders ---------------------------
// @author    : TheWeiJun
package main
​
import (
  "collyx-spider/common"
  "collyx-spider/items/http"
  "collyx-spider/pipelines"
  "collyx-spider/spiders/crawler"
)
​
func main() {
  request := http.FormRequest{
    Url:         "https://xxxxx",
    Payload:     "xxxxx",
    Method:      "POST",
    RedisKey:    "ExplainingGoodsChan",
    RedisClient: common.LocalRedis,
    RedisMethod: "spop",
    Process:     pipelines.DemoParse,
    Topic:       "test",
  }
  crawler.Crawl(&request)
​
}

说明:只需要配置抓取的url、payload、method、redis、kafka等参数即可;如果某些参数不想使用,可以去掉。

2、engine模块源码如下,对colly进行初始化参数配置:

package engine
​
import (
  "collyx-spider/common"
  downloader2 "collyx-spider/downloader"
  extensions2 "collyx-spider/extensions"
  "collyx-spider/items/http"
  "collyx-spider/scheduler"
  "github.com/gocolly/colly"
  "time"
)
​
var Requests = common.GetDefaultRequests()
var TaskQueue = common.GetDefaultTaskQueue()
var Proxy = common.GetDefaultProxy()
var KeepAlive = common.GetDefaultKeepAlive()
var kafkaStatus = common.GetKafkaDefaultProducer()
var RequestChan = make(chan bool, Requests)
var TaskChan = make(chan interface{}, TaskQueue)
​
func CollyConnect(request *http.FormRequest) {
  var c = colly.NewCollector(
    colly.Async(true),
    colly.AllowURLRevisit(),
  )
  c.Limit(&colly.LimitRule{
    Parallelism: Requests,
    Delay:       time.Second * 3,
    RandomDelay: time.Second * 5,
  })
  if Proxy {
    extensions2.SetProxy(c, KeepAlive)
  }
  //if kafkaStatus {
  //  common.InitDefaultKafkaProducer()
  //}
  extensions2.URLLengthFilter(c, 10000)
  downloader2.ResponseOnError(c, RequestChan)
  downloader2.DownloadRetry(c, RequestChan)
  request.SetConnect(c)
  request.SetTasks(TaskChan)
  request.SetRequests(RequestChan)
}
​
func StartRequests(request *http.FormRequest) {
  /*add headers add parse*/
  go scheduler.GetTaskChan(request)
  if request.Headers != nil {
    request.Headers(request.Connect)
  } else {
    extensions2.GetHeaders(request.Connect)
  }
  downloader2.Response(request)
​
}

说明:该模块主要是做初始化调度器、请求headers扩展、初始化下载器、初始化colly等操作,是框架运行的重要模块之一。

3、scheduler模块源码展示,完整代码:

package scheduler
​
import (
  "collyx-spider/items/http"
  log "github.com/sirupsen/logrus"
  "strings"
  "time"
)
​
func GetTaskChan(request *http.FormRequest) {
  redisKey := request.RedisKey
  redisClient := request.RedisClient
  redisMethod := request.RedisMethod
  limits := int64(cap(request.TasksChan))
  TaskChan := request.TasksChan
  methodLowerStr := strings.ToLower(redisMethod)
  for {
    switch methodLowerStr {
    case "do":
      result, _ := redisClient.Do("qpop", redisKey, 0, limits).Result()
      searchList := result.([]interface{})
      if len(searchList) == 0 {
        log.Debugf("no task")
        time.Sleep(time.Second * 3)
        continue
      }
      for _, task := range searchList {
        TaskChan <- task
      }
    case "spop":
      searchList, _ := redisClient.SPopN(redisKey, limits).Result()
      if len(searchList) == 0 {
        log.Debugf("no task")
        time.Sleep(time.Second * 3)
        continue
      }
      for _, task := range searchList {
        TaskChan <- task
      }
    default:
      log.Info("Methods are not allowed.....")
    }
    time.Sleep(time.Second)
  }
}

说明:这里从spider里的结构体指针取值,获取任务交给TaskChan通道进行任务分发。

4、items模块源码展示

4.1 request_struct.go模块代码如下:

package http
​
import (
  "github.com/go-redis/redis"
  "github.com/gocolly/colly"
)
​
type FormRequest struct {
  Url          string
  Payload      string
  Method       string
  RedisKey     string
  RedisClient  *redis.Client
  RedisMethod  string
  GetParamFunc func(*FormRequest)
  Connect      *colly.Collector
  Process      func([]byte, string, string) string
  RequestChan  chan bool
  TasksChan    chan interface{}
  Topic        string
  Headers      func(collector *colly.Collector)
  TaskId       string
}
​
func (request *FormRequest) SetRequests(requests chan bool) {
  request.RequestChan = requests
}
​
func (request *FormRequest) SetTasks(tasks chan interface{}) {
  request.TasksChan = tasks
}
​
func (request *FormRequest) SetConnect(conn *colly.Collector) {
  request.Connect = conn
}
​
func (request *FormRequest) SetUrl(url string) {
  request.Url = url
}

总结:request结构体负责spiders请求自定义,设置初始化请求参数。

4.2 解析结构体,根据解析内容和保存内容自定义结构体,截图如下:

5、downloader模块分享,目录代码结构如下图所示:

总结:该模块拥有下载成功、下载错误、下载重试三个功能,接来下分享下源代码。

5.1 download_error.go代码如下:

package downloader
​
import (
  "github.com/gocolly/colly"
)
​
func ResponseOnError(c *colly.Collector, taskLimitChan chan bool) {
  c.OnError(func(r *colly.Response, e error) {
    defer func() {
      <-taskLimitChan
    }()
  })
​
  c.OnScraped(func(r *colly.Response) {
    defer func() {
      <-taskLimitChan
    }()
​
  })
}

模块说明:该模块为捕获错误请求,并及时释放并发通道。

5.2 download_ok.go代码如下:

package downloader
​
import (
  "collyx-spider/common"
  "collyx-spider/items/http"
  "github.com/gocolly/colly"
)
​
func Response(request *http.FormRequest) {
  c := request.Connect
  c.OnResponse(func(response *colly.Response) {
    defer common.CatchError()
    task := response.Ctx.Get("task")
    isNext := request.Process(response.Body, task, request.Topic)
    if isNext != "" {
      request.RedisClient.SAdd(request.RedisKey, isNext)
    }
  })
}

模块说明:该模块为处理200状态码请求,并将调用spiders提前定义的解析函数进行数据抽取。

5.3 download_rety.go代码如下:

package downloader
​
import (
  "collyx-spider/common"
  "github.com/gocolly/colly"
  "log"
)
​
func RetryFunc(c *colly.Collector, request *colly.Response, RequestChan chan bool) {
  url := request.Request.URL.String()
  body := request.Request.Body
  method := request.Request.Method
  ctx := request.Request.Ctx
  RequestChan <- true
  c.Request(method, url, body, ctx, nil)
}
​
func DownloadRetry(c *colly.Collector, RequestChan chan bool) {
  c.OnError(func(request *colly.Response, e error) {
    if common.CheckErrorIsBadNetWork(e.Error()) {
      taskId := request.Request.Ctx.Get("task")
      log.Printf("Start the retry task:%s", taskId)
      RetryFunc(c, request, RequestChan)
    }
  })
​
}

模块说明:通过自定义的错误函数捕获错误类型,并开启重试机制进行重试,弥补了colly请求失败数据缺失问题。

6、pipelines模块分享,完整代码如下:

package pipelines
​
import (
  "collyx-spider/common"
  "collyx-spider/items"
  "encoding/json"
  log "github.com/sirupsen/logrus"
)
​
func DemoParse(bytes []byte, task, topic string) string {
  item := items.Demo{}
  json.Unmarshal(bytes, &item)
  Promotions := item.Promotions
  if Promotions != nil {
    data := Promotions[0].BaseInfo.Title
    proId := Promotions[0].BaseInfo.PromotionId
    common.KafkaDefaultProducer.AsyncSendWithKey(task, topic, data+proId)
    log.Println(data, Promotions[0].BaseInfo.PromotionId, topic)
  } else {
    log.Println(Promotions)
  }
  return ""
}

模块说明:pipelines在框架中职位主要是负责数据解析、数据持久化操作。

7、cralwer模块分享,代码如下:

package crawler
​
import (
  "collyx-spider/common"
  "collyx-spider/engine"
  "collyx-spider/items/http"
  "fmt"
  "github.com/gocolly/colly"
  log "github.com/sirupsen/logrus"
  "strings"
  "time"
)
​
func MakeRequestFromFunc(request *http.FormRequest) {
  for true {
    select {
    case TaskId := <-request.TasksChan:
      ctx := colly.NewContext()
      ctx.Put("task", TaskId)
      request.TaskId = TaskId.(string)
      if request.Method == "POST" {
        request.GetParamFunc(request)
        if strings.Contains(TaskId.(string), ":") {
          split := strings.Split(TaskId.(string), ":")
          TaskId = split[0]
          data := fmt.Sprintf(request.Payload, TaskId)
          ctx.Put("data", data)
          request.Connect.Request(request.Method, request.Url, strings.NewReader(data), ctx, nil)
        }
        request.RequestChan <- true
      } else {
        if strings.Contains(TaskId.(string), "http") {
          request.Url = TaskId.(string)
        } else {
          request.GetParamFunc(request)
        }
        request.Connect.Request(request.Method, request.Url, nil, ctx, nil)
        request.RequestChan <- true
      }
    default:
      time.Sleep(time.Second * 3)
      log.Info("TaskChan not has taskId")
    }
  }
}
​
func MakeRequestFromUrl(request *http.FormRequest) {
  for true {
    select {
    case TaskId := <-request.TasksChan:
      ctx := colly.NewContext()
      ctx.Put("task", TaskId)
      if request.Method == "POST" {
        payload := strings.NewReader(fmt.Sprintf(request.Payload, TaskId))
        request.Connect.Request(request.Method, request.Url, payload, ctx, nil)
      } else {
        fmt.Println(fmt.Sprintf(request.Url, TaskId))
        request.Connect.Request(request.Method, fmt.Sprintf(request.Url, TaskId), nil, ctx, nil)
      }
      request.RequestChan <- true
    default:
      time.Sleep(time.Second * 3)
      log.Info("TaskChan not has taskId.......")
    }
  }
}
​
func RequestFromUrl(request *http.FormRequest) {
  if request.GetParamFunc != nil {
    MakeRequestFromFunc(request)
  } else {
    MakeRequestFromUrl(request)
  }
}
​
func Crawl(request *http.FormRequest) {
  /*making requests*/
  engine.CollyConnect(request)
  engine.StartRequests(request)
  go RequestFromUrl(request)
  common.DumpRealTimeInfo(len(request.RequestChan))
}

总结:cralwer模块主要负责engine初始化及engine信号发送,驱动整个爬虫项目运行。

8、extensions模块,代码目录截图如下:

8.1 AddHeaders.go 源码如下:

package extensions
​
import (
  "fmt"
  "github.com/gocolly/colly"
  "math/rand"
)
​
var UaGens = []func() string{
  genFirefoxUA,
  genChromeUA,
}
​
var ffVersions = []float32{
  58.0,
  57.0,
  56.0,
  52.0,
  48.0,
  40.0,
  35.0,
}
​
var chromeVersions = []string{
  "65.0.3325.146",
  "64.0.3282.0",
  "41.0.2228.0",
  "40.0.2214.93",
  "37.0.2062.124",
}
​
var osStrings = []string{
  "Macintosh; Intel Mac OS X 10_10",
  "Windows NT 10.0",
  "Windows NT 5.1",
  "Windows NT 6.1; WOW64",
  "Windows NT 6.1; Win64; x64",
  "X11; Linux x86_64",
}
​
func genFirefoxUA() string {
  version := ffVersions[rand.Intn(len(ffVersions))]
  os := osStrings[rand.Intn(len(osStrings))]
  return fmt.Sprintf("Mozilla/5.0 (%s; rv:%.1f) Gecko/20100101 Firefox/%.1f", os, version, version)
}
​
func genChromeUA() string {
  version := chromeVersions[rand.Intn(len(chromeVersions))]
  os := osStrings[rand.Intn(len(osStrings))]
  return fmt.Sprintf("Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", os, version)
}
​
func GetHeaders(c *colly.Collector) {
  c.OnRequest(func(r *colly.Request) {
    r.Headers.Set("User-Agent", UaGens[rand.Intn(len(UaGens))]())
  })
}

模块说明:负责更换随机ua,防止spider被网站gank。

8.2 AddProxy.go 源码如下:

package extensions
​
import (
  "collyx-spider/common"
  "github.com/gocolly/colly"
  "github.com/gocolly/colly/proxy"
)
​
func SetProxy(c *colly.Collector, KeepAlive bool) {
  proxyList := common.RefreshProxies()
  if p, err := proxy.RoundRobinProxySwitcher(
    proxyList...,
  ); err == nil {
    c.SetProxyFunc(p)
  }
}

模块说明:该模块主要是给request设置代理,防止出现请求失败等错误。

8.3 URLLengthFilter.go 源码分享:

package extensions
​
import "github.com/gocolly/colly"
​
func URLLengthFilter(c *colly.Collector, URLLengthLimit int) {
  c.OnRequest(func(r *colly.Request) {
    if len(r.URL.String()) > URLLengthLimit {
      r.Abort()
    }
  })
}

模块说明:对于url过长请求进行丢弃,Abort在OnRequest回调中取消HTTP请求。源码分享环节到这里就结束了,接下来我们运行代码,展示一下collyx爬虫框架性能吧!


四、框架demo展示

1、启动编辑好的案例代码,运行截图如下:

总结:爬虫运行5分钟后,在代理足够充足情况下统计,抓取该网站每分钟约产生2000条数据,可以毫不吹牛的说,这是我迄今为止见过最快的爬虫框架。


五、心得分享

今天分享到这里就结束了,对于collyx框架而言还有很长的路要走。我始终觉得只要努力,我们就会朝着目标一步步去实现。最后,感谢大家耐心阅读本文!

今天分享到这里就结束了,欢迎大家关注下期文章,我们不见不散⛽️

往期推荐

GoLang - colly爬虫框架

某站弹幕Protobuf协议逆向分析  |  Go语言版本

某游戏社区App | So层逆向分析

某容器管理平台模拟登录(Go语言版本)

某麦最新analysis算法逆向分析(多种语言实现)

作者简介

我是TheWeiJun,有着执着的追求,信奉终身成长,不定义自己,热爱技术但不拘泥于技术,爱好分享,喜欢读书和乐于结交朋友,欢迎加我微信与我交朋友。

分享日常学习中关于爬虫、逆向和分析的一些思路,文中若有错误的地方,欢迎大家多多交流指正☀️

文章来源:逆向与爬虫的故事(微信公众号)

原文链接:爬虫最快框架collyx,今天开源了...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

爬虫最快框架collyx,今天开源了... 的相关文章

  • 为神经网络打乱两个 numpy 数组

    我有两个 numpy 数组用于输入数据 X 和输出数据 y X np array 2 3 sample 1 x 16 4 dtype float sample 2 x y np array 1 0 sample 1 y 0 1 dtype
  • 在推送到容器注册表之前如何对构建的映像运行测试?

    从 gitlab 文档中可以看出如何使用 kaniko 创建 docker 镜像 build stage build image name gcr io kaniko project executor debug entrypoint sc
  • 高效地将大型 Pandas 数据帧写入磁盘

    我正在尝试找到使用 Python Pandas 高效地将大型数据帧 250MB 写入磁盘或从磁盘写入的最佳方法 我已经尝试了所有方法Python 数据分析 但表现却非常令人失望 这是一个更大项目的一部分 该项目探索将我们当前的分析 数据管理
  • Python设置1和True的解释

    在 IPython 3 交互式 shell 中 In 53 set2 1 2 True hello In 54 len set2 Out 54 3 In 55 set2 Out 55 hello True 2 是因为 1 和 True 得到
  • 如何使用我自己的自定义表单覆盖 django-rest-auth 中的表单?

    我正在使用 django rest auth 并尝试通过覆盖表单的方法之一来修复密码重置视图中的错误 尽管我已经使用不同的 django rest auth 表单成功完成了类似的操作 但我无法让它在这个表单上工作 无论我做什么 都会使用旧的
  • 如何在特定文件夹中运行 shell 命令

    我可以用这个out err exec Command git log Output 获取将在与可执行位置相同的路径中运行的命令的输出 如何指定要在哪个文件夹中运行命令 exec Command https golang org pkg os
  • 如何检索分配给 Django 中的组的所有权限

    我正在执行一项任务来检索分配给 Django 中的组的一组权限 我可以使用以下代码获取创建的组 但无法使用它来获取分配给它们的权限 from django contrib auth models import Group Permissio
  • 绝对导入不起作用,但相对导入起作用

    这是我的应用程序结构 foodo setup py foodo init py foodo py models py foodo foodo foodo py从导入类models py module from foodo models im
  • 如何使用 python、openCV 计算图像中的行数

    我想数纸张 所以我正在考虑使用线条检测 我尝试过一些方法 例如Canny HoughLines and FLD 但我只得到处理过的照片 我不知道如何计算 有一些小线段就是我们想要的线 我用过len lines or len contours
  • Python函数组成

    我尝试使用良好的语法来实现函数组合 这就是我所得到的 from functools import partial class compfunc partial def lshift self y f lambda args kwargs s
  • 当我从本地计算机更改为虚拟主机时,从 python 脚本调用 pdftotext 不起作用

    我编写了一个小的 python 脚本来解析 提取 PDF 中的信息 我在本地机器上测试了它 我有 python 2 6 2 和 pdftotext 版本 0 12 4 我正在尝试在我的虚拟主机服务器 dreamhost 上运行它 它有 py
  • Bokeh 中单独的节点和边缘悬停工具?

    我正在尝试为 Bokeh 中的节点和边缘获取单独的悬停工具提示 但未能使其正常工作 有人可以指出我做错了什么吗 我相信代码应该如下所示 from bokeh io import show output notebook from bokeh
  • 一个类似 dict 的 Python 类

    我想编写一个自定义类 其行为类似于dict 所以 我继承自dict 不过 我的问题是 我是否需要创建一个私有的dict我的成员 init 方法 我不明白这个有什么意义 因为我已经有了dict如果我只是继承自的行为dict 谁能指出为什么大多
  • 在 pygame 中,我如何创建一个数据结构来跟踪调整大小事件和对象的坐标?

    我希望在调整屏幕大小后使鼠标事件与对象保持同步 有人告诉我需要创建一个数据结构来跟踪 调整事件大小 新坐标以匹配调整大小 如何使用简单的代数方程来完成此操作并将其集成到调整大小事件中以进行准确更新 反过来做 创建一个虚拟游戏地图 在绘制场景
  • 错误:尝试使用 scrappy 登录时出现 raise ValueError("No element found in %s" % response)

    问题描述 我想从我大学的bbs上抓取一些信息 这是地址 http bbs byr cn http bbs byr cn下面是我的蜘蛛的代码 from lxml import etree import scrapy try from scra
  • 将 Python Selenium 输出写入 Excel

    我编写了一个脚本来从在线网站上抓取产品信息 目标是将这些信息写入 Excel 文件 由于我的Python知识有限 我只知道如何在Powershell中使用Out file导出 但结果是每个产品的信息都打印在不同的行上 我希望每种产品都有一条
  • 我可以在 if 语句中使用“as”机制吗

    是否可以使用as in if类似的声明with我们使用的 例如 with open tmp foo r as ofile do something with ofile 这是我的代码 def my list rtrn lst True if
  • 更新 matplotlib 中颜色条的范围

    我想更新一个contourf在函数内绘制 效果很好 然而 数据的范围发生了变化 因此我还必须更新颜色条 这就是我未能做到的地方 请参阅以下最小工作示例 import matplotlib pyplot as plt import numpy
  • 无法将matplotlib安装到pycharm

    我最近开始使用Python速成课程学习Python编程 我陷入困境 因为我无法让 matplotlib 在 pycharm 中工作 我已经安装了pip 我已经通过命令提示符使用 pip 安装了 matplotlib 现在 当我打开 pych
  • 在不同的 GPU 上同时训练多个 keras/tensorflow 模型

    我想在 Jupyter Notebook 中同时在多个 GPU 上训练多个模型 我正在使用 4GPU 的节点上工作 我想将一个 GPU 分配给一个模型并同时训练 4 个不同的模型 现在 我通过 例如 为一台笔记本选择 GPU import

随机推荐

  • tomcat中间件的默认端口号_tomcat默认端口号(三个tomcat端口号)

    tomcat默认端口号 三个tomcat端口号 2020 05 08 10 43 21 共10个回答 Tomcat的默认端口号是多少 您好 提问者 Tomcat的默认端口号是 8080 weblogic的默认端口号是 7001 tomcat
  • 【机器学习笔记1】一元线性回归模型及预测

    目录 什么是线性回归模型 一元线性回归模型 问题引入 问题解析 代价函数 损失函数 代价函数的图像 为什么不是最小而是极小值 梯度下降算法 梯度下降算法公式 对于一元线性回归模型 学习率a的选择 关于梯度下降每一步的变化 补充 代码部分 案
  • SpringBoot整合邮箱验证(典中典)

    大体思路 先生成一个六位随机验证码并存起来 调用邮箱接口发送验证码 将用户输入的验证码和之前保存的验证码进行比对 目录 大体思路 第一步 开启SMTP服务 简单邮件传输协议 第二步 在项目中导入相关依赖 第三步 在配置文件里进行相关配置 第
  • CVE-2022-30190 MSDT远程代码执行漏洞复现

    目录 0x01 声明 0x02 简介 0x03 漏洞概述 0x04 影响版本 0x05 环境搭建 0x06 漏洞复现 是否存在利用点 CMD执行 生成docx文件利用 0x07 CS上线 启动CS服务端 CS客户端连接 设置监听 生成攻击e
  • JAVA 关于static中静态代码块的使用

    与一般静态方法的比较 一般情况下 如果有些代码必须在项目启动的时候就执行的时候 需要使用静态代码块 这种代码是主动执行的 需要在项目启动的时候就初始化 两者的区别就是 静态代码块是自动执行的 静态方法是被调用的时候才执行的 静态方法可以用类
  • 【多线程】ThreadLocal

    目录 简介 底层 set get 回收 简介 线程变量 以ThreadLocal为键 任意对象为值的结构 这个结构被附带在线程上 一个线程根据一个ThreadLocal对象查询到绑定在这个线程上的一个值 本地线程 线程的局部变量 只有当前线
  • 学习少儿编程成为一种必然趋势

    AI人工智能和少儿编程一直是大家热议的话题 在政策引领下 一些城市把人工智能带入中小学教材当中 格物斯坦小坦克认为从编程思维入手 让孩子养成清晰明朗的逻辑思维 在学习 做事各个方面 孩子将来都会得心应手 Scratch编程与其他代码编程 最
  • DS静态查找之折半查找

    题目描述 给出一个队列和要查找的数值 找出数值在队列中的位置 队列位置从1开始 要求使用折半查找算法 输入 第一行输入n 表示队列有n个数据 第二行输入n个数据 都是正整数 用空格隔开 第三行输入t 表示有t个要查找的数值 第四行起 输入t
  • 抓包基本命令

    一 概述 在一个A应用程序内数据有不同的格式如 Integer String等 但是通过网络将数据传输给B应用程序 那么在到达B应用程序之前 数据都将统一解析成数据包 也就是二进制串在网络中传输 在B应用程序前布置一个 网 在这个数据包到达
  • Linux: USB Gadget 驱动简介

    文章目录 1 前言 2 背景 3 USB Gadget 驱动 3 1 什么是 USB Gadget 驱动 3 2 USB Gadget 驱动框架 3 3 USB 设备控制器 UDC 驱动 3 3 1 USB 设备控制器 UDC 驱动 概述
  • Eggjs 从放弃到开始使用

    原文 codesky me archives eg 用掘金刊登虽然分流了但是主要是 现在分享的曝光率实在太低了 所以 支持的请点下原博收藏下关注下以及我的微博 咦 这篇文章标题为什么反了 实际上这是个人走过的心路历程 最初看到 eggjs
  • FastDFS的Tracker及Storage节点添加及删除

    1 增加Storage节点 通过配置 自动加入 1 安装Storage并配置mod fastdfs conf及storage conf 设置fdfs storaged及nginx自启动 2 启动新加的storage节点 会自动同步相同gro
  • openGL之API学习(四)纹理操作

    纹理操作代码流程 向着色器传递纹理单元 glUniform1i gSampler 0 向GPU上传纹理数据 GLuint m textureObj glGenTextures 1 m textureObj 生成一个纹理对象 一个纹理对象有多
  • 谁会嫌钱多啊,最适合打工人小白的Python兼职攻略以及接私活经验!

    这次小编想谈谈一个非常热门的话题 就是如何在学习python的同时去赚钱 在这篇文章中 你会学习到如何通过学习python来赚取副业收入 相信大家都对钱感兴趣吧 如果你和马云爸爸对钱不敢兴趣的话 那这篇文章就不适合你了 如果你想知道如何使用
  • 计算机英语-基础知识

    计算机专业英语基础知识 1 专业英语的专业性和客观性 科技文章属于严肃的书面语体 崇尚严谨周密 逻辑性强 要求层次分明 重点突出 各个领域的专业英语都以表达科技概念 理论和事实为主要目的 因此 它们很注重客观事实和真相 要求逻辑性强 条理规
  • APISIX源码解析-插件-客户端IP【real-ip】

    real ip 客户端IP插件 关键属性 源码解析 real ip 插件用于动态改变传递到 APISIX 的客户端的 IP 和端口 local function get addr conf ctx if conf source http x
  • 卷运维不如卷网络安全

    最近发现很多从事运维的选择了辞职 重新规划自己的职业发展方向 运维工程师这个岗位在IT行业里面确实是处于最底层的 不管什么环节出现问题 基本都是运维背锅 背锅也就罢了 薪资水平也比不上别的岗位 一般运维的薪资水平大多数都是6 9K 还要高频
  • 【Rust】用RefCell避开`&mut XX`导致的借用检查

    derive Debug struct WhatAboutThis lt a gt name String nickname Option lt a str gt impl lt a gt WhatAboutThis lt a gt fn
  • 什么是本地储存?

    本地储存的作用 把一些数据记录在浏览器中 是浏览器提供给我们的一些本地存储数据的机制 localStorage 永久缓存 除非手动删除 sessionStorage 会话缓存 关闭浏览器就没有了 共同点 只能存储字符串格式的数据 local
  • 爬虫最快框架collyx,今天开源了...

    作者 TheWeiJun 工欲善其事 必先利其器 大家好 我是TheWeiJun 之前接触colly时 写过一篇关于colly框架的文章 由于当时能力有限加上时间不够充足 一直没能够去研究这个框架 后来经过3个多月的不断尝试完善 基于col