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)

 

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