PHP数据采集程序 – 利用phpQuery

此段程序的产生是由于某朋友想把aliexpress上的某个店的某个目录的全部产品图片采集下来。

思路是:把目录页面抓取过来,分析有多少分页,然后循环获取产品的链接,获得链接的同时进入链接对应的产品页面抓取符合规则的图片地址,然后把这些图片下载分别存放,最后生成一个CSV文件,它记录了所有链接。

遇到的问题:
1 在使用PHP抓取网页的时候,被服务器拒绝了,返回403,浏览器访问正常。
第一反应肯定是User Agent的问题,在PHP配置文件中,user_agent配置指令可以修改PHP本身的UA标识,改成user_agent=”Mozilla/5.0 (Windows NT 6.2; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0″,那么就伪装为firefox浏览器了。

在大部分的服务器设置中,为了防止一些恶意爬虫、采集器等,通常禁止它们访问,比如在Nginx中:

if ($http_accept_language ~* ^zh) { return 404;}
if ($http_user_agent ~* (^$|LWP::Simple|BBBike|wget|scrapbot)) { return 403;}

这个配置可以让中文浏览器得到404(浏览器的语言标识可修改),对用户代理是空的或是|LWP::Simple|BBBike|wget|scrapbot之一,全部返回403,当然了,用户代理是可以修改的(伪装)。

2 在抓取到网页后,如何快速提取想要的内容?
刚开始是用正则来提取内容的,实现起来比较费力,后来想用PHP的DOM扩展来提取内容,不过还觉得不方便,最终选择了phpQuery库(jQuery的服务器实现,实际也是用PHP DOM来实现)来提取内容,由于对jQuery比较熟悉,故而对phpQuery的学习成本了零。

3 如何下载图片?
最终使用如下代码实现图片下载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

function GrabImage($url, $filename=""){
if($url == ""){return false;}

$ext = strrchr($url, ".");//得到图片的扩展名
if($ext != ".gif" && $ext != ".jpg" && $ext != ".bmp"){echo "格式不支持!";return false;}

if($filename == ""){ $filename = time()."$ext"; }//以时间戳另起名

//开始捕捉
ob_start();
readfile($url);
$img = ob_get_contents();
ob_end_clean();
$size = strlen($img);
$fp2 = fopen($filename , "a");
fwrite($fp2, $img);
fclose($fp2);
return $filename;
}

分析aliexpress的目录链接形式都是如此http://www.aliexpress.com/store/group/目录名/店ID_目录ID.html, 如果第二页就是http://www.aliexpress.com/store/group/目录名/店ID_目录ID/2.html, 所以为了方便,我让“目录名/店ID_目录ID.html”这段直接作为请求的URI,然后分析这个URI取出相关参数,开始抓取,这样只要拷贝粘贴就可以开始了。比如PHP脚本文件名为get.php,要采集的目录链接为http://www.aliexpress.com/store/group/xxxx/666_888.html,那么只要访问http://localhost/aliexpress/get.php/xxxx/666_888.html就可以开始了。

另外,如果目录图片比较多,程序运行时间可能会超过限制值,所以要修改一些PHP的相关指令,开足马力:

max_execution_time=3600
max_input_time = 3600
memory_limit=1024M
这里运行最大执行时间为1小时,最大等待数据返回浏览器时间为1小时,最大可用内存为1G。max_input_time也叫超时时间,当PHP脚本运行了很久还有没有访问,在浏览器端就会弹出一个窗口,关闭后浏览器不再等待服务器的数据返回,所以如果是让浏览器下载文件的,这个值务必要加大才行。

代码参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

function GrabImage($url, $filename=""){
if($url == ""){return false;}

$ext = strrchr($url, ".");//得到图片的扩展名
if($ext != ".gif" && $ext != ".jpg" && $ext != ".bmp"){echo "格式不支持!";return false;}

if($filename == ""){ $filename = time()."$ext"; }//以时间戳另起名

//开始捕捉
ob_start();
readfile($url);
$img = ob_get_contents();
ob_end_clean();
$size = strlen($img);
$fp2 = fopen($filename , "a");
fwrite($fp2, $img);
fclose($fp2);
return $filename;
}

//加载phpQuery库
require_once('phpQuery/phpQuery.php');

ini_set("display_errors","0");
ini_set("max_execution_time","7200");
ini_set("memory_limit","1024M");

//解析地址
$request_arr = explode('/',ltrim($_SERVER['REQUEST_URI'],$_SERVER['SCRIPT_NAME']));
$temp_var = explode('_',$request_arr[1]);
$shop_id = $temp_var[0];
$cat = $request_arr[0];
$cat_id = rtrim($temp_var[1],'.html');

$shop_all_url = "http://www.aliexpress.com/store/group/".$cat."/".$shop_id."_".$cat_id;

//存放目录
$base = dirname(__FILE__)."/";
$new_base = $base.$cat."_".$shop_id."_".$cat_id."/";
if(!is_dir($new_base)){
mkdir($new_base);
}

$html = file_get_contents($shop_all_url."/1.html");
phpQuery::newDocument($html);
$page_text = pq("span.pg-info")->text();

// $page_total 截获所有页
$page_total = 1;
preg_match('/.*\/([0-9]{1,12})/',$page_text,$page_total);
if(isset($page_total[1])){
$page_total = (int)$page_total[1];
}
//$page_total = 1; 多页时,限制其为1页,测试用

/**/
$excude = array(); //记录那些重复的,不需要的图片地址

if($page_total > 0){
$fp = fopen($base.$cat."_".$shop_id."_".$cat_id.".csv","w");
$csv_str = "ID,Links\n";
for($i = 1; $i <= $page_total; $i++){
$html = file_get_contents($shop_all_url."/".$i.".html");

phpQuery::newDocument($html);
$links = pq("li.list-item .img .pic a");

$j = 0;
foreach($links as $ls){
//if($j > 0){ break; } 多个产品时,限制其只处理一个,测试用
$phtml_link = pq($ls)->attr('href');
preg_match('/http:\/\/.*_([0-9]{1,16})\.html/',$phtml_link,$pid);
$pid = $pid[1];

fputcsv(array($pid,$phtml_link));
$img_path = $new_base.$pid.'/';
//创建目录,如果脚本执行以为终止,可以再次运行,跳过之前下载的数据
if(!is_dir($img_path)){ mkdir($img_path); }else{ continue; }

phpQuery::newDocument(file_get_contents($phtml_link));
$img_links = pq("#custom-description img");

$ii = 0;
foreach($img_links as $lks){
$isrc = trim(pq($lks)->attr("src"));
if((strstr($isrc,"http://style.alibaba.com") !== FALSE) || in_array($isrc,$excude)){
continue;
}else{
GrabImage($isrc,$img_path."/".$ii.'.jpg');
$ii++;
}

}
$j++;
}
}
fclose($fp);
}
//根据请求相关参数,生产文件
//file_put_contents($base.$cat."_".$shop_id."_".$cat_id.".csv", $csv_str);

坚持原创技术分享,您的支持将鼓励我继续创作!