PHP_京东数据采集

作者: php飞扬 | 来源:发表于2016-12-15 12:11 被阅读195次

    为什么要做数据采集??

    我是一个做网站开发的程序员,做一个网站想要我的网站好看,网站内容 充 实,让网站更加漂亮。如果是我们
    自己去录入数据,我们可能要去找 很多资料,还需要美工做很多的图片,这样的成本就会很高。
    而且现在京东和淘宝商品数据非常的丰富,我们可以直接写一套数据采集器,直接把数据采集到本网站来使用。

    下面直接上代码说明如何做采集

    采集时要的参数

    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
    • 这里我做的商品页的商品详情数据没有找到获取的方法,如果有知道的请分享给我。

    相关文章

      网友评论

      • f3276d10c214:我也写过获取商品详情的,不过不太会正则,用了phpquery:smile:
        php飞扬:@f3276d10c214 http://dx.3.cn/desc/商品id?cdn=2 谢谢找到了,有时间我在加进去
        php飞扬:@f3276d10c214 代码的类库我已是在网上找的,中间的一些正则和实现是一边测试一边做了修改
        f3276d10c214:商品详情在源代码匹配desc:后面的链接就是了,好奇一下,你代码的日期是怎么是2014

      本文标题:PHP_京东数据采集

      本文链接:https://www.haomeiwen.com/subject/kogwmttx.html