百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

GO语言版爬虫神器pholcus推荐(go爬虫和python爬虫)

ccwgpt 2024-10-04 13:58 35 浏览 0 评论

前言

爬虫(web crawler),wiki百科给的解析是自动浏览万维网的网络机器人,像Google和百度的搜索引擎。爬虫,不仅仅在于机器自动,更关注信息的检索和收集。万维网,是一个庞大的资料库,所以爬虫一直都是热门话题,备受关注,很多程序员或者学习,都会拿这个练手。我们都知道,爬虫知识第一步,更重要的后面的数据运用。前段时间,马蜂窝的机器评论新闻也闹得挺热闹的。但这一步,也不好走,很多网站有反爬机制,如何骗过对方,也挺费劲的。

关于反爬策略

1. 检测http header中user-agent/referer;

2. 检测cookie,多次访问cookie set次数;

3. 单个IP单位时间内访问次数控制,超过阈值,返回max requests;

4. 验证码;

5. 动态页面,关键数据(如验证码)用ajax请求,甚至加密;

6. 机器行为学习分析;

对于第三点,误伤大,很多公司都不舍得用,除非有很好的算法策略,而且意义不大,现在都找代理买IP分布式爬;

第四,ocr一下,分析些验证码,大学的时候,简单玩过下。现在的验证码多种多样的,比如电话语音验证码,还是挺烦的;

第五点,到现在其实根本不用介意,爬虫会使用phantomjs/selenium模拟浏览器,因为使用一般httpclient肯定被反爬。

最后一点,通过机器分析你的行为,成本高。

框架

