回调函数

本章介绍爬虫中所有的回调函数,回调函数实际是爬虫运行过程中的一些钩子(Hook),开发者可以根据需要,来实现这些回调函数。

前面讲爬虫生命周期的时候,基本已经列出了所有的回调函数以及各自的回调点,下面逐一介绍。

initCrawl

function initCrawl(site)

@param site 内置对象,参考内置对象site

爬虫初始化的时候被调用,多节点运行时,只在第一个节点(又称主节点)中被调用,其他节点等待主节点的initCrawl方法执行完之后才继续往下执行。

建议在此回调中做添加入口页的操作。

beforeCrawl

function beforeCrawl(site)

@param site 内置对象,参考内置对象site

initCrawl方法之后被调用,在所有节点中都会被调用。

全局的User-Agent设置、Cookie设置建议放到此回调函数中。

onDataReceived

function onDataReceived(data, site)

@param data 数据对象,Pipeline中前一个应用的数据

@param site 内置对象,参考内置对象site

Pipeline中的爬虫通过此回调来获取前一个应用的数据,在beforeCrawl之后被回调。

前一个应用传递过来的每条数据,会封装成一个data对象,回调给onDataReceived函数。

afterCrawl

function afterCrawl(site)

@param site 内置对象,参考内置对象site

爬虫结束时调用,每个节点都会回调,在beforeExit之前被回调。

beforeExit

function beforeExit(site)

@param site 内置对象,参考内置对象site

爬虫结束时回调,只有最后一个结束的节点会回调此方法,在afterCrawl之后被回调。

beforeDownloadPage

function beforeDownloadPage(page, site)

@param page 内置对象,参考内置对象page

@param site 内置对象,参考内置对象site

@return page 内置对象,参考内置对象page。不重写此函数时,默认返回原page对象

当链接调度器从待爬队列中调度出来一个链接的时候,回调此函数。在此回调函数中可以修改链接地址page.url,修改完之后需要return page。常见的场景是链接中有时间戳,而添加链接和处理链接的时间通常是不确定的,这时可以在此回调函数中更新链接中的时间戳。

configs.beforeDownloadPage = function(page, site) {
/*
假设当前page.url是
http://stock.example.com/detail/?id=3648803&_=1514369219644
这里需要把链接中的时间戳替换成当前时间戳
*/
var timestamp = new Date().getTime();
console.log("替换前:" + page.url);
page.url = page.url.replace(/&_=\d+/, "&_="+timestamp);
console.log("替换后:" + page.url);
return page;
}

注意:

在此回调中网页还未开始下载,所以page.rawnull

onChangeProxy

function onChangeProxy(site, page)

@param site 内置对象,参考内置对象site

@param page 内置对象,参考内置对象page

当获取到一个新的代理的时候,回调此函数。切换代理之后,之前的cookie会被清空,一般在此回调中做一些cookie的加载。

isAntiSpider

function isAntiSpider(url, content, page)

@param url 当前正在处理的链接地址

@param content 当前下载的网页内容

@param page 内置对象,参考内置对象page

@return boolean 是否反爬,true表示反爬,false表示没有反爬。不重写此函数时,默认返回false

每个被调度的链接下载完成之后,会先判断返回的状态码是否403,如果403,则直接会触发切换代理;如果不是403,则回调此函数,开发者一般需要在此函数中判断返回码或者网页内容,给出是否反爬的判断,如果判断为反爬,需要返回true,否则返回false

configs.isAntiSpider = function(url, content, page) {
if (page.raw && page.raw.indexOf("请求太快了,请休息一会") >= 0) {
return true;
}
return false;
}

afterDownloadPage

function afterDownloadPage(page, site)

@param page 内置对象,参考内置对象page

@param site 内置对象,参考内置对象site

@return page 内置对象,参考内置对象page。不重写此函数时,默认返回原page对象。

每个被调度的链接下载完成之后回调该函数。在该函数中可以修改page.urlpage.raw,修改之后,修改之后的内容会一直持续到该链接的生命周期结束。修改page.raw后会影响后续的数据抽取,所以一般可以在这个回调函数中发一些请求,把获取的数据拼接到page.raw中,以便后续抽取。

onProcessScanPage

function onProcessScanPage(page, content, site)

@param page 内置对象,参考内置对象page

@param content 网页内容,contentpage.raw的区别在于,content中的链接都是绝对地址(以http开头)

@param site 内置对象,参考内置对象site

@return boolean 是否还需要自动发现链接,true表示还需要自动发现,false表示不需要自动发现。不重写此函数时,默认返回configs.autoFindUrls的值。

网页在下载完之后,如果当前链接是入口页,则回调此函数。一般在此函数中实现手动链接发现,一般是发现帮助页,也可以直接发现内容页。

