Python爬蟲-爬998KA網站的所有商品信息

此專欄用來記錄爬各類網站商品信息的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)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章