利用php(以及开源工具)实现爬虫
流程说明
- 从数据库或者循环构建爬虫的url(包括分页参数)**
- 分段取出使用多线程保存数据到redis**
- 启用队列把数据保存到数据库**
开始
- 使用tp5.0的框架,安装爬虫扩展 QueryList 4.0
composer require jaeger/querylist
GitHub地址
2. 安装多线程curl扩展CurlMulti 插件
composer require jaeger/querylist-curl-multi
GitHub地址
3. 如果需要运行js脚本,安装PhantomJS 插件
composer require jaeger/querylist-phantomjs
GitHub地址
4. 安装tp5的队列扩展
composer require topthink/think-queue
GitHub地址
5. 安装taskPHP扩展使用php的cli模式(不超时也可定时执行程序)
composer require taskphp/taskphp dev-master
GitHub地址
配置使用
queue配置(/extra/queue.php)
'Database', // 数据库驱动
// 'expire' => null, // 任务的过期时间,默认为60秒; 若要禁用,则设置为 null
// 'default' => 'default', // 默认的队列名称
// 'table' => 'prefix_jobs', // 存储消息的表名,不带前缀
// 'dsn' => [],
// --------------------
'connector' => 'Redis', // Redis 驱动
'expire' => null, // 任务的过期时间,默认为60秒; 若要禁用,则设置为 null
'default' => 'default2', // 默认的队列名称
'host' => '127.0.0.1', // redis 主机ip
'port' => 6379, // redis 端口
'password' => '', // redis 密码
'select' => 0, // 使用哪一个 db,默认为 db0
'timeout' => 0, // redis连接的超时时间
'persistent' => false, // 是否是长连接s
];
taskphp配置
[
//key为任务名,多任务下名称必须唯一
// 'all' => [
// 'callback' => ['app\\index\\controller\\Demo', 'run'], //任务调用:类名和方法
// //指定任务进程最大内存 系统默认为512M
// 'worker_memory' => '2048M',
// //开启任务进程的多线程模式
// 'worker_pthreads' => false,
// //任务的进程数 系统默认1
// 'worker_count' => 1,
// //crontad格式 :秒 分 时 天 月 年 周 // 大概是6分钟 执行一个周期
// 'crontab' => '1 * * * * * *',
// ],
// 'bufen' => [
// 'callback' => ['app\\index\\controller\\Demo', 'run2'], //任务调用:类名和方法
// //指定任务进程最大内存 系统默认为512M
// 'worker_memory' => '1024M',
// //开启任务进程的多线程模式
// 'worker_pthreads' => false,
// //任务的进程数 系统默认1
// 'worker_count' => 1,
// //crontad格式 :秒 分 时 天 月 年 周 // 大概是6分钟 执行一个周期
// 'crontab' => '1 * * * * * *',
// ],
// 'bufenRedis' => [
// 'callback' => ['app\\index\\controller\\Demo', 'run3'], //任务调用:类名和方法
// //指定任务进程最大内存 系统默认为512M
// 'worker_memory' => '1024M',
// //开启任务进程的多线程模式
// 'worker_pthreads' => false,
// //任务的进程数 系统默认1
// 'worker_count' => 1,
// //crontad格式 :秒 分 时 天 月 年 周 // 大概是6分钟 执行一个周期
// 'crontab' => '1 * * * * * *',
// ],
],
];
}
protected function configure()
{
$this->addArgument('param', Argument::OPTIONAL);
// 设置命令名称
$this->setName($_SERVER['argv'][1])->setDescription('this is a taskphp!');
}
protected function execute(Input $input, Output $output)
{
//系统配置
$config = $this->get_config();
//加载配置信息
\taskphp\Config::load($config);
//定义启动文件入口标记
define("START_PATH", dirname(APP_PATH));
//运行框架
\taskphp\App::run();
}
}
主逻辑
chunk(200, function ($datas) {
$url = [];
foreach ($datas as $data) {
$url[] = 'http://i.meituan.com/poi/' . $data['aid'];
}
$this->startnojs($url);
});
// 测试
// $url = [
// 'http://i.meituan.com/poi/71225712',
// 'http://i.meituan.com/poi/116558576',
// ];
// $data['aid'] = 1;
// $this->start($url, $data['aid']);
}
public function startnojs($url = [])
{
$ql = QueryList::getInstance();
$ql->use(CurlMulti::class);
$ql->rules(['html' => ['html', 'html', '']])
->curlMulti($url)
->success(function (QueryList $ql, CurlMulti $curl, $r) {
$p = $r['info']['url'];
$pid = substr($p, 25);
$ret2 = $ql->getHtml();
preg_match("/这家店不错哦,一起去吧!(.*?)。\"/", $ret2, $m);
if (!empty($m)) {
if (!empty($m[1])) {
$pieces = explode(",", $m[1]);
$data = [
'name' => $pieces[0],
'address' => $pieces[1],
'mobile' => $pieces[2],
'p' => $pid,
];
$count = Db::table('info')->where('address', $pieces[1])->count();
if ($count > 0) {
} else {
$this->push($data);
}
}
}
$ql->destruct();
})->start([
'maxThread' => 100,
'maxTry' => 3,
]);
}
/**
* 多线程+cookie爬虫
* @return [type] [description]
*/
public function start($url = [])
{
$ql = QueryList::getInstance();
$ql->use(CurlMulti::class);
$ql->use(PhantomJs::class, 'D:/phantomjs-2.1.1-windows/bin/phantomjs.exe');
$ql->rules(['html' => ['html', 'html', '']])
->curlMulti($url)
->success(function (QueryList $ql, CurlMulti $curl, $r) {
$p = $r['info']['url'];
$pid = substr($p, 25);
$ret = $ql->browser(function (\JonnyW\PhantomJs\Http\RequestInterface $r) use ($p, $pid) {
$r->setMethod('GET');
$r->addHeader('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8');
// $r->addHeader('Referer', 'http://cq.meituan.com/s/%E5%90%83%E9%A5%AD/');
$r->addHeader('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 YaBrowser/18.4.0.2080 Yowser/2.5 Safari/537.36');
$r->addHeader('Cookie', 'IKUT=9156; BAIDUID=AA818089000D26F1B318D034B442113F:FG=1; BIDUPSID=AA818089000D26F1B318D034B442113F; PSTM=1526021220; BDUSS=h5dVM2Zk56OTIydUk2TFNFZzExVDBHOFNZQXhMOH5yVHFyQTRaaWRycHdBQzViQVFBQUFBJCQAAAAAAAAAAAEAAADXQoYktKi452pjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBzBltwcwZbTk; Hm_lvt_16bc67e4f6394c05d03992ea0a0e9123=1526714879,1527237021,1527237078; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; Hm_lvt_6859ce5aaf00fb00387e6434e4fcc925=1527583339,1527645854,1527650790,1527651051; PSINO=3; locale=zh; H_PS_PSSID=1449_21111; Hm_lpvt_6859ce5aaf00fb00387e6434e4fcc925=1527652553');
$r->setUrl($p);
$r->setTimeout(10000); // 10 seconds
$r->setDelay(3); // 3 seconds
return $r;
});
$ret2 = $ret->getHtml();
preg_match("/这家店不错哦,一起去吧!(.*?)。\"/", $ret2, $m);
if (!empty($m)) {
if (!empty($m[1])) {
$pieces = explode(",", $m[1]);
$data = [
'name' => $pieces[0],
'address' => $pieces[1],
'mobile' => $pieces[2],
'p' => $pid,
];
$count = Db::table('info')->where('address', $pieces[1])->count();
if ($count > 0) {
} else {
$this->push($data);
}
}
}
$ql->destruct();
})->start([
'maxThread' => 20,
'maxTry' => 3,
]);
}
/**
* 推送列队
* @param array $data [description]
* @return [type] [description]
*/
public function push($data = [])
{
$jobData = json_encode($data);
$jobHandlerClassName = 'app\index\controller\Job';
$jobQueueName = "PaChongShuJu";
$isPushed = Queue::push($jobHandlerClassName, $jobData, $jobQueueName);
// if ($isPushed) {
// echo "ok";
// }else{
// var_dump($isPushed);
// }
}
}
job文件
add_db($pieces);
if ($job->attempts() > 3) {
//通过这个方法可以检查这个任务已经重试了几次了
$job->delete();
}
//如果任务执行成功后 记得删除任务,不然这个任务会重复执行,直到达到最大重试次数后失败后,执行failed方法
$job->delete();
// 也可以重新发布这个任务
// $job->release($delay); //$delay为延迟时间
}
public function failed($data)
{
// ...任务达到最大重试次数后,失败了
}
public function add_db($data = [])
{
$data = (array) json_decode(json_decode($data));
$count = Db::table('info')->where('address', $data['address'])->count();
if ($count == 0) {
Db::table('info')->insert($data);
}
}
}
taskPHP任务入口程序
once();
}
public static function run2()
{
$papachong = new \app\index\controller\Chong();
$papachong->once2();
}
public static function run3()
{
$papachong = new \app\index\controller\Test();
$papachong->make_url();
}
}
tp5命令行配置文件(application/command.php)
执行开始
- 开启redis服务器
- 运行监听队列
php think queue:work --queue PaChongShuJu
- 运行taskPHP任务(win直接运行目录下面的bat文件)开始爬虫
php think start