collyソース学習
7972 ワード
collyソース学習
collyはgolangが書いたネット爬虫類です.とても使いやすいです.ソースコードを見てみると、品質もとてもいいです.本文はそのソースコードを読んでみましょう.
使用例
Visitから
まず、爬虫類を作るには、構造体Collectorが必要です.すべての論理はこのCollectorをめぐって行われています.
このCollectorはURLを「登る」ときにCollector.Visitメソッドを使用します.このVisitメソッドには、具体的にはいくつかのステップがあります. Request を組み立てる Response を取得 Response解析HTML/XML 終了ページキャプチャ どのステップでもエラーが発生する可能性があります collyは、各ステップで実行する必要がある論理を作成することができます.そして、この論理は必ずしも単一ではなく、複数であってもいいです.たとえば、Responseで取得が完了し、HTMLとして解析した後、OnHtmlを使用してロジックを追加することができます.これも私たちが最もよく使う関数です.その実現原理は以下の通りである.
全体としてこのコードのパターンは巧みだと思いますが、簡単に言えば構造体にコールバック関数を格納し、コールバック関数の登録をOnXXXで開放し、内部で適切な場所でコールバック関数のネスト実行を行います.
このコードモードは完全に覚えられ,適切なシーンは論理を注入する必要があり,クラスライブラリの拡張性を高めることができる.
例えば、ORMを設計して、SaveやUpdateのときにいくつかの論理を注入したいと思っています.このコードモードを使用すると、大体このような論理になります.
Collectorのコンポーネントモデル
ColllyのCollectorの作成も興味深いので、新しい方法を見てみましょう.
パラメータは、関数func(*Collector)を返す可変配列です.そしてそのコンポーネントをパラメータとしてNew関数で定義することができます.
この設計モードはコンポーネント化された需要シーンに適しています.バックグラウンドに異なるコンポーネントがある場合は、必要に応じてこれらのコンポーネントをロードします.基本的には、この論理を参照することができます.
CollectorのDebuggerロジック
Collectorの作成は完了しますが、さまざまな場所で「デバッグ」が必要です.ここでのデバッグcollyは、ログ記録でもWebを開いてリアルタイムで表示できるように設計されています.
これはどうやってできたのですか?イベントモデルも巧みに使われています.
基本的にコアコードは以下の通りです.
Debuggerのインタフェースを設計して、中のInitは実は必要に応じて存在するかどうか、最も核心的なのはEvent関数で、それは1つのEvent構造のポインタを受信して、すべてのデバッグ情報の関連するデバッグのタイプ、デバッグの要求ID、デバッグの情報などはすべてこのEventの中に存在することができます.
記録が必要な場所でEventイベントを作成しdebuggerでデバッガに出力します.
collyのdebuggerにはサプライズがあります.それはweb方式の表示をサポートしています.私たちは中のdebug/webdebugger.goを見ています.
見ましたか.ポイントはInit関数でhttp serverを起動し、Eventで現在の情報を収集し、あるルートhandlerでwebに表示することです.
このデザインは他の様々なLoggerのデザインよりも少し優れているような気がします.
まとめ
collyコードを見ると、基本的にコードは非常にはっきりしていて、複雑ではありません.上の3つの点で分かったと思いますが、基本的にこの爬虫類フレームワークのアーキテクチャ設計ははっきりしていて、残りは具体的なコード実装の部分で、ゆっくり見ることができます.
collyのフレームワーク全体が洗練されていて、くだらない話や過剰な設計はありません.この構造として定義されている場所は構造として定義されています.例えば、Colletor、ここでは複雑なCollectorインタフェースとして設計されていません.しかし、このインタフェースとして定義されている場所、例えばDebuggerは、インタフェースとして定義されています.またcollyも利用者の拡張性を十分に考慮している.いくつかのOnXXXプロセスとコールバック関数の設計も非常に合理的です.
collyはgolangが書いたネット爬虫類です.とても使いやすいです.ソースコードを見てみると、品質もとてもいいです.本文はそのソースコードを読んでみましょう.
使用例
func main() {
c := colly.NewCollector()
// Find and visit all links
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
e.Request.Visit(e.Attr("href"))
})
c.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting", r.URL)
})
c.Visit("http://go-colly.org/")
}
Visitから
まず、爬虫類を作るには、構造体Collectorが必要です.すべての論理はこのCollectorをめぐって行われています.
このCollectorはURLを「登る」ときにCollector.Visitメソッドを使用します.このVisitメソッドには、具体的にはいくつかのステップがあります.
type HTMLCallback func(*HTMLElement)
type htmlCallbackContainer struct {
Selector string
Function HTMLCallback
}
type Collector struct {
...
htmlCallbacks []*htmlCallbackContainer // htmlCallbacks HTML
...
}
// , htmlCallbackContainer, DOM ,
func (c *Collector) OnHTML(goquerySelector string, f HTMLCallback) {
...
if c.htmlCallbacks == nil {
c.htmlCallbacks = make([]*htmlCallbackContainer, 0, 4)
}
c.htmlCallbacks = append(c.htmlCallbacks, &htmlCallbackContainer{
Selector: goquerySelector,
Function: f,
})
...
}
// HTML DOM , htmlCallbacks
func (c *Collector) handleOnHTML(resp *Response) error {
...
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(resp.Body))
...
for _, cc := range c.htmlCallbacks {
i := 0
doc.Find(cc.Selector).Each(func(_ int, s *goquery.Selection) {
for _, n := range s.Nodes {
e := NewHTMLElementFromSelectionNode(resp, s, n, i)
...
cc.Function(e)
}
})
}
return nil
}
// Visit , handleOnHTML 。
func (c *Collector) fetch(u, method string, depth int, requestData io.Reader, ctx *Context, hdr http.Header, req *http.Request) error {
...
err = c.handleOnHTML(response)
...
return err
}
全体としてこのコードのパターンは巧みだと思いますが、簡単に言えば構造体にコールバック関数を格納し、コールバック関数の登録をOnXXXで開放し、内部で適切な場所でコールバック関数のネスト実行を行います.
このコードモードは完全に覚えられ,適切なシーンは論理を注入する必要があり,クラスライブラリの拡張性を高めることができる.
例えば、ORMを設計して、SaveやUpdateのときにいくつかの論理を注入したいと思っています.このコードモードを使用すると、大体このような論理になります.
// ,
type SaveCallback func(*Resource)
type UpdateCallback func(string, *Resource)
type UpdateCallbackContainer struct {
Id string
Function UpdateCallback
}
type Resource struct {
Id string
saveCallbacks []SaveCallback
updateCallbacks []*UpdateCallbackContainer
}
func (r *Resource) OnSave(f SaveCallback) {
if r.saveCallbacks == nil {
r.saveCallbacks = make([]SaveCallback, 0, 4)
}
r.saveCallbacks = append(r.saveCallbacks, f)
}
func (r *Resource) Save() {
// Do Something
if r.saveCallbacks != nil {
for _, f := range r.saveCallbacks {
f(r)
}
}
}
func (r *Resource) OnUpdate(id string, f UpdateCallback) {
if r.updateCallbacks == nil {
r.updateCallbacks = make([]*UpdateCallbackContainer, 0, 4)
}
r.updateCallbacks = append(r.updateCallbacks, &UpdateCallbackContainer{ id, f})
}
func (r *Resource) Update() {
// Do something
id := r.Id
if r.updateCallbacks != nil {
for _, c := range r.updateCallbacks {
c.Function(id, r)
}
}
}
Collectorのコンポーネントモデル
ColllyのCollectorの作成も興味深いので、新しい方法を見てみましょう.
func NewCollector(options ...func(*Collector)) *Collector {
c := &Collector{}
c.Init()
for _, f := range options {
f(c)
}
...
return c
}
func UserAgent(ua string) func(*Collector) {
return func(c *Collector) {
c.UserAgent = ua
}
}
func main() {
c := NewCollector(
colly.UserAgent("Chrome")
)
}
パラメータは、関数func(*Collector)を返す可変配列です.そしてそのコンポーネントをパラメータとしてNew関数で定義することができます.
この設計モードはコンポーネント化された需要シーンに適しています.バックグラウンドに異なるコンポーネントがある場合は、必要に応じてこれらのコンポーネントをロードします.基本的には、この論理を参照することができます.
type Admin struct {
SideBar string
}
func NewAdmin(options ...func(*Admin)) *Admin {
ad := &Admin{}
for _, f := range options {
f(ad)
}
return ad
}
func SideBar(sidebar string) func(*Admin) {
return func(admin *Admin) {
admin.SideBar = sidebar
}
}
CollectorのDebuggerロジック
Collectorの作成は完了しますが、さまざまな場所で「デバッグ」が必要です.ここでのデバッグcollyは、ログ記録でもWebを開いてリアルタイムで表示できるように設計されています.
これはどうやってできたのですか?イベントモデルも巧みに使われています.
基本的にコアコードは以下の通りです.
package admin
import (
"io"
"log"
)
type Event struct {
Type string
RequestID int
Message string
}
type Debugger interface {
Init() error
Event(*Event)
}
type LogDebugger struct {
Output io.Writer
logger *log.Logger
}
func (l *LogDebugger) Init() error {
l.logger = log.New(l.Output, "", 1)
return nil
}
func (l *LogDebugger) Event(e *Event) {
l.logger.Printf("[%6d - %s] %q
", e.RequestID, e.Type, e.Message)
}
func createEvent( requestID, collectorID uint32) *debug.Event {
return &debug.Event{
RequestID: requestID,
Type: eventType,
}
}
c.debugger.Event(createEvent("request", r.ID, c.ID, map[string]string{
"url": r.URL.String(),
}))
Debuggerのインタフェースを設計して、中のInitは実は必要に応じて存在するかどうか、最も核心的なのはEvent関数で、それは1つのEvent構造のポインタを受信して、すべてのデバッグ情報の関連するデバッグのタイプ、デバッグの要求ID、デバッグの情報などはすべてこのEventの中に存在することができます.
記録が必要な場所でEventイベントを作成しdebuggerでデバッガに出力します.
collyのdebuggerにはサプライズがあります.それはweb方式の表示をサポートしています.私たちは中のdebug/webdebugger.goを見ています.
type WebDebugger struct {
Address string
initialized bool
CurrentRequests map[uint32]requestInfo
RequestLog []requestInfo
}
type requestInfo struct {
URL string
Started time.Time
Duration time.Duration
ResponseStatus string
ID uint32
CollectorID uint32
}
func (w *WebDebugger) Init() error {
...
if w.Address == "" {
w.Address = "127.0.0.1:7676"
}
w.RequestLog = make([]requestInfo, 0)
w.CurrentRequests = make(map[uint32]requestInfo)
http.HandleFunc("/", w.indexHandler)
http.HandleFunc("/status", w.statusHandler)
log.Println("Starting debug webserver on", w.Address)
go http.ListenAndServe(w.Address, nil)
return nil
}
func (w *WebDebugger) Event(e *Event) {
switch e.Type {
case "request":
w.CurrentRequests[e.RequestID] = requestInfo{
URL: e.Values["url"],
Started: time.Now(),
ID: e.RequestID,
CollectorID: e.CollectorID,
}
case "response", "error":
r := w.CurrentRequests[e.RequestID]
r.Duration = time.Since(r.Started)
r.ResponseStatus = e.Values["status"]
w.RequestLog = append(w.RequestLog, r)
delete(w.CurrentRequests, e.RequestID)
}
}
見ましたか.ポイントはInit関数でhttp serverを起動し、Eventで現在の情報を収集し、あるルートhandlerでwebに表示することです.
このデザインは他の様々なLoggerのデザインよりも少し優れているような気がします.
まとめ
collyコードを見ると、基本的にコードは非常にはっきりしていて、複雑ではありません.上の3つの点で分かったと思いますが、基本的にこの爬虫類フレームワークのアーキテクチャ設計ははっきりしていて、残りは具体的なコード実装の部分で、ゆっくり見ることができます.
collyのフレームワーク全体が洗練されていて、くだらない話や過剰な設計はありません.この構造として定義されている場所は構造として定義されています.例えば、Colletor、ここでは複雑なCollectorインタフェースとして設計されていません.しかし、このインタフェースとして定義されている場所、例えばDebuggerは、インタフェースとして定義されています.またcollyも利用者の拡張性を十分に考慮している.いくつかのOnXXXプロセスとコールバック関数の設計も非常に合理的です.