使用了两个文件完成简单的数据抓取,第一个文件负责获取每个最小分类的 每一个品牌
本来获取分类的方法是
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)