原来的思路是部署好反爬虫项目和scrapy爬虫后,通过nodejs网页项目调用scrapy实现爬虫并存储爬取数据,但是经过一番搜索,论坛上并没有之前的nodejs与scrapy交互的相应示例。因此尝试自己摸索nodejs的调用逻辑。
功能逻辑如下:网页客户端输入搜索关键词后单击爬取按钮调用服务器上爬虫程序,爬虫程序将爬取的到的数据保存成csv下载至服务器路径内。
在学习了node.js的官方文档后,发现child_process模块能够提供nodejs项目一个衍生子进程的功能。
child_process.exec(command[, options][, callback])
衍生 shell,然后在 shell 中执行 command
,并缓冲任何产生的输出。 传给 exec 函数的 command
字符串会被shell 直接处理。
而scrapy爬虫的启动方式也可以通过shell命令:
scrapy crawl projectName -a args=XXX
因此通过该方法可以实现nodejs与shell建立管道传入command指令执行scrapy爬虫。
该方法的AP现总结如下:
- child.stdin 获取标准输入
- child.stdout 获取标准输出
- child.stderr 获取标准错误输出
- 获取子进程的PID:child.pid
- 提供生成子进程的方法:child_process.spawn(cmd, args=[], [options])
- 提供直接执行系统命令的方法:child_process.exec(cmd, [options], callback)
- 提供调用脚本文件的方法:child_process.execFile(file, [args], [options], [callback])
接下来先编写前端界面与后端方法,实现前后端连通。前端界面包括一个input框以及一个提交button,实现逻辑为input框输入爬取关键词,button的click事件将keyword传至后台并拼接command命令通过管道调用scrapy。
index.ejs编写前端界面:
<div ng-controller="crawlerCtrl" id="crawlerPage" style="display: none;">
<h1>数据爬取</h1>
<input type="text" id="keyword" name="keyword" placeholder="请输入数据关键词">
<button ng-click="startCrawler()">提交爬虫</button>
</div>
前端提交http请求,调用后端函数调用爬虫,并通过后端响应判断是否调用成功。传入的参数为keyword,前端获取该参数并传至后端作为爬虫的参数:
var crawler = angular.module("crawler",[]);
crawler.controller("crawlerCtrl", ["$scope", "$http",
function ($scope, $http){
$scope.startCrawler = function(){
// 获取前端提交的keyword参数
var keyword = document.getElementById("keyword").value;
// alert(keyword);
$http({
method: 'post',
url: '/crawler/startCrawler',
data: {
keyword : keyword
}
}).then(
function success(res) {
if (res.data.isSuccess === "1") {
alert("爬取成功!");
} else if (res.data.isSuccess === "0") {
alert("未成功调用");
} else if (res.data.isSuccess === "-1") {
alert("正在爬取...");
} else {
alert("路径下没有正确的爬虫项目");
}
}
);
}
}
]);
后端代码实现调用爬虫,编写crawler.js路由文件
// 使用child_process库实现建立与shell的管道通讯
var exec = require('child_process').exec;
后端回调函数:
第一次尝试我写的方法如下:
router.post('/startCrawler',function(req,res){
var keyword = req.body.keyword;
console.log(keyword);
var cmdStr1 = 'cd /home/hjq/antiCrawler';
exec(cmdStr1,function(err,stdout,stderr){
if(err){
res.send({'isSuccess':'-2'});
return ;
}
else{
var cmdStr2 = 'scrapy crawl antiCrawler -a keyword=' + keyword;
console.log(cmdStr2);
console.log(stdout);
exec(cmdStr2,function(err,stdout,stderr){
if(err){
console.log(err);
res.send({'isSuccess':'0'});
return ;
}
else{
//爬虫正确执行
res.send({'isSuccess':'1'});
console.log(stdout);
return ;
}
});
}
});
});
即先建立第一次管道通信,切换工作路径至爬虫根路径下,再通过第二次管道通信执行爬虫。但是测试时报错:回传的是“0”号结果,意味着爬虫出现err,查看控制台返回的详细错误是:
Scrapy 1.8.0 - no active project
Unknown command: crawl
Use "scrapy" to see available commands
网上搜索给出的结论有两种:1. 不是爬虫的根目录;2. 缺失scrapy爬虫的cfg文件
因此大胆猜测是第一次管道通信结束后,切换的工作路径也同时丢失,导致第二次管道通信调用爬虫时是在nodejs的工作路径下调用的,自然就没法调用成功。
解决方法:
var cmdStr2 = 'cd /home/hjq/antiCrawler;scrapy crawl antiCrawler -a keyword=' + keyword;
将两条命令写成一条传入管道执行,成功调用。
爬虫保存数据成csv在项目pipelines中实现,此处不再赘述。
另外我还没测试通过管道通信shell连接ssh,若无法通过管道连接ssh那爬虫项目将要部署在客户端同一个服务器上,物理层要做出相应改变。