京东全球购的简单爬虫

使用了两个文件完成简单的数据抓取,第一个文件负责获取每个最小分类的 每一个品牌

本来获取分类的方法是

get_jd_menu()

但是发现商品的品牌很难通过商品名称获取,于是遍历了所有的品牌来确保获取每一个商品的品牌

get_jd_menu2()

读取分类主要用到了

requests
from lxml import etree

这两个库,用xpath获取html中的数据,最大的好处就是容错性好,避免了用正则的情况下遇到没有考虑到的网页结构,从而导致程序意外终止的情况。

在get_jd_menu2()中首先建立了一个字典来存储起始页面,从起始页面读取二级分类三级分类和三级分类下的所有品牌,读取到品牌页面的时候,就使用yield返回当前分类的数据给主文件,以供爬取商品数据。

jd_getlist.py文件:

#!/usr/bin/python  
# -*- coding: utf-8 -*-
#2016/08/31
import requests
import re
from lxml import etree

def get_jd_menu():
    jd_url='http://www.jd.hk/'

    r=requests.get(jd_url)

    tree=etree.HTML(r.text)

    html=tree.xpath('//*[@id="categorys-2014"]/div[2]/div/*')

    for category in html:
        #获取分类标题
        category_title=category.xpath('h3/a')[0].text
        #print('title:',category_title)
        #遍历item获取子类别
        for item in category.xpath('div/div[1]/ul/*'):

            item_title    =item.xpath('a')[0].text
            item_url    ='http:'+item.xpath('a')[0].attrib.get('href')
            yield {
                'category_title':category_title,
                'item_title':item_title,
                'item_url':item_url
            }
def get_jd_menu2():
    menu_start={
        # '个护化妆':'http://list.jd.hk/list.html?cat=1316,1625'
        # '营养保健':'http://list.jd.hk/list.html?cat=9192,9193'
        # '数码':'http://list.jd.hk/list.html?cat=652,654'
        # '家用电器':'http://list.jd.hk/list.html?cat=737,794'
         '运动户外':'http://list.jd.hk/list.html?cat=1318,12099'
        # '进口食品':'http://list.jd.hk/list.html?cat=1320,5019',
        # '汽车用品':'http://list.jd.hk/list.html?cat=6728,6742'
    }
    menu_result=[]
    for i in menu_start:
        cat1_title=i
        html=etree.HTML(requests.get(menu_start[i]).text)
        cat2_list=html.xpath('/html/body/div[4]/div/div/div/div[2]/div/div[2]/ul/li/a')
        
        for o in cat2_list:
            cat2_title    =o.text
            cat2_url    ='http://list.jd.hk'+o.attrib.get('href')
            
            html=etree.HTML(requests.get(cat2_url).text)
            cat3_list=html.xpath('//*[@id="J_selectorCategory"]/div/div[2]/div[1]/ul/li/a')
            
            for p in cat3_list:
                cat3_title    =p.attrib.get('title')
                cat3_url    =p.attrib.get('href')
                cat_id        =cat3_url[1:-5].replace('-',',')
                brand_json_url='http://list.jd.hk/list.html?trans=1&md=1&my=list_brand&cat='+cat_id
                brand_json=requests.get(brand_json_url).json()
                
                #若brand_json['brands']不为列表,那么查找下一个分类,此分类为空
                if type(brand_json['brands']) is not list:
                    continue
                
                for w in brand_json['brands']:
                    brand_id    =w['id']
                    brand_name    =w['name']
                    brand_url    ='http://list.jd.hk/list.html?cat={0}&ev=exbrand_{1}&trans=1'.format(cat_id,brand_id)
                    yield {
                        'cat_1'        :cat1_title,
                        'cat_2'        :cat2_title,
                        'cat_3'        :cat3_title,
                        'brand_id'    :brand_id,
                        'brand_name':brand_name,
                        'brand_url'    :brand_url
                    }
if __name__=='__main__':
    for i in get_jd_menu2():
        print(i)

主文件中使用from jd_getlist import get_jd_menu2来引入menu生成器,使用codecs写文件避免编码问题,使用json解析京东的jsonp接口的数据,依然使用lxml来获取页面中商品的数据。

由于京东的商品价格和评论数都是独立的jsonp协议获取的,所以需要在获取完商品信息后使用sku获取价格和评论数,效率非常低,在考虑这里可以使用协程或者多线程处理会不会快一点。

文件头定义了文件存储的路径和国家名,国家名与商品名匹配可以得到商品所属的国家。

search_page_goods函数注释掉了,因为搜索页面会有很多sku重复,所以暂时注销掉,有需要再启用。

jd_getgoods.py文件:

#!/usr/bin/python  
# -*- coding: utf-8 -*-
#2016/08/31
import requests
import json
import math
import codecs
from jd_getlist import get_jd_menu2
from lxml import etree



path='c:/xiji/python/jd/jd_160902_运动户外.csv'

all_country=(
'美国','日本','韩国','法国',
'澳大利亚','希腊','澳门','泰国',
'台湾','英国','意大利','西班牙',
'德国','印尼','越南','波兰',
'菲律宾','马来西亚','奥地利','新西兰',
'南非','保加利亚','香港','罗马',
'比利时','瑞士','叙利亚','爱尔兰',
'荷兰','丹麦','加拿大','澳洲',
'印度尼西亚','俄罗斯','新加坡','迪拜',
'阿根廷','以色列','挪威','中国'
)


