为什么要做数据采集??
我是一个做网站开发的程序员,做一个网站想要我的网站好看,网站内容 充 实,让网站更加漂亮。如果是我们
自己去录入数据,我们可能要去找 很多资料,还需要美工做很多的图片,这样的成本就会很高。
而且现在京东和淘宝商品数据非常的丰富,我们可以直接写一套数据采集器,直接把数据采集到本网站来使用。
下面直接上代码说明如何做采集
采集时要的参数
1.采集的地址。
这里是采集的jd的列表页,最好是列表页的最后一级分类如:http://list.jd.com/list.html?cat=12218,12222,12243
2.采集的数量。
采集的数量要小于翻页的数量,不然多了没有。
采集方法
/**
* 京东数据的采集
*/
public function jd(){
//采集的数量
$number=10;
//采集的地址
$url='http://list.jd.com/list.html?cat=12218,12222,12243';
//实例化采集的类
$jd= new jd_collect($number);
//检查url是否有效
$is_url=$jd->checkListUrl($url);
if($is_url){
//给类赋值采集的地址
$jd->readListPage($url);
//获取列表页商品的详情页url
$list=$jd->pickGoodsLinkFromList();
//采集到的数据数组
$data=$jd->collect();
//分类的父级赋值
$parent_id=0;
//分类id赋值
$cate_id=[0,0];
//判断分类是否存在,添加
foreach ($data['cat'] as $k=>$v){
$cate_id[$k]=GoodsCategory::getName(['name'=>$v]);
if(!$cate_id[$k]){
$parent_id=GoodsCategory::add([
'name'=>$v,
]);
$cate_id[$k]=&$parent_id;
}
}
//对商品进行添加
foreach($data['item'] as $v){
$id = Goods::add([
'name'=>$v['name'],
'memo'=>$v['name'],
'sell_price'=>$v['sell_price'],
'cost_price'=>$v['cost_price'],
'market_price'=>$v['market_price'],
'sort'=>50,
'img'=>$v['img'][0],
'list_img'=>$v['img'][0],
'small_img'=>$v['img'][0],
'store_num'=>100,
'cate_id'=>$cate_id[0],
'cate_two_id'=>$cate_id[1],
'start_time'=>time(),
'end_time'=>time()+100*24*60*60,
]);
//对商品的图片进行添加
foreach ($v['img'] as $img){
GoodsImg::add([
'goods_id'=>$id,
'image'=>$img,
'list_img'=>$img,
'small_img'=>$img,
]);
}
}
}
}
京东数据采集的操作类
<?php
namespace App\Helpers\JD;
use App\Helpers\JD\collect;
use Psy\Util\Json;
/**
* @brief 京东商品列表采集器,适用于普通类,不可再分类的列表
* @author nswe
* @date 2013/12/25 13:52:19
*/
class jd_collect extends collect
{
//属性数据
private $cacheAttrData = array();
//采集数量
private $number = 20;
/**
* @brief 构造函数
*/
public function __construct($number=0)
{
if($number){
$this->number=$number;
}
}
/**
* @brief 检查列表url
*/
public function checkListUrl($url)
{
return strpos($url,'http://list.jd.com/list.html?cat=') === false ? false : true;
}
/**
* @brief 检查详情url
*/
public function checkShowUrl($url)
{
return strpos($url,'http://item.jd.com') === false ? false : true;
}
/**
* @brief 挑选分类
* @return array 根据层次返回分类
*/
public function pickCatFromList()
{
$catExp = '@<div class="trigger">(.+?)</div>@';
preg_match_all($catExp,$this->listPageHtml,$match);
if(!isset($match[0]))
{
throw new Exception('页面缺少商品分类');
}
foreach ($match[1] as $v){
$data[]=strip_tags($v);
}
return $data;
}
/**
* @brief 挑选属性
* @return array 属性数据
*/
public function pickAttributeFromList()
{
$keyExp = '@<div class="a-key">(.+?)</div>@';
$valueExp = '@<ul class="f-list">(.+?)</ul>@';
preg_match_all($keyExp,$this->listPageHtml,$matchKey);
preg_match_all($valueExp,$this->listPageHtml,$matchValue);
if(!isset($matchKey[1]))
{
throw new Exception('页面缺少商品属性名称');
}
if(!isset($matchValue[1]))
{
throw new Exception('页面缺少商品属性数值');
}
//过滤无用的数据
array_shift($matchKey[1]);//移除品牌
array_shift($matchKey[1]);//移除价格
array_shift($matchValue[1]);//移除价格
$attrData = array();
foreach($matchKey[1] as $key => $val)
{
$attrData[trim($val,':')] = trim(strip_tags(strtr($matchValue[1][$key],array('</li>' => '</li>,'))),',');
}
return $attrData;
}
/**
* @brief 挑选列表页面的商品连接
* @return array 商品详情的url
*/
public function pickGoodsLinkFromList()
{
$linkExp ='<a target="_blank" href="//item.jd.com/(.*?).html".*?>';
preg_match_all($linkExp,$this->listPageHtml,$match);
if(!isset($match[1]))
{
throw new Exception('页面缺少商品详情连接');
}
return $match[1];
}
/**
* @brief 获取商品名称从详情页面
* @return string 商品名字
*/
public function pickGoodsNameFromShow()
{
$exp = '/<h1>([^<>]*)<\/h1>/';
preg_match($exp,$this->showPageHtml,$match);
if(!isset($match[0]))
{
throw new Exception('没有找到商品名称');
}
return strip_tags($match[0]);
}
/**
* @brief 获取商品名称从详情页面
* @return string 商品描述
*/
public function pickGoodsMemoFromShow()
{
$exp = "/<div id=\"p-ad\".*?>.*?<\/div>/ism";
preg_match($exp,$this->showPageHtml,$match);
if(!isset($match[0]))
{
throw new Exception('没有找到商品名称');
}
return strip_tags($match[0]);
}
/**
* @brief 获取商品价格从API
* @param $idArray string 商品id数组,如:J_970602
* @return string 商品价格json
*/
public function getGoodsPriceFromAPI($idString)
{
// $apiUrl = 'http://p.3.cn/prices/mgets?skuIds='.trim($idString,',');
$apiUrl='http://p.3.cn/prices/get?type=1&area=1_72_4137&pdtk=HxOK%2F%2B%2Fwpjlr75zX3szVoe%2BWRMfuWsRCxWZRS2x7B84nzLc1bJwTzqlQm2fSRjIk&pduid=844111226&pdpin=&pdbp=0&skuid='.trim($idString,',');
$result = file_get_contents($apiUrl);
$result = strtr($result,array('J_' => ''));
return json_decode($result,true);
}
/**
* @brief 获取商品属性从详情页面
* @return string 商品某属性
*/
public function pickGoodsAttributeFromShow()
{
$exp = '@<ul class="detail-list">(.+?)</ul>@s';
preg_match($exp,$this->showPageHtml,$match);
return $this->cacheAttrData=[];
if(!isset($match[1]))
{
throw new Exception('没有找到商品属性');
}
$match[1] = trim(strip_tags(strtr($match[1],array('<li>' => '</li>,'))));
$tempArray = explode(',',$match[1]);
$attrArray = array();
$tmp = array();
foreach($tempArray as $key => $val)
{
$tmp = explode(':',$val);
$attrArray[$tmp[0]] = trim($tmp[1]);
}
return $this->cacheAttrData = $attrArray;
}
/**
* @brief 获取商品图片从详情页面
* @return array 商品的图片url
*/
public function pickGoodsImageFromShow()
{
$exp='<img.+data-url=\'(.*?)\'.+>';
preg_match_all($exp,$this->showPageHtml,$match);
if(!isset($match[1]) || !is_array($match[1]))
{
throw new Exception('没有找到商品图片');
}
$jdImageServerPre = 'http://img13.360buyimg.com/n0/';
foreach($match[1] as $key => $val)
{
$match[1][$key] = $jdImageServerPre.$val;
}
return $match[1];
}
/**
* @brief 获取商品规格从详情页面
* @return array 商品的规格 array(规格名称=>规格值)
*/
public function pickGoodsSpecFromShow()
{
$exp = '@<li id="choose-(?:version|color)".*?>.*?</li>@s';
preg_match_all($exp,$this->showPageHtml,$match);
$result = array();
if(isset($match[0]) && $match[0])
{
foreach($match[0] as $key => $val)
{
$val = trim(strip_tags(strtr($val,array('</a>' => '</a>,'))),',');
$temp = explode(':',$val);
if(isset($temp[1]))
{
$result[$temp[0]] = $temp[1];
}
}
}
return $result;
}
/**
* @brief 获取商品详情从详情页面
* @return string 商品的详情数据
*/
public function pickGoodsContentFromShow()
{
$exp = '@<div class="detail-content">.*<!--product-detail end-->@s';
preg_match($exp,$this->showPageHtml,$match);
if(!isset($match[0]))
{
throw new Exception('没有找到商品详情');
}
return strtr($match[0],array('data-lazyload' => 'src'));
}
/**
* @brief 获取商品重量
* @return string 商品重量
*/
public function pickGoodsWeightFromShow()
{
if(!$this->cacheAttrData)
{
$this->pickGoodsAttributeFromShow();
}
preg_match('@[\d\.]+@',$this->cacheAttrData['商品毛重'],$matchAttr);
return isset($matchAttr[0]) ? $matchAttr[0] : 0;
}
/**
* @brief 获取商品计量单位
* @return string 计量单位
*/
public function pickGoodsUnitFromShow()
{
if(!$this->cacheAttrData)
{
$this->pickGoodsAttributeFromShow();
}
preg_match('@[\d\.]+(.*)$@',$this->cacheAttrData['商品毛重'],$matchAttr);
return isset($matchAttr[1]) ? $matchAttr[1] : '千克';
}
/**
* @brief 开始采集商品
* @return array('cat' => '商品分类','attr' => '属性','item' => array(
* 'goods_no' => '商品编号','up_time' => '上架时间','weight' => '重量','unit' => '计量单位','name' => '商品名字','price' => '商品价格','img' => array(商品图片),'content' => '商品详情','spec' => '商品规格','attr' => '商品属性'
* ))
*/
public function collect()
{
$result = array(
'cat' => $this->pickCatFromList(),
// 'attr'=> $this->pickAttributeFromList(),
'item'=> array()
);
$goodsUrl = $this->pickGoodsLinkFromList();
//取列表页商品的数量
$goodsUrl=array_slice($goodsUrl,0,$this->number);
foreach($goodsUrl as $key => $val)
{
$showurl='http://item.jd.com/'.$val.'.html';
$this->readShowPage($showurl);
preg_match('@\d+@',$val,$match);
$priceObj = $this->getGoodsPriceFromAPI('J_'.$match[0]);
// $attrData = $this->pickGoodsAttributeFromShow();
$result['item'][] = array(
// 'goods_no' => $attrData['商品编号'],
// 'up_time' => $attrData['上架时间'],
// 'weight' => $this->pickGoodsWeightFromShow(),
// 'unit' => $this->pickGoodsUnitFromShow(),
'name' => $this->pickGoodsNameFromShow(),
'sell_price' => $priceObj[0]['p'],
'market_price' => $priceObj[0]['m'],
'cost_price' => $priceObj[0]['op'],
'img' => $this->pickGoodsImageFromShow(),
// 'content'=> $this->pickGoodsContentFromShow(),
// 'spec' => $this->pickGoodsSpecFromShow(),
// 'attr' => $attrData
);
}
return $result;
}
}
采集器抽象类
<?php
namespace App\Helpers\JD;
/**
* @brief 采集器抽象类
* @date 2014/1/1 20:21:11
* @author chendeshan
*/
abstract class collect
{
//已经采集到的列表页面html代码
protected $listPageHtml = '';
//已经采集到的详情页面html代码
protected $showPageHtml = '';
/**
* @brief 获取列表页面的html代码
* @param $url string 列表页面url地址
*/
public function readListPage($url)
{
if($this->checkListUrl($url) == false)
{
throw new Exception('URL不符合规范');
exit;
}
if(!$content = file_get_contents($url))
{
throw new Exception('没有采集到列表页面的html代码');
}
//转码GBK转换UTF-8
$this->listPageHtml = $this->converContent($content);
}
/**
* @brief 获取商品详情页面数据
* @param $url string 详情页面url
*/
public function readShowPage($url)
{
if($this->checkShowUrl($url) == false)
{
throw new Exception('URL不符合规范');
exit;
}
$content = file_get_contents($url);
//转码GBK转换UTF-8
$this->showPageHtml = $this->converContent($content);
}
/**
* @brief 字符串转码
* @param $content string 要转换的字符串
* @return string
*/
public function converContent($content)
{
if($this->isUTF8($content) == false)
{
return iconv('GBK','UTF-8//IGNORE',$content);
}
return $content;
}
public function isUTF8($string)
{
$e=mb_detect_encoding($string, array('UTF-8', 'GBK'));
switch($e){
case 'UTF-8' : //如果是utf8编码
return true;
break;
case 'GBK': //如果是gbk编码
break;
}
}
/**
* @brief 检查商品列表url的合法性
* @param $url string
* @return boolean
*/
abstract public function checkListUrl($url);
/**
* @brief 检查商品详情url的合法性
* @param $url string
* @return boolean
*/
abstract public function checkShowUrl($url);
/**
* @brief 采集商品信息
* @return array
*/
abstract public function collect();
}
我在本次采集的操作过程中遇到几个问题给总结下。
-
注意采集来数据的页面的编码格式,要主相应的判断和转换
-
注意正则的匹配。采集页面有可能调整对应的正则就需要重新。
-
注意找出那一部分是页面加载的数据,那一部分是ajax加载过来的数据,要分清楚,不如是得不到数据的
这里我们可以用火狐浏览器的firebug来查看,那些数据是可以用正则来匹配的
图片.png
- 不能用正则匹配的就需要用ajax的请求获得数据如商品详情页商品的价格
这里获取数据的时间我们要注意下,我在做这里的时间第一次直接用http://p.3.cn/prices/mgets?skuIds= 就可以获取数据,但是后面京东需要验证菜返回数据所以我们可以复制firebug中的连接,进行请求就会有数据了。
图片.png
- 这里我做的商品页的商品详情数据没有找到获取的方法,如果有知道的请分享给我。
网友评论