コード例 #1
0
ファイル: matrix.go プロジェクト: clock145/pholcus
// 添加请求到队列,并发安全
func (self *Matrix) Push(req *request.Request) {
	if sdl.checkStatus(status.STOP) {
		return
	}

	// 禁止并发,降低请求积存量
	self.Lock()
	defer self.Unlock()

	// 达到请求上限,停止该规则运行
	if self.maxPage >= 0 {
		return
	}

	// 暂停状态时等待,降低请求积存量
	waited := false
	for sdl.checkStatus(status.PAUSE) {
		waited = true
		runtime.Gosched()
	}
	if waited && sdl.checkStatus(status.STOP) {
		return
	}

	// 资源使用过多时等待,降低请求积存量
	waited = false
	for self.resCount > sdl.avgRes() {
		waited = true
		runtime.Gosched()
	}
	if waited && sdl.checkStatus(status.STOP) {
		return
	}

	// 不可重复下载的req
	if !req.IsReloadable() {
		hash := makeUnique(req)
		// 已存在成功记录时退出
		if self.hasHistory(hash) {
			return
		}
		// 添加到临时记录
		self.insertTempHistory(hash)
	}

	var priority = req.GetPriority()

	// 初始化该蜘蛛下该优先级队列
	if _, found := self.reqs[priority]; !found {
		self.priorities = append(self.priorities, priority)
		sort.Ints(self.priorities) // 从小到大排序
		self.reqs[priority] = []*request.Request{}
	}

	// 添加请求到队列
	self.reqs[priority] = append(self.reqs[priority], req)

	// 大致限制加入队列的请求量,并发情况下应该会比maxPage多
	atomic.AddInt64(&self.maxPage, 1)
}
コード例 #2
0
ファイル: matrix.go プロジェクト: clock145/pholcus
// 返回是否作为新的失败请求被添加至队列尾部
func (self *Matrix) DoHistory(req *request.Request, ok bool) bool {
	hash := makeUnique(req)

	if !req.IsReloadable() {
		self.tempHistoryLock.Lock()
		delete(self.tempHistory, hash)
		self.tempHistoryLock.Unlock()

		if ok {
			self.history.UpsertSuccess(hash)
			return false
		}
	}

	if ok {
		return false
	}

	self.failureLock.Lock()
	defer self.failureLock.Unlock()
	if _, ok := self.failures[hash]; !ok {
		// 首次失败时,在任务队列末尾重新执行一次
		self.failures[hash] = req
		logs.Log.Informational(" *     + 失败请求: [%v]\n", req.GetUrl())
		return true
	}
	// 失败两次后,加入历史失败记录
	self.history.UpsertFailure(req)
	return false
}
コード例 #3
0
func (self *Surfer) Download(sp *spider.Spider, cReq *request.Request) *spider.Context {
	ctx := spider.GetContext(sp, cReq)

	var resp *http.Response
	var err error

	switch cReq.GetDownloaderID() {
	case SURF_ID:
		resp, err = self.surf.Download(cReq)

	case PHANTOM_ID:
		resp, err = self.phantom.Download(cReq)
	}

	if resp.StatusCode >= 400 {
		err = errors.New("响应状态 " + resp.Status)
	}

	if resp.Header.Get("Content-Encoding") == "gzip" {
		var gzipReader *gzip.Reader
		gzipReader, err = gzip.NewReader(resp.Body)
		resp.Body.Close()
		if err == nil {
			resp.Body = ioutil.NopCloser(gzipReader)
		}
	}

	ctx.SetResponse(resp).SetError(err)

	return ctx
}
コード例 #4
0
ファイル: failure.go プロジェクト: ZenithDandelion/pholcus
// 更新或加入失败记录,
// 对比是否已存在,不存在就记录,
// 返回值表示是否有插入操作。
func (self *Failure) UpsertFailure(req *request.Request) bool {
	self.RWMutex.Lock()
	defer self.RWMutex.Unlock()
	if self.list[req.Unique()] != nil {
		return false
	}
	self.list[req.Unique()] = req
	return true
}
コード例 #5
0
ファイル: crawl.go プロジェクト: ReinhardHsu/pholcus
// core processer
func (self *crawler) Process(req *request.Request) {
	var (
		ctx     = self.Downloader.Download(self.Spider, req) // download page
		downUrl = req.GetUrl()
	)

	if err := ctx.GetError(); err != nil {
		// 返回是否作为新的失败请求被添加至队列尾部
		if self.Spider.DoHistory(req, false) {
			// 统计失败数
			cache.PageFailCount()
		}
		// 提示错误
		logs.Log.Error(" *     Fail  [download][%v]: %v\n", downUrl, err)
		return
	}

	defer func() {
		if err := recover(); err != nil {
			if activeStop, _ := err.(string); activeStop == spider.ACTIVE_STOP {
				return
			}
			// 返回是否作为新的失败请求被添加至队列尾部
			if self.Spider.DoHistory(req, false) {
				// 统计失败数
				cache.PageFailCount()
			}
			// 提示错误
			logs.Log.Error(" *     Panic  [process][%v]: %v\n", downUrl, err)
		}
	}()

	// 过程处理,提炼数据
	ctx.Parse(req.GetRuleName())

	// 处理成功请求记录
	self.Spider.DoHistory(req, true)

	// 统计成功页数
	cache.PageSuccCount()

	// 提示抓取成功
	logs.Log.Informational(" *     Success: %v\n", downUrl)

	// 该条请求文本结果存入pipeline
	for _, item := range ctx.PullItems() {
		self.Pipeline.CollectData(item)
	}
	// 该条请求文件结果存入pipeline
	for _, f := range ctx.PullFiles() {
		self.Pipeline.CollectFile(f)
	}
	// 释放ctx准备复用
	spider.PutContext(ctx)
}
コード例 #6
0
ファイル: context.go プロジェクト: henrylee2cn/pholcus
// 生成并添加请求至队列。
// Request.Url与Request.Rule必须设置。
// Request.Spider无需手动设置(由系统自动设置)。
// Request.EnableCookie在Spider字段中统一设置,规则请求中指定的无效。
// 以下字段有默认值,可不设置:
// Request.Method默认为GET方法;
// Request.DialTimeout默认为常量request.DefaultDialTimeout,小于0时不限制等待响应时长;
// Request.ConnTimeout默认为常量request.DefaultConnTimeout,小于0时不限制下载超时;
// Request.TryTimes默认为常量request.DefaultTryTimes,小于0时不限制失败重载次数;
// Request.RedirectTimes默认不限制重定向次数,小于0时可禁止重定向跳转;
// Request.RetryPause默认为常量request.DefaultRetryPause;
// Request.DownloaderID指定下载器ID,0为默认的Surf高并发下载器,功能完备,1为PhantomJS下载器,特点破防力强,速度慢,低并发。
// 默认自动补填Referer。
func (self *Context) AddQueue(req *request.Request) *Context {
	// 若已主动终止任务,则崩溃爬虫协程
	self.spider.tryPanic()

	err := req.
		SetSpiderName(self.spider.GetName()).
		SetEnableCookie(self.spider.GetEnableCookie()).
		Prepare()

	if err != nil {
		logs.Log.Error(err.Error())
		return self
	}

	// 自动设置Referer
	if req.GetReferer() == "" && self.Response != nil {
		req.SetReferer(self.GetUrl())
	}

	self.spider.RequestPush(req)
	return self
}
コード例 #7
0
ファイル: failure.go プロジェクト: ZenithDandelion/pholcus
// 删除失败记录
func (self *Failure) DeleteFailure(req *request.Request) {
	self.RWMutex.Lock()
	delete(self.list, req.Unique())
	self.RWMutex.Unlock()
}
コード例 #8
0
ファイル: crawler.go プロジェクト: henrylee2cn/pholcus
// core processer
func (self *crawler) Process(req *request.Request) {
	var (
		downUrl = req.GetUrl()
		sp      = self.Spider
	)
	defer func() {
		if p := recover(); p != nil {
			if sp.IsStopping() {
				// println("Process$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$")
				return
			}
			// 返回是否作为新的失败请求被添加至队列尾部
			if sp.DoHistory(req, false) {
				// 统计失败数
				cache.PageFailCount()
			}
			// 提示错误
			stack := make([]byte, 4<<10) //4KB
			length := runtime.Stack(stack, true)
			start := bytes.Index(stack, []byte("/src/runtime/panic.go"))
			stack = stack[start:length]
			start = bytes.Index(stack, []byte("\n")) + 1
			stack = stack[start:]
			if end := bytes.Index(stack, []byte("\ngoroutine ")); end != -1 {
				stack = stack[:end]
			}
			stack = bytes.Replace(stack, []byte("\n"), []byte("\r\n"), -1)
			logs.Log.Error(" *     Panic  [process][%s]: %s\r\n[TRACE]\r\n%s", downUrl, p, stack)
		}
	}()

	var ctx = self.Downloader.Download(sp, req) // download page

	if err := ctx.GetError(); err != nil {
		// 返回是否作为新的失败请求被添加至队列尾部
		if sp.DoHistory(req, false) {
			// 统计失败数
			cache.PageFailCount()
		}
		// 提示错误
		logs.Log.Error(" *     Fail  [download][%v]: %v\n", downUrl, err)
		return
	}

	// 过程处理,提炼数据
	ctx.Parse(req.GetRuleName())

	// 该条请求文件结果存入pipeline
	for _, f := range ctx.PullFiles() {
		if self.Pipeline.CollectFile(f) != nil {
			break
		}
	}
	// 该条请求文本结果存入pipeline
	for _, item := range ctx.PullItems() {
		if self.Pipeline.CollectData(item) != nil {
			break
		}
	}

	// 处理成功请求记录
	sp.DoHistory(req, true)

	// 统计成功页数
	cache.PageSuccCount()

	// 提示抓取成功
	logs.Log.Informational(" *     Success: %v\n", downUrl)

	// 释放ctx准备复用
	spider.PutContext(ctx)
}
コード例 #9
0
ファイル: matrix.go プロジェクト: clock145/pholcus
func makeUnique(req *request.Request) string {
	return util.MakeUnique(req.GetUrl() + req.GetMethod())
}