'''
http://search.jd.hk/
获取页面商品
'''
# def search_page_goods(goods_items):
    # for item in goods_items:
        # goods_sku    =item.xpath('div[@class="p-cnt"]/div')[0].attrib.get('skuid')[2:]
        # goods_name    =item.xpath('div[@class="p-cnt"]/div/div[@class="p-name"]/a/em/text()')[0]
        # yield (goods_sku,goods_name)


#存储所有数据
def save_csv(data,info,filename):
    str=''
    count=0
    for i in data:
        count+=1
        if count%1000==0:
            print('line:',count)
        # str+='"{0[0]}","{0[1]}","{0[2]}","{0[3]}","http://item.jd.hk/{0[4]}.html","{0[5]}","{0[6]}","{1[0]}","{1[1]}"'.format(i,info[i[4]])+"\n"
        if i[4] in info:
            str+='"{0[1]}","{0[2]}","{0[3]}","http://item.jd.hk/{0[4]}.html","{0[5]}","{0[6]}","{1[0]}","{1[1]}"'.format(i,info[i[4]])+"\n"
        else:
            str+='"{0[1]}","{0[2]}","{0[3]}","http://item.jd.hk/{0[4]}.html","{0[5]}","{0[6]}"'.format(i)+"\n"
    f=codecs.open(filename,'w','utf-8')
    f.write(str)
    f.close()

    
'''
http://list.jd.hk/
获取页面商品
'''
def list_page_goods(goods_items):
    for item in goods_items:
        goods_sku    =item.xpath('div')[0].attrib.get('data-sku')
        goods_name    =item.xpath('div/div[@class="p-name"]/a/em/text()')
        if goods_name.__len__()==0:
            goods_name    =item.xpath('div/div/div[2]/div[1]/div[@class="p-name"]/a/em/text()')
        yield (goods_sku,goods_name[0])
        
        



'''
http://p.3.cn/prices/mgets?callback=jQuery&type=1&skuIds=J_2746071,J_1950759370
获取商品价格

获取评论1
http://club.jd.com/comment/productCommentSummaries.action?callback=jQuery&my=pinglun&referenceIds=2746071
获取评论2(详细)
http://zy.jd.hk/getCommentCountBySkuId.html?callback=jQuery&skuids=1950118101%2C2746071
'''
def goods_info(goods_list):
    result_list={}
    list_count=goods_list.__len__()
    loop_num=math.ceil(list_count/30)
    
    for i in range(loop_num):
        print(i*30,goods_list[i*30][1:3])
        #获取当前30条商品切片,取第五个值(sku),组合成列表
        skulist=[x[4] for x in goods_list[i*30:i*30+30]]
        url_price    ='http://p.3.cn/prices/mgets?callback=jQuery&type=1&skuIds=J_'+',J_'.join(skulist)
        url_comment    ='http://zy.jd.hk/getCommentCountBySkuId.html?callback=jQuery&skuids='+','.join(skulist)
        
        #遍历价格json
        for o in json.loads(requests.get(url_price).text[7:-3]):
            result_list.update(
                {
                    o['id'][2:]:[o['p']]
                }
            )

        #遍历评论json
        for o in json.loads(requests.get(url_comment).text[7:-2]):
            result_list[o['id']].append(str(o['count']))
        

    return result_list

#存储所有商品信息
goods_list=[]
#记录商品数量
goods_count=0
for menu in get_jd_menu2():
    
    # if goods_count>2000:
        # break
        
    #限定当前爬取分类
    # if menu['cat_1']!='个护化妆':
        # continue

    #获取页面
    html=etree.HTML(requests.get(menu['brand_url']).text)
    
    #获取页数
    page_count=int(html.xpath('//span[@class="fp-text"]/i')[0].text)
    
    #获取此分类所有品牌
    print(goods_count,menu['cat_1'],menu['cat_2'],menu['cat_3'],menu['brand_name'],page_count)

        
    #遍历每一页
    for page in [x+1 for x in range(page_count)]:
        list_url    =menu['brand_url']+'&page='+str(page)
        #获取document
        list_html_res=requests.get(list_url).text
        list_html_code=etree.HTML(list_html_res)
        
        #获取所有商品
        goods_items=list_html_code.xpath('//ul[@class="gl-warp clearfix"]/li')
        
        tmp=list_page_goods(goods_items)
        
        #判断国家
        for one in goods_items:
            name=one.xpath('//div[@class="p-name"]/a/em/text()')[0]
            brand_country=''
            for country in all_country:
                if country in name:
                    brand_country=country
                    break
            break
        
        #将sku name添加到goods_list
        for i in tmp:
            goods_count+=1
            goods_list.append(
                (
                    menu['cat_1'],            #品类
                    menu['cat_2'],            #子品类
                    menu['cat_3'],            #子品类
                    menu['brand_name'],        #品牌
                    i[0],                    #sku
                    i[1],                     #名称
                    brand_country            #商品国家
                )
            )

#获取商品的价格和评论数
info=goods_info(goods_list)
#写出文件
save_csv(goods_list,info,path)
最后修改:2017 年 04 月 27 日 11 : 58 PM

发表评论