一、最近刚好想换手机,然后就想知道京东上心仪的手机价格如何,对比手机价格如何,以及相应的历史价格,然后就用Python requests+MySQLdb+smtplib爬取相关的数据
二、关于实现的主要步骤:
1、根据京东搜索页面,搜索某型号(如小米手机)的自营手机,得出该型号在京东自营上的链接url
2、根据该url,将小米手机的京东自营的所有种类的url均爬取下来,存在列表或者字典里
3、根据找到的所有商品的url,爬取商品对应的价格,商品的简介
4、将以上相关信息插入数据库,并根据数据库下一条目与上一条目的价格对比,若产品降价了,则发相关email到指定用户进行通知
三、Python实现
1、如何获得京东自营的某型号手机的大类url:
首先,在京东自营上搜索某手机时,出现的结果列表并不会显示该品牌手机的所有型号手机,如可能会显示小米5 黑色32G,但可能不会显示小米5 白色 32G这一型号,这一型号在我们点击小米5 黑色32G链接进入下一级页面后会有相应的链接,所以我们第一步先要爬出所有该品牌的大类链接,再由大类链接去找到所有的该品牌的所有机型。
脚本实现如下:调用webdriver的PhantonJS, 进行JS渲染,从而得到页面的html,再运用正则判断商店名是否为该品牌的自营手机店,从而确定抓取哪些url
def get_href_list(self, search_url, shop_name):
'''得到京东某品牌自营的各种乐视手机的型号大类的url列表'''
dv = webdriver.PhantomJS()
a = search_url
dv.get(a)
time.sleep(4)
html = dv.execute_script("return document.documentElement.outerHTML")
aa = open('tey.txt','w+')
aa.write(html)
aa.close()
bb = open('tey.txt' , 'r+')
bbb = bb.read()
bb.close()
re_cmp = re.compile('', re.S)
re_cmp_shop2 = re.compile('(.*?)', re.S)
cc = re.findall(re_cmp , bbb)
aa = open('tey_1.txt','w+')
for i in cc:
aa.write(i + '\n')
aa.close()
return_href_list = []
dd= re.findall(re_cmp_shop, bbb)
aa = open('tey_2.txt','w+')
print 'url is %s, shop name is %s'%(search_url,shop_name)
for jj in range(len(dd)):
ddd = re.findall(re_cmp_shop2, dd[jj])
if len(ddd) !=0 and ddd[0] == shop_name :
return_href_list.append('https://item.jd.com/' + cc[jj] + '.html')
aa.write(ddd[0] + '\n' + 'https://item.jd.com/' + cc[jj] + '.html' + '\n\n')
aa.close()
self.return_list = return_href_list
return return_href_list
2、抓到大类url后,需要进一步根据大类url,抓取该品牌所以机型Url,及相应的价格,简介等。首先也是对大类中的每个url进行JS渲染,再根据正则表达式抓取相应的简介,价格是抓包发现由特定的url进行交互实现的价格数值的传递,因此对该url进行调用即可得到特定机型的价格,(参数为特定机型的京东item id),最后将所有的信息存入一个列表中。
def get_href_info(self, href_list):
'''用来根据给定的大类url来找出相对应的所有不同型号的乐视手机的url及对应的规格说明及相应的价格'''
final_href = []
final_return_result = []
count = 0
for single_href_list in href_list:
count += 1
try:
dv = webdriver.PhantomJS()
a = single_href_list
dv.get(a)
time.sleep(4)
html = dv.execute_script("return document.documentElement.outerHTML")
aa = open('leshi_%s.txt'%count,'w+')
aa.write(html)
aa.close()
re_compile_info = re.compile('(.*?)', re.S)
full_info = re.findall(re_compile_info, html)[0]
re_compile = re.compile('colorSize: (.*?)],', re.S)
cc = re.findall(re_compile, html)[0]
except:
dv = webdriver.PhantomJS()
a = single_href_list
dv.get(a)
time.sleep(4)
html = dv.execute_script("return document.documentElement.outerHTML")
aa = open('leshi_%s.txt'%count,'w+')
aa.write(html)
aa.close()
re_compile_info = re.compile('(.*?)', re.S)
full_info = re.findall(re_compile_info, html)[0]
re_compile = re.compile('colorSize: (.*?)],', re.S)
cc = re.findall(re_compile, html)[0]
re_compile1 = re.compile('{(.*?)}', re.S)
ccc = re.findall(re_compile1, cc)
if len(ccc[0]) == 0:
info_list = []
info_list.append(full_info)
info_list.append(re.findall(re.compile('.com/(.*?).html' ,re.S), a)[0])
final_href.append(info_list)
else:
re_item_id_compile = re.compile('"SkuId":(.*?),', re.S)
for i in ccc:
item_id = re.findall(re_item_id_compile, i)[0]
re_compile_info = re.compile('(.*?)', re.S)
html = urllib.urlopen('https://item.jd.com/' + item_id + '.html')
html = html.read()
full_info = re.findall(re_compile_info, html)[0]
info_string = full_info
info_list = []
info_list.append(info_string)
info_list.append(item_id)
final_href.append(info_list)
aa = open('leshi.txt','w+')
for kk in final_href:
hhhh = []
print kk[0]
print kk[1]
try_time = 10
while try_time>0:
try:
a = ['https://p.3.cn/prices/mgets?type=1&pduid=1557983493&pdpin=&pdbp=0&skuIds=J_','https://p.3.cn/prices/mgets?type=1&pduid=1557983493&pdbp=0&skuIds=J_', 'https://p.3.cn/prices/mgets?type=1&pduid=1557983493&skuIds=J_', 'https://p.3.cn/prices/mgets?type=1&area=1_72_4137_0&pdtk=&pduid=1557983493&pdpin=&pdbp=0&skuIds=J_']
aaa = a[random.randint(0,4)] + str(kk[1])
b=urllib.urlopen(aaa)
c=b.read()
d=eval(c)
kk.append(d[0]['p'])
break
except:
try_time = try_time - 1
print 'fail %s time in href %s'%((10 -try_time), str(kk[1]))
time.sleep(random.randint(1,3))
if len(kk) == 2:
kk.append('Not captured')
aa.write(kk[0] + ' ' + 'https://item.jd.com/' + str(kk[1]) + '.html' + ' ' + kk[2])
aa.write('\n\n')
hhhh.append(str(kk[1]))
hhhh.append(kk[0])
hhhh.append(kk[2])
hhhh.append('https://item.jd.com/' + str(kk[1]) + '.html')
hhhh.append( time.strftime("%Y-%m-%d,%H:%M:%p", time.localtime()))
final_return_result.append(hhhh)
aa.close()
self.return_list = final_return_result
return final_return_result
3、调用Mysql数据库将相关数据插入数据库:
使用的MySQLdb数据库,需要注意以下几点:
(1)、注意数据库的编码,默认为latin编码,这对中文编码是会存在异常的,需要将其修改为utf-8编码,具体修改是在MYSQL的配置文件里,就不赘述了;
(2)、MYSQL插入表时不支持变量插入,需要自己实现做好处理,表名的各事业有相关要求
(3)、执行语句时,如插入语句,对变量的格式(带”的字符创或不带”的整型)需要自己多留意
def connect_sql(self, data_list, databse_name,):
'''将爬到的相关数据插入数据库'''
conn = MySQLdb.connect(host = 'localhost', user = 'root', passwd = '', charset = 'utf8')
cur = conn.cursor()
database_create = 'create database if not exists %s'%databse_name
cur.execute(database_create)
conn.select_db(databse_name)
table_name = 'price_' + time.strftime("%Y_%m_%d_%H_%M_%p", time.localtime())
cur.execute('create table %s (item_id varchar(20) primary key not null, machine_name varchar(100),machine_price varchar(50),item_url varchar(50), time varchar(50))'%table_name)
conn.commit()
for ii in data_list:
try:
insert_sql = 'insert ignore %s values(\'%s\',\'%s\',\'%s\',\'%s\',\'%s\')'%(table_name, ii[0], ii[1].decode('gbk').encode('utf-8'), ii[2], ii[3], ii[4])
cur.execute(insert_sql)
except:
insert_sql = 'insert ignore %s values(\'%s\',\'%s\',\'%s\',\'%s\',\'%s\')'%(table_name, ii[0], ii[1].encode('utf-8'), ii[2], ii[3], ii[4])
cur.execute(insert_sql)
conn.commit()
cur.close()
conn.close()
4、邮件通知事件:
从数据库的表中select数据,得到价格,与之前时间的价格对比,若有降价,发送email
msg = MIMEMultipart()
msg[‘from’] = ‘**@163.com’
msg[‘to’] = ‘***@qq.com’
email_info = u’%s 降价了,现在价格是%s 元,速速买’%(name, aaa[0][0])
msg[‘subject’] = Header(email_info,’utf-8’)
server = smtplib.SMTP(‘smtp.163.com’)
server.login(‘@163.com’,’***’)
error=server.sendmail(msg[‘from’], msg[‘to’],msg.as_string())
server.close
5、之后的定时爬取价格:
第一次完成爬取数据后,之后需要定时的爬取价格,由于手机型号,url,简介等都在第一次已经确定了,故而之后只需要根据这些信息去爬取可能会变化的价格就OK了,所以写了个函数实现价格爬取
根据爬去的商品的京东id,去调用京东的特定url,返回价格,并在数据库建立新表,插入数据库,其他的插入项如id等则等同于之前。
def get_price(self, all_info_list,databse_name):
'''根据数据库已有的一个表数据来爬下一个时间的商品价格'''
cur_time = time.strftime("%Y_%m_%d_%H_%M_%p", time.localtime())
agen_list = ['Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0', 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0','Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko)', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0']
hea ={
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0',
'Host':'p.3.cn',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language':'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3','Accept-Encoding':'gzip, deflate','Content-Length':'94'
}
for i in all_info_list:
item_id = i[0]
try_time = 10
while try_time>0:
try:
a = ['https://p.3.cn/prices/mgets?type=1&pduid=1557983493&pdpin=&pdbp=0&skuIds=J_','https://p.3.cn/prices/mgets?type=1&pduid=1557983493&pdbp=0&skuIds=J_', 'https://p.3.cn/prices/mgets?type=1&pduid=1557983493&skuIds=J_', 'https://p.3.cn/prices/mgets?type=1&area=1_72_4137_0&pdtk=&pduid=1557983493&pdpin=&pdbp=0&skuIds=J_']
aa = a[random.randint(0,4)] + str(item_id)
hea['User-Agent'] = agen_list[random.randint(0,4)]
b=requests.get(aa, headers=hea)
c=b.text
d=eval(c)
i[2] = d[0]['p']
i[4] = cur_time
break
except:
try_time = try_time - 1
time.sleep(random.randint(1,3))
print 'fail %s time in href %s'%((10 - try_time),i[3])
if try_time == 0:
i[2] = 'Not captured'
self.connect_sql(all_info_list,databse_name)