目前网上有很多介绍[scrapy](https://doc.scrapy.org/en/latest/intro/overview.html)和[pyspider](http://docs.pyspider.org/en/latest/Quickstart/)的,而且官网有很详细的介绍,之前用scrapy主要写spider和item,用起来挺顺手的,这里就不累述了。那时候python还是2.7, 真正多线程还得用gevent实现,而且对中文很不友好。题外话,程序员要更好地学习什么技术时,最好看官网doc,并仔细研究源码,学习起来才能事半功倍。

本文要介绍的是,另一个分布式爬虫框架pholcus([git传送](https://github.com/henrylee2cn/pholcus.git))。整个框架使用golang写的,源码写的很仔细,很赏心悦目,大家可以研究下。程序员之间交流,show code。看源码真会有惊喜,比如hashmap hashcode中的移位一样(当然JDK里面的源码都是大神精心布置和benchmark的)。同时,作为golang初学者,真心推荐下golang,对比C#、java、pyhon、perl、js, golang个性突出,使用简单。它没有继承、没有泛型、没有析构函数等,

不要求显式初始化和隐式构造。它提供基于CSP并行的轻量级线程goroutine, 可以很轻松实现高并发。平时工作,用Java比较多,当你切换到golang模式,有channel,有lock,有goroutine,而你只需关心struct和interface,最后build生成可执行文件, 很省心。

pholcus,基本可以拿来即用,你只需写好spider放到pholcus_lib下,并注册即可,具体操作指引请看作者doc。而且,你可以一台单机可以随便起几万协程跑数据,当然并不是协程数越高跑的越快。

新增selenium下载器

pholcus有两个下载器,surf和phantomjs(phantomjs已停止更新),用起来发现phantomjs还是有很多请求被反爬。所以,在GitHub找了个[chromedp](https://github.com/chromedp/chromedp)还能用,不过它还是用打开Chrome,很多费劲。而且作者也说了该版本还有待完善,并且很快出来新版本,我们就期待下吧。如果你要使用chromedp, 你可以参考以下代码

 ctx,cancelF := context.WithCancel(context.Background())
cdp,err := chromedp.New(ctx)
if err != nil {
 log.Printf("[E] chromedp init error: %v",err)
}
self.Cdp = cdp
self.ctx = &ctx
self.cacel = cancelF
self.initial = true

记得自己get

> go get -u github.com/chromedp/chromedp

最后采用webdriver,自己新增了个selenium downloader,效果挺好,爆破力极强,[源码传送](https://github.com/onebite/pholcus/blob/master/app/downloader/surfer/sel.go),

具体可以参考如下:

package surfer
import (
 "github.com/tebeka/selenium"
 "net/http"
 "net"
 "sync"
 "os"
 ch "github.com/tebeka/selenium/chrome"
 "fmt"
 "time"
 "strings"
 "io/ioutil"
 "github.com/henrylee2cn/pholcus/config"
 "github.com/henrylee2cn/pholcus/logs"
)
/** problems:
1、 运行久了,会报一下错误,说chromedriver no long running
 The process started from chrome location /opt/google/chrome/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed
 解决:如果你自定义路径下的exe, 需设置Chromedriver.exe路径参数,chrome默认启用的是local setttings里的chromedriver.exe
 */
type Sel struct {
 Service *selenium.Service
 caps selenium.Capabilities
 saveScript string
 port int
 host string
 initial bool
 maxReq chan int //最大请求数,过大会崩溃
 sessionGroup sync.WaitGroup
}
var ms sync.Mutex
func NewSel() Surfer{
 s := &Sel{
 initial: false,
 maxReq: make(chan int, 8),
 }
 return s
}
func (self *Sel)initIE(){
 if self.initial {
 return
 }
 ms.Lock()
 defer ms.Unlock()
 port,_ := pickUnusedPort()
 opts := []selenium.ServiceOption{
 selenium.Output(os.Stderr),
 }
 selenium.SetDebug(false)
 service, err := selenium.NewIEDriverService(config.IEDRIVER,port,opts...)
 if err != nil {
 panic(err)
 }
 caps := selenium.Capabilities{"browserName":"internet explorer"}
 chromeCaps := ch.Capabilities{
 Path: config.IEDRIVER,
 }
 caps.AddChrome(chromeCaps)
 self.Service = service
 self.caps = caps
 self.port = port
 self.host = fmt.Sprintf("http://localhost:%d/wd/hub",port)
 self.saveScript = ""
}
func pickUnusedPort() (int, error){
 addr, err := net.ResolveTCPAddr("tcp","127.0.0.1:0")
 if err != nil {
 return 0,nil
 }
 l,err := net.ListenTCP("tcp",addr)
 if err != nil {
 return 0,err
 }
 port := l.Addr().(*net.TCPAddr).Port
 if err := l.Close(); err != nil {
 return 0,err
 }
 return port, nil
}
func (self *Sel) getRemote2() (selenium.WebDriver,error) {
 mutex.Lock()
 defer mutex.Unlock()
 if self.initial {
 wb, err := selenium.NewRemote(self.caps,self.host)
 if err == nil {
 return wb,nil
 }
 }
 if self.initial && self.Service != nil {
 self.initial = false
 self.sessionGroup.Wait()
 self.Service.Stop()
 }
 port,_ := pickUnusedPort()
 opts := []selenium.ServiceOption{
 selenium.Output(os.Stderr),
 }
 selenium.SetDebug(false)
 service, err := selenium.NewChromeDriverService(config.CHROMEDRIVER,port,opts...)
 if err != nil {
 logs.Log.Critical("[WebDriver Error] start on port:%d %v",port,err)
 self.initial = false
 return nil,err
 }
 logs.Log.Critical("[WebDriver] start on port:%d",port)
 caps := selenium.Capabilities{"browserName":"chrome"}
 chromeCaps := ch.Capabilities{
 Args: []string{
 "--headless",
 "--start-maximized",
 "--no-sandbox",
 "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
 "--disable-gpu",
 "--disable-impl-side-painting",
 "--disable-gpu-sandbox",
 "--disable-accelerated-2d-canvas",
 "--disable-extensions",
 "--disable-accelerated-jpeg-decoding",
 "--test-type=ui",
 },
 Prefs: map[string]interface{} {
 "browser.download.dir": config.FILE_DIR,
 "download.default_directory": config.FILE_DIR,
 "download.prompt_for_download": false,
 "download.directory_upgrade": true,
 "safebrowsing.enabled": true,
 },
 }
 caps.AddChrome(chromeCaps)
 self.Service = service
 self.caps = caps
 self.port = port
 self.host = fmt.Sprintf("http://localhost:%d/wd/hub",port)
 self.saveScript = ""
 self.initial = true
 return selenium.NewRemote(self.caps,self.host)
}
func (self *Sel) getRemote() (selenium.WebDriver,error){
 //here no need to lock
 if self.initial {
 wb, err := selenium.NewRemote(self.caps,self.host)
 if err == nil {
 return wb,nil
 }
 }
 return self.getRemote2()
}
func (self *Sel) freeOne() {
 <- self.maxReq
}
func (self *Sel) Download(req Request) (resp *http.Response,err error) {
 self.maxReq <- 1
 defer self.freeOne()
 param, err := NewParam(req)
 if err != nil {
 return nil, err
 }
 resp = param.writeback(resp)
 resp.Request.URL = param.url
 self.sessionGroup.Add(1)
 defer self.sessionGroup.Done()
 for i := 0; i < param.tryTimes; i++ {
 if i != 0 {
 time.Sleep(param.retryPause)
 }
 html, err := self.GetPageSource(req)
 if err != nil {
 continue
 }
 resp.StatusCode = http.StatusOK
 resp.Status = http.StatusText(http.StatusOK)
 resp.Body = ioutil.NopCloser(strings.NewReader(html))
 break
 }
 if err != nil {
 resp.StatusCode = http.StatusBadGateway
 resp.Status = err.Error()
 }
 return
}
func (self *Sel) GetPageSource(req Request) (string,error) {
 //session create here not outside as time.sleep make session easy timeout
 wd, err := self.getRemote()
 if err != nil {
 //wait some time
 time.Sleep(10*time.Second)
 wd, err = self.getRemote()
 if err != nil {
 logs.Log.Informational("[Crash]WebDriver crash: %v",err)
 return "", err
 }
 }
 if wd != nil {
 defer wd.Close()
 }
 if req.GetCookieKV() != nil && len(req.GetCookieKV()) > 0 {
 //不能给_blank set cookies,即domain为空时不能set cookies
 //We don't support adding cookies to "about:blank". You need to be on the actual domain
 //you want to add the cookie to.
 wd.Get(req.GetUrl())
 for key,v := range req.GetCookieKV() {
 coo := &selenium.Cookie{
 Name: key,
 Value: v,
 }
 err := wd.AddCookie(coo)
 if err != nil {
 logs.Log.Informational("errrrrrrrrrrrrrrrrr %v %v %v ",key,v,err)
 }
 }
 }
 err = wd.Get(req.GetUrl())
 if err != nil {
 return "", err
 }
 wd.ExecuteScript(" window.scrollTo(0, 999999999);",[]interface{}{})
 html, err := wd.PageSource()
 return html, err
}

贴一下,chromedriver.exe支持的[webdriver命令]

相关推荐

自己动手写Android数据库框架_android开发数据库搭建

http://blog.csdn.net/feiduclear_up/article/details/50557590推荐理由关于Android数据库操作,由于每次都要自己写数据库操作,每次还得去...

谷歌开源大模型评测工具LMEval,打通谷歌、OpenAI、Anthropic

智东西编译|金碧辉编辑|程茜智东西5月28日消息,据科技媒体TheDecoder5月26日报道,当天,谷歌正式发布开源大模型评测框架LMEval,支持对GPT-4o、Claude3.7...

工信部:着力推动大模型算法、框架等基础性原创性的技术突破

工信部新闻发言人今日在发布会上表示,下一步,我们将坚持突出重点领域,大力推动制造业数字化转型,推动人工智能创新应用。主要从以下四个方面着力。一是夯实人工智能技术底座。通过科技创新重大项目,着力推动大模...

乒乓反复纠结“框架不稳定”的三个小误区

很多球友由于对框架的认知不清晰,往往会把“框架不稳定”当成一种心理负担,从而影响学球进度,其典型状态就是训练中有模有样,一旦进入实战,就像被捆住了手脚。通过训练和学习,结合“基本功打卡群”球友们交流发...

前AMD、英特尔显卡架构师Raja再战GPU,号称要全面重构堆栈

IT之家8月5日消息,知名GPU架构师拉贾科杜里(RajaKoduri)此前曾先后在AMD和英特尔的显卡部门担任要职。而在今日,由Raja创立的GPU软件与IP初创企...

三种必须掌握的嵌入式开发程序架构

前言在嵌入式软件开发,包括单片机开发中,软件架构对于开发人员是一个必须认真考虑的问题。软件架构对于系统整体的稳定性和可靠性是非常重要的,一个合适的软件架构不仅结构清晰,并且便于开发。我相...

怪不得别人3秒就知道软考案例怎么做能50+

软考高级统一合格标准必须三科都达到45分,案例分析也一直是考生头疼的一门,但是掌握到得分点,案例能不能50+还不是你们说了算吗?今天就结合架构案例考点,分享实用的备考攻略~一、吃透考点,搭建知识框架从...

UML统一建模常用图有哪些,各自的作用是什么?一篇文章彻底讲透

10万+爆款解析:9大UML图实战案例,小白也能秒懂!为什么需要UML?UML(统一建模语言)是软件开发的“蓝图”,用图形化语言描述系统结构、行为和交互,让复杂需求一目了然。它能:降低沟通成本避...

勒索软件转向云原生架构,直指备份基础设施

勒索软件组织和其他网络犯罪分子正越来越多地将目标对准基于云的备份系统,对久已确立的灾难恢复方法构成了挑战。谷歌安全研究人员在一份关于云安全威胁演变的报告中警告称,随着攻击者不断改进数据窃取、身份泄露和...

ConceptDraw DIAGRAM:释放创意,绘就高效办公新未来

在当今数字化时代,可视化工具已成为提升工作效率和激发创意的关键。ConceptDrawDIAGRAM,作为一款世界顶级的商业绘图软件,凭借其强大的功能和用户友好的界面,正逐渐成为众多专业人士的首选绘...

APP 制作界面设计教程:一步到位_app界面设计模板一套

想让APP界面设计高效落地,无需繁琐流程,掌握“框架搭建—细节填充—体验优化”三步法,即可一步到位完成专业级设计。黄金框架搭建是基础。采用“三三制布局”:将屏幕横向三等分,纵向保留三...

MCP 的工作原理:关键组件_mcp部件

以下是MCP架构的关键组件:MCP主机:像ClaudeDesktop、GitHubCopilot或旅行助手这样的AI智能体,它们希望通过MCP协议访问工具、资源等。MCP主机会...

软件架构_软件架构师工资一般多少

软件架构师自身需要是程序员,并且必须一直坚持做一线程序员。软件架构应该是能力最强的一群程序员,他们通常会在自身承接编程任务的同时,逐渐引导整个团队向一个能够最大化生产力的系统设计方向前进。软件系统的架...

不知不觉将手机字体调大!老花眼是因为“老了吗”?

现在不管是联系、交友,还是购物,都离不开手机。中老年人使用手机的时间也在逐渐加长,刷抖音、看短视频、发朋友圈……看手机的同时,人们也不得不面对“视力危机”——老花眼,习惯眯眼看、凑近看、瞪眼看,不少人...

8000通用汉字学习系列讲座(第046讲)

[表声母字]加(续)[从声汉字]伽茄泇迦枷痂袈笳嘉驾架咖贺瘸(计14字)嘉[正音]标准音读jiā。[辨形]上下结构,十四画。会意形声字,从壴从加,加也表声。注:从壴,字义与鼓乐有关;从加,字义与...

取消回复欢迎 发表评论: