前言

最近在帮忙做内蒙那边的爬虫项目,因为公司基本上都是做业务开发的,会写爬虫的基本上是没有,我之前又做过一点,所以就找到我咯

目标是爬取知网、超星期刊和读秀的文章数据,不过知网那边在做的时候史诗性的加强了反爬虫策略(①今天打开和明天打开的页面都看起来一样,但是元素的位置变了 ;②需要获取文章的链接,但是链接在 5 分钟到 3 小时左右的随机时间就会过期,这就导致批量获取完链接再去抓详情页的数据的时候进不去详情页了😂)

协商之后,只需要做超星和读秀的,难度小了不少

验证码

由于提供的服务器没有显卡,加上 CPU 的性能也比较拉,人家不愿意给设备加钱但是愿意盯着去手点验证码,这块就没有怎么去折腾,知网的验证码比较简单,当时尝试了 OCR 的方法,用的 sml2h3/ddddocr: 带带弟弟 通用验证码识别OCR pypi版 (github.com) 这个搭了个 API Server ,然后去调它的接口拿 OCR 的结果,知网验证码的问题解决了,读秀和超星是一套验证码机制,里面是扭曲图形加扭曲文字的形式,因为上面的原因,就没有深入研究

开发

按说爬虫一般都是用 Python 写,也有成熟的爬虫框架(scrapy 之类的),不过在这里不涉及和别人协同,所以技术上的选择很自由,根据我的个人爱好,我选用 Golang 来搞(虽然写过 Python,第一个学会的正经编程语言也是 Python ,但是学 Golang 了之后就不大愿意写 Python 了,毕竟一个二进制文件直接跑可太爽了不是)

之前也用 Golang 写的简单的爬虫,可以看这里:用Go来完成每日健康自动打卡

这次也是用的 chromedp ,不过又发现了些好玩的新东西,小小的记录一下

人机检测

推荐一个库:go-rod/stealth: anti-bot-detection with rod (github.com),可以过大部分的人机检测,之前找了套了 cf 的网站试过,没得问题

if err := chromedp.Run(ctx,
chromedp.EmulateViewport(1920, 1080),
// 使用非常简单,加一行这个
chromedp.Evaluate(stealth.JS, nil),
// chromedp.Navigate("https://www.duxiu.com/"),
// 爬虫逻辑...
); err != nil {
// 错误处理
}

批量查找可能存在的数据

举例:Genetic dissection of crown root traits and their relationships with aboveground agronomic traits in maize_期刊搜索 (duxiu.com)

读秀的情况里面,有一些元素可能有的期刊有,有的期刊没有,比如基金支持,比如作者简介(图片所示的这个就没有)

duxiu-Journal-Example

对于这种动态的字段,我们把作者、刊名、出版日期拿出来康康有没有什么规律

// selector
#m_top > dl > dd:nth-child(2) > span
#m_top > dl > dd:nth-child(3) > span
#m_top > dl > dd:nth-child(4) > span

很多站都是这样的,我们可以匹配 dd:nth-child(x) 来获取需要的数据,核心代码示例如下:

              var InitChild int
authors := []string{}
for InitChild = 1; InitChild > 0 && InitChild < 100; InitChild++ {
var span string
// 遍历所有节点,获取 span 里面的内容
doc.Find("body > dl > dd:nth-child(" + fmt.Sprint(InitChild) + ") span").Each(func(i int, s *goquery.Selection) {
span = s.Text()
logrus.Debugln("thisSpan", InitChild, ":", span)
// <span>作&nbsp;&nbsp;者&nbsp;:&nbsp;</span>
// 上面是 F12 看到的内容,因为中文间有空格,我们像下面这样匹配,对于有相近名称的字段,也可以排除
if strings.Contains(span, "作") && strings.Contains(span, "者") && !strings.Contains(span, "简") && !strings.Contains(span, "介") {
logrus.Infoln("dd:nth-child("+fmt.Sprint(InitChild)+") -> found target!", span)
// 匹配成功,拿到里面的内容开始处理,结束循环
doc.Find("body > dl > dd:nth-child(" + fmt.Sprint(InitChild) + ") a").Each(func(i int, s *goquery.Selection) {
// 数据处理逻辑...
author := s.Text()
authors = append(authors, author)
InitChild = -1
logrus.Debugln("--------------------------------")
})
}
})
}

后面又爬了一些网站,上面的代码可以说是屡试不爽d=====( ̄▽ ̄*)b

小结

用 Python 、Go 、C# 都写过爬虫,个人而言是比较喜欢用 Go 来搞,Python 一是配环境不大喜欢,二是弱类型感觉写的不是很舒服,C# 的话最近也做了带点爬虫的练手项目:luckykeeper/gitlabCalendar: ASP.NET Core Web API 练手项目:GitLab私仓提交日历抓取和可视化,向大家展示你在公司的摸鱼(划掉)工作情况吧ヾ(≧▽≦*)o (github.com),用的 HtmlAgilityPack ,不过似乎只能用 XPath 来选元素,不是很好用… 如果有知道好用的库的话也可以在下面推荐一波

如果没有试过用 Go 写爬虫的话不妨试试,就我个人的感觉写的还是很巴适的ヾ(≧▽≦*)o