func (ctx *Context) VisitWithContext(URL string) error {
	return ctx.RequestWithContext("GET", ctx.AbsoluteURL(URL), nil, nil)
}
VisitWithContext issues a GET to the specified URL with current context
package spider
import (
	"bytes"
	"context"
	"database/sql"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"github.com/gocolly/colly"
	"github.com/nange/gospider/common"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)
type key int
const reqContexKey key = 0
type Context struct {
	task  *Task
	c     *colly.Collector
	nextC *colly.Collector
	ctlCtx    context.Context
	ctlCancel context.CancelFunc
	collyContext *colly.Context
	outputDB       *sql.DB
	outputCSVFiles map[string]io.WriteCloser
}
func newContext(ctx context.Context, cancel context.CancelFunc, task *Task, c *colly.Collector, nextC *colly.Collector) (*Context, error) {
	gsCtx := &Context{
		task:      task,
		c:         c,
		nextC:     nextC,
		ctlCtx:    ctx,
		ctlCancel: cancel,
	}
	if task.OutputConfig.Type == common.OutputTypeCSV {
		gsCtx.outputCSVFiles = make(map[string]io.WriteCloser)
		csvConf := task.OutputConfig.CSVConf
		if task.OutputToMultipleNamespace {
			for ns, conf := range task.MultipleNamespaceConf {
				csvname := fmt.Sprintf("%s.csv", ns)
				if err := createCSVFileIfNeeded(csvConf.CSVFilePath, csvname, conf.OutputFields); err != nil {
					return nil, err
				}
				outputPath := path.Join(csvConf.CSVFilePath, csvname)
				csvfile, err := os.OpenFile(outputPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
				if err != nil {
					return nil, errors.Wrapf(err, "open csv file [%s] failed", csvname)
				}
				gsCtx.outputCSVFiles[ns] = csvfile
			}
		} else {
			csvname := fmt.Sprintf("%s.csv", task.Namespace)
			if err := createCSVFileIfNeeded(csvConf.CSVFilePath, csvname, task.OutputFields); err != nil {
				return nil, err
			}
			outputPath := path.Join(csvConf.CSVFilePath, csvname)
			csvfile, err := os.OpenFile(outputPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
			if err != nil {
				return nil, errors.Wrapf(err, "open csv file [%s] failed", csvname)
			}
			gsCtx.outputCSVFiles[task.Namespace] = csvfile
		}
	}
	return gsCtx, nil
}
func (ctx *Context) cloneWithReq(req *colly.Request) *Context {
	newctx := context.WithValue(ctx.ctlCtx, reqContexKey, req)
	return &Context{
		task:           ctx.task,
		c:              ctx.c,
		nextC:          ctx.nextC,
		ctlCtx:         newctx,
		ctlCancel:      ctx.ctlCancel,
		outputDB:       ctx.outputDB,
		outputCSVFiles: ctx.outputCSVFiles,
	}
}
func (ctx *Context) setOutputDB(db *sql.DB) {
	ctx.outputDB = db
}
func (ctx *Context) closeCSVFileIfNeeded() {
	if len(ctx.outputCSVFiles) == 0 {
		return
	}
	for ns, closer := range ctx.outputCSVFiles {
		log.Debugf("closing csv file [%s]", ns+".csv")
		closer.Close()
	}
}
func (ctx *Context) GetRequest() *Request {
	if req, ok := ctx.ctlCtx.Value(reqContexKey).(*colly.Request); ok {
		return newRequest(req, ctx)
	}
	return nil
}
func (ctx *Context) Retry() error {
	if req, ok := ctx.ctlCtx.Value(reqContexKey).(*colly.Request); ok {
		return req.Retry()
	}
	return nil
}
func (ctx *Context) PutReqContextValue(key string, value interface{}) {
	if ctx.collyContext == nil {
		if req, ok := ctx.ctlCtx.Value(reqContexKey).(*colly.Request); ok {
			ctx.collyContext = req.Ctx
		} else {
			ctx.collyContext = colly.NewContext()
		}
	}
	ctx.collyContext.Put(key, value)
}
func (ctx *Context) GetReqContextValue(key string) string {
	if ctx.collyContext == nil {
		if req, ok := ctx.ctlCtx.Value(reqContexKey).(*colly.Request); ok {
			ctx.collyContext = req.Ctx
		} else {
			return ""
		}
	}
	return ctx.collyContext.Get(key)
}
func (ctx *Context) GetAnyReqContextValue(key string) interface{} {
	if ctx.collyContext == nil {
		if req, ok := ctx.ctlCtx.Value(reqContexKey).(*colly.Request); ok {
			ctx.collyContext = req.Ctx
		} else {
			return nil
		}
	}
	return ctx.collyContext.GetAny(key)
}
func (ctx *Context) Visit(URL string) error {
	return ctx.c.Visit(ctx.AbsoluteURL(URL))
}