onProcessHelperPage

function onProcessHelperPage(page, content, site)

@param page 内置对象,参考内置对象page

@param content 网页内容,contentpage.raw的区别在于,content中的链接都是绝对地址(以http开头)

@param site 内置对象,参考内置对象site

@return boolean 是否还需要自动发现链接,true表示还需要自动发现,false表示不需要自动发现。不重写此函数时,默认返回configs.autoFindUrls的值。

入口页判断以及onProcessScanPage回调之后,会继续判断当前链接是否是帮助页,如果是,则回调此函数。一般在此函数中实现手动链接发现,多数情况是发现内容页链接以及下一页帮助页的链接。

onProcessContentPage

function onProcessContentPage(page, content, site)

@param page 内置对象,参考内置对象page

@param content 网页内容,contentpage.raw的区别在于,content中的链接都是绝对地址(以http开头)

@param site 内置对象,参考内置对象site

@return boolean 是否还需要自动发现链接,true表示还需要自动发现,false表示不需要自动发现。不重写此函数时,默认返回configs.autoFindUrls的值。

帮助页判断以及onProcessHelperPage回调之后,会继续判断当前链接是否是内容页,如果是,则回调此函数。一般内容页不需要再做链接发现,所以此函数多数情况下直接返回false

onProcessXxxPage小结:

  • 这个系列的三个函数主要用来控制链接的发现,如果想要提高爬虫爬取效率,或者需要精确地控制爬虫的爬取路径,需要重点实现这三个函数,并禁用自动链接发现。
  • 一个链接可能同时是入口页和帮助页,也可能同时是帮助页和内容页,甚至可能同时是入口页、帮助页和内容页,这种情况下,这个链接产生的onProcessXxxPage回调,必须同时都返回false,才能禁用自动链接发现。

afterDownloadAttachedPage

function afterDownloadAttachedPage(page, site)

@param page 内置对象,参考内置对象page

@param site 内置对象,参考内置对象site

@return page 内置对象,参考内置对象page。不重写此函数时,默认返回原page对象。

attachedUrl下载完成之后会回调此函数。可以在此函数中修改page.raw的值,从而影响attachedUrl的后续抽取。多数场景是,attachedUrl返回的数据是jsonp格式,这时需要在此回调中把数据处理成json数据,以便后续用JsonPath来抽取。

afterExtractField

function afterExtractField(fieldName, data, page, site, index)

@param fieldName 抽取项名

@param data 当前抽取项抽取出的数据

@param page 内置对象,参考内置对象page

@param site 内置对象,参考内置对象site

@param index 当前项是在父抽取项的第几个结果中进行抽取,从0开始。

@return 数据对象 返回此项对应的数据。当不重写此函数时,默认返回原data对象。

在每个抽取项抽取到内容时回调此函数,一个网页的抽取过程中,会多次回调此函数。在此函数中,可以对抽取到的数据做进一步的处理,然后返回处理后的数据。

注意:

  1. 当抽取项为子项时,fieldName从顶层父抽取项开始,以.拼接至当前抽取项
  2. 有子项的抽取项不会产生此回调
  3. 如果当前抽取项不是repeated的,data类型为String,此函数必须返回String类型;如果当前抽取项是repeated的,data类型为String数组,则此函数必须返回String数组。

beforeHandleImg

function beforeHandleImg(fieldName, img)

@param fieldName 抽取项名,同afterExtractField

@param img 一个完整的img标签

@return String 处理后的img

在抽取的内容中发现标签时,回调此函数。一般在此函数中修改src,使src指向真实的图片地址。

beforeHostFile

function beforeHostFile(fieldName, url)

@param fieldName 抽取项名,同afterExtractField

@param url 待托管的文件链接

@return newUrl 处理后的托管链接

在托管文件之前回调此函数,在此函数中可以对文件地址做修改。常用的场景是,在图片托管中,修改链接地址来获取分辨率更高的图片。

afterHostFile

function afterHostFile(fieldName, hostedUrl)

@param fieldName 抽取项名,同afterExtractField

@param hostedUrl 托管后的链接地址

在托管后的文件链接计算结束之后回调此函数,在此函数中可以对托管后的链接进行收集。

afterExtractPage

function afterExtractPage(page, data, site)

@param page 内置对象,参考内置对象page

@param data 整个页面抽取出的数据

@param site 内置对象,参考内置对象site

@return 数据对象 返回处理后的抽取数据。当不重写此函数时,默认返回原data对象。

当整个网页完成抽取时回调此函数。一般在此回调中做一些数据整理的操作,也可以继续发送网络请求,把返回的数据整理后放到data中返回。