此專欄用來記錄爬各類網站商品信息的Python代碼,以便回憶~(誰讓博主記性不好咧~)
此次爬蟲是關於998KA的商品信息,如果有需要998KA.CN.csv文件的童鞋可以去我的博客上下載,可以看看998KA網站內商品信息的具體內容,方便深入理解代碼~
# !/usr/bin/env python
import re
import requests
from lxml import etree
import time
Max = 500 # 每獲取到500個商品的信息, 保存一次, 調試時可以改爲10, 方便看到結果
# xpath獲取數據的規則
get_cateid_rule = '//select[@name="cateid"]/option/attribute::value' # 從網頁源碼中獲取分類的id的xpath規則
get_catename_rule = '//select[@name="cateid"]/option/text()' # 從網頁源碼中獲取分類的名字的xpath規則
gonggao_rule = r'//div[contains(@class, "boxcon")]/span/text()' # 從網頁源碼中獲取商戶公告的標識的xpath規則
get_good_list_payload = "cateid=%s" # 發送post請求時的data部分,這裏使用了格式化的表示方法, 使用%s佔位符(類似c中的%s)
get_good_info_payload = "goodid=%s"
# 獲取信息的url
pingtai = "http://wwww.998ka.cn/"
cate_ajax = "http://wwww.998ka.cn/ajax/getgoodlist" # 獲取goods_list的url地址
good_ajax = "http://wwww.998ka.cn/ajax/getgoodinfo" # 獲取具體商品信息的url地址
# 請求頭字典
headers = {
'Cookie': "_qddaz=QD.i3wgpi.5be72m.jxyrtpbm; pgv_pvi=8245519360; pgv_si=s9958262784; Hm_lvt_d7682ab43891c68a00de46e9ce5b76aa=1563082827; __jsluid_h=6130e5c13d16e6f3382281a282d4a535; Hm_lvt_d7682ab43891c68a00de46e9ce5b76aa=1563082904; Hm_lpvt_d7682ab43891c68a00de46e9ce5b76aa=1563082904; Hm_lpvt_d7682ab43891c68a00de46e9ce5b76aa=1563082914; PHPSESSID=ggtkdgh2m11345i97lok1ce203; IESESSION=alive; tencentSig=5170108416; _qddab=3-ksdh2e.jy2kwcul",
'Origin': "http://wwww.998ka.cn",
'Accept-Encoding': "gzip, deflate",
'Accept-Language': "zh-CN,zh;q=0.9",
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
'Content-Type': "application/x-www-form-urlencoded; charset=UTF-8",
'Accept': "*/*",
'Referer': "http://wwww.998ka.cn/links/E585221043918FB0",
'X-Requested-With': "XMLHttpRequest",
'Connection': "keep-alive",
'Cache-Control': "no-cache",
'Postman-Token': "24eb553c-79d0-4bc9-90cb-9daa063950b7,b20edaf9-53f0-4e44-96cf-d6a9a4cb6c8b",
'Host': "wwww.998ka.cn",
'content-length': "10",
'cache-control': "no-cache"
}
def remove(data):
"""
去除獲取的文本中的轉義字符,方便保存
"""
return str(data).replace('\n', '').replace(' ', '') \
.replace('\t', '').replace('\r', '')
def get_all_url():
"""
將csv文件中的所有url進行分類,找出其中的店鋪url
"""
shop_url = []
with open('998KA.CN.csv', 'r', encoding='utf-8') as f:
all_lines = f.readlines() # 從文件中一行一行讀取數據,並返回一個列表
# print(all_lines)
for line in all_lines:
tmp = line.split(',') # 用逗號分隔每一行的數據,並存在數組裏
url = tmp[0]
if 'cate' in url: # 含有'cate'、'links'、'product'這些關鍵字的就是店鋪url
shop_url.append(url)
if 'links' in url:
shop_url.append(url) # 在列表的末尾添加這些url
if 'product' in url:
shop_url.append(url)
with open('998ka.txt', 'w', encoding='utf-8') as f1: # 將url存進文件中
for i in shop_url:
f1.write(i + '\n')
return shop_url
def get_goodids(session, url, cateid):
"""
根據商品列表的id,獲得下面所有商品的id和name
:param session:
:param url:
:param cateid:
:return: ids, names
"""
payload = get_good_list_payload % (cateid) # 格式化構造字符串, get_good_list_payload 這個變量中有兩個%s佔位符,
# print(payload) #在後面加上 % (cateid)後, 會自動將後面的兩個變量填充到對應位置
try:
res = session.post(cate_ajax, data=payload, headers=headers)
except Exception as e:
print(url, e)
return None
# 返回商品的id和name信息
else:
if 'option' not in res.text:
print("此分類下沒有商品", cateid)
return None, None
else:
goodlist = res.text.split('</option>')
gdid, gdname = [], []
for i in goodlist:
id = re.findall(r'\d+', i) # 將商品編號提取出來
gdid.extend(id)
name = re.sub('<.*>', '', i) # 將商品名稱提取出來
gdname.append(name)
print(gdid, gdname)
return gdid, gdname
def get_goodinfo(session, url, goodid):
"""
根據商品id,獲取商品所有信息
"""
try:
payload = get_good_info_payload % (goodid) # 構造請求數據
# print(payload)
res = session.post(good_ajax, data=payload, headers=headers)
goodinfo = res.text.encode('utf-8').decode('unicode_escape')
# print(goodinfo)
# goodinfo = res.text
# print(goodinfo)
except Exception as e:
print(res.status_code, res.text)
print(url, e)
return goodinfo
def data_together(url, goodname, sellerinfo, price, stock):
""""
這裏是將信息整合的部分, 一個商鋪頁面有很多信息, 儘量獲取他們, 獲取不到的信息, 就用一個''佔位即可
"""
try:
datalist = [
'', # id, 空着
today, # 今天的日期, 格式如20190711,可以使用time庫獲取
'', # 插入數據庫的時間, 空着即可,
goodname, # 商品標題/商品名
'', # 商品詳細信息
'', # 廠家,
'', # 有效期,
sellerinfo, # 商鋪信息, 比如聯繫方式,如果搞不到空着即可
price, # 價格
'', # 價格增長量
'', # 批發價
'', # 商品分類, 空着
'', # 空着
'998ka.cn', # 網站域名
stock, # 庫存量, 沒有可以空着
'', # 空着即可
url, # 商品所屬頁面的url,
url, # 商品詳情頁面的url, 沒有的話設置爲所屬頁面的url
]
except Exception as e:
print(url, e)
else:
datalist = list(map(remove, datalist)) # 通過內置函數map() , 將dataList這個列表中的每一個字符串進行去空格,去換行符等處理
data = '\t'.join(datalist) # 用join函數, 以'\t'爲分隔符鏈接dataList這個列表中的每一項,返回一個字符串給data
return data
def get_all_data(url, try_cnt=2):
"""
獲取某個商鋪的所有商品的信息,重複次數:2
"""
all_data = []
session = requests.session()
try:
r = session.get(url=url, headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", })
except Exception as e:
print(url, e, "連接主頁面失敗")
else:
if r.status_code != 200:
if r.status_code == 503:
if try_cnt:
time.sleep(5)
return get_all_data(url, try_cnt-1)
else:
print(url, "data爲0")
return all_data
else:
print(url, "data爲0")
return all_data
res_xpath = etree.HTML(r.text) # etree.HTML解析html內容
# 聯繫方式
qq = ''
if re.findall('uin=[0-9]+', r.text): # 找到所有匹配的字符串,並返回
qq = re.findall('uin=[0-9]+', r.text)[0].replace('uin=', '')
# 商家信息
gonggao = res_xpath.xpath(gonggao_rule)
# url一共分三種情況
if 'product' in url: # 沒有分類也沒有名稱的情況
seller = res_xpath.xpath('//div[@class="boxcon"]//text()') # 商家信息
sellerinfo = {'商家信息': seller, '商家公告': gonggao, 'qq': qq}
goodname = res_xpath.xpath('//span[@style="font-size:1.2em"]/text()') # 商品名稱
if len(goodname) == 0:
goodname = res_xpath.xpath('//span[@class="t1"]/text()')
goodid = res_xpath.xpath('//script/input[@name="goodid"]/attribute::value') # 商品ID
price = res_xpath.xpath('//label/span[@class="price"]/text()')
print(sellerinfo, goodname, goodid, price)
all_data.append(data_together(url, goodname, sellerinfo, price, ''))
else:
cateidlist = res_xpath.xpath(get_cateid_rule) # 第一個不可用,商品列表的id
catenames = res_xpath.xpath(get_catename_rule)
seller = res_xpath.xpath('//div[@class="boxcon"]/text()')
if qq != '':
if len(cateidlist) == 0: # 沒有分類有商品的情況
name_rule = '//span[@class="t1"]/text()'
id_rule = '//div/input[@name="cateid"]/attribute::value'
cateidlist.extend('0')
catenames.extend('嗯')
cateid = res_xpath.xpath(id_rule) # 獲取分類id
catename = res_xpath.xpath(name_rule) # 獲取分類名稱
if len(catename) == 0:
catename = res_xpath.xpath('//span[@style="font-size:1.2em"]/text()')
cateid = res_xpath.xpath('//script/input[@name="cateid"]/attribute::value')
cateidlist.extend(cateid)
catenames.extend(catename)
seller = res_xpath.xpath('//h1/p/text()')
# print(cateidlist, catenames, seller)
sellerinfo = {'商家信息': seller, '商家公告': gonggao, 'qq': qq}
# print(sellerinfo)
print(url, cateidlist, catenames, 'qq:', qq) # 這是分類那一欄的,以及每一類的id和名稱
for cateid, catename in zip(cateidlist[1:], catenames[1:]):
goodids, goodnames = get_goodids(session, url, cateid)
if goodids:
for goodid, goodname in zip(goodids, goodnames):
goodinfo = get_goodinfo(session, url, goodid)
# 從獲取的信息中提取單價
price = re.findall('\d+.?\d*', goodinfo)[0]
# 從獲取的信息中提取庫存
stock = re.sub('.*value="', '', goodinfo)
stock = re.sub('">","is_discount":.*', '', stock)
stock = re.findall('\d+', stock)[0]
print(price, stock)
all_data.append(data_together(url, goodname, sellerinfo, price, stock))
return all_data
def write_data(total_data):
"""
功能: 將商品信息追加到以當天時間命名的文件上
"""
with open('./data/' + today + '.txt', 'a', encoding='utf-8') as f:
for i in total_data:
f.write(i + '\n')
if __name__ == '__main__':
today = time.strftime('%Y%m%d') # 獲取當前時間
total_data = []
print(len(get_all_url()))
num = 0
# 將每個店鋪的信息存在data文件中
for url in get_all_url():
time.sleep(3) # 暫停3秒後執行程序
try:
all_data = get_all_data(url) # 獲取店鋪裏的所有信息
total_data.extend(all_data) # 將信息追加進總的信息中
if len(total_data) > Max: # 當已經保存的數據條數大於設置設數量時, 追加到文件之後
write_data(total_data)
num += 1
total_data = []
except Exception as e: # 捕獲其他所有的異常信息
print(url, e)
# 將total_data裏剩下的數據也追加到文件中
num += len(total_data)
print(num)
write_data(total_data)