python爬虫 |
您所在的位置:网站首页 › 怎么查看淘宝商品销售量 › python爬虫 |
(一)项目目标 1. 获取天猫店铺 “探路者官方旗舰店” 所有商品的名称、价格以及销量。 说明:本次项目目标是从一个热门店铺排行榜中随机选择的,没有任何针对性的含义。 该排行榜的网址为: http://www.xlphb.cn/index.php?c=shop 2. 该店铺的截图如下: 3. 左上角有一个 “所有商品” 的链接,点击进入如下截图: 4. 下方截图可以看到数据仍然是分页排列的,一共有14页的数据。 (二)网页分析 1. 首先还是打开charles,刷新页面,通过charles的搜索功能,找到目标数据的请求 2. 确认该请求是否全部包含目标数据 从下方截图可以看到,返回的数据格式是html,经手动确认,该请求包含该页的所有商品信息。 3. 分析请求的具体情况 从下方截图可以看到,该请求的具体信息是: url: https://toread.tmall.com/i/asynSearch.htm?_ksTS=1529821691770_124&callback=jsonp125&mid=w-18307703560-0&wid=18307703560&path=/search.htm&search=y&spm=a1z10.3-b-s.w4011-18307703560.430.4ee0605f0KyPWs&scene=taobao_shop&pageNo=3&tsearch=y 请求类型: GET 4. 可以看到,这个请求有非常多的query参数。 经手动测试,有一些参数即使没有,也可以拿到数据,最终精简到如下url: https://toread.tmall.com/i/asynSearch.htm?mid=w-18307703560-0&pageNo=2 注意,最后一个pageNo是指的页数,这样我们就可以直接通过改变pageNo,就能获得到不同页面的数据了。 5. 然后就是对页面数据进行解析时有一个坑。 一般来讲,我比较擅长使用css和正则表达式语法来选择数据,但是请看以下截图: 目标数据就在这些div和dl里面,但是淘宝设置的class 使用了这样的形式 "\"item" \", 这样我在用pyquery解析的时候总是要么要错,要么拿到数据。 但是没关系,下面的代码中,我使用了xpath语法来选择,躲开这个坑。 6. 网页基本分析完毕,请求只有一个,比较简单。但是要注意一下两点: - 不知道淘宝的具体规则是什么,发送一个上面的请求并不一定能够获取到目标数据,但是只要重复不断发送,就能获取到。 - headers一定要写全。 (三)核心代码实现 1. 一些需要使用的模块和常量 import requests from requests.exceptions import RequestException from scrapy.selector import Selector import csv import random from requests.exceptions import ConnectionError s = requests.session() # csv结果文件的存储文件名 filename = '爬取结果.csv' # 定义代理池url,可以从代理池项目文件中找到接口 PROXY_POOL_URL = 'http://127.0.0.1:5555/random'2. 随机切换User-Agent: 我是在项目中加入了一个agent.txt文件,里面存储了一些User-Agent可供使用。 # ag作为开关,仅第一次读取,之后就从ag里面拿 ag = None def change_agent(): global ag if not ag: with open('agent.txt') as f: ag = f.readlines() return ag[random.randint(1,866)].strip()3. 使用代理池 代理池的代码就不贴出来了,我是从github找的其他大神写的,从公开渠道获取免费代理后存储入redis的一个项目。 下面的代码使用前,我已经打开了redis和代理池的代码。 # 定义获取代理的函数 def get_proxy(): """从代理池中取出一个代理""" try: response = requests.get(PROXY_POOL_URL) if response.status_code == 200: return response.text return None except ConnectionError: return None def build_proxy(): """将代理池中取出的代理构造成完整形式""" proxy = get_proxy() if proxy: return { 'http': 'http://' + proxy, #'https': 'https://' + proxy } else: return None4. 上面网页分析的部分有说到,发送该请求并不一定会获取到目标数据,如果没有获取到,需要重新发送。 这里定义个一个辅助函数,用于判断获取到的网页是否包含目标数据。 注意这里用的scrapy库里面的Selector模块,方便使用xpath语法选取数据。 当然还有其他库可以使用xpath语法,我对scrapy比较熟悉,所以使用这个。 def decide_if_loop(html): """通过解析要拿到的页面第一个数据,判断是否拿到真正的页面,如果假的页面,就返回False""" selector = Selector(text=html) data = selector.xpath('/html/body/div/div[3]/div[1]/dl[1]/dd[2]/a/text()').extract_first() return False if not data else selector5. 下面是获取页面的函数: 注意该函数内,调用了上面的切换User-Agent 和 使用代理池的函数。 def get_page(url): """ 1. user-agent 不断切换 2. 直接使用代理池中的代理来请求 """ headers = { 'User-Agent':change_agent(), 'Referer':'https://toread.tmall.com', 'accept':'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01', 'x-requested-with':'XMLHttpRequest', 'accept-encoding':'gzip, deflate, br', 'accept-language':'zh-CN,zh;q=0.9,en;q=0.8' } try: response = s.get(url, headers=headers, verify = False, proxies=build_proxy()) except: response = s.get(url, headers=headers, verify = False) if response.status_code == 200: return response.text else: print("请求错误:{}".format(response.status_code))6. 获取到真实页面之后,就需要进行页面解析 注意传入的参数是selector,就是前面的判断是否需要循环的函数中的返回值。 因为该函数内已经对页面进行解析后得到selector了,所以这里就不再重复,直接传入使用。 def parse_detail(selector): """从拿到的真实页面中,解析出商品名,销量和价格""" data = [] # 两个for循环解析一个html页面 for i in range(1,13): for j in range(1, 6): title = selector.xpath('/html/body/div/div[3]/div['+str(i)+']/dl['+str(j)+']/dd[2]/a/text()').extract_first() price = selector.xpath('/html/body/div/div[3]/div['+str(i)+']/dl['+str(j)+']/dd[2]/div/div[1]/span[2]/text()').extract_first() num = selector.xpath('/html/body/div/div[3]/div['+str(i)+']/dl['+str(j)+']/dd[2]/div/div[3]/span/text()').extract_first() # 这个判断用于防止最后一页商品不全时,或者页面出现任何错误,值可能为空的情况 if title and price and num: data.append([title.strip(), price.strip(), num.strip()]) return data7. 下面定义了两个函数,将解析到的数据存储本地csv文件。 def save_to_csv(rows, filename): if rows: with open(filename, "a") as f: f_csv = csv.writer(f) for row in rows: f_csv.writerow(row) def write_csv_headers(filename): csv_headers = ["商品名称", "价格", "销量"] with open(filename, "a") as f: f_csv = csv.writer(f) f_csv.writerow(csv_headers)8. 下面的一些函数将整个代码串在一起 def loop(url): html = get_page(url) selector = decide_if_loop(html) if not selector: loop(url) else: data = parse_detail(selector) save_to_csv(data, filename) def get_urls(): urls = [] base_url = 'https://toread.tmall.com/i/asynSearch.htm?mid=w-18307703560-0&pageNo=' for i in range(1, 15): urls.append(base_url + str(i)) return urls def main(): write_csv_headers(filename) for url in get_urls(): loop(url) if __name__=="__main__": main()(四)项目结果以及经验教训 经过不断的失败后重试,项目终于成功的获取了所有的14页的数据,共700多条,也就是该天猫店铺所有商品的名称、价格以及销量。展示截图如下: 经验教训: 淘宝的坑还是比较多的,在尝试的过程中: 1. 如果直接请求页面的url,可以直接获得到数据,但是全部是假数据; 2. 真实的请求也需要多次获取才能拿到目标数据; 3. 如果不使用代理池,自己的ip很容易被封掉。 4. 项目代码重用率比较低,如果要爬其他店铺的商品信息,如要重新进行分析。 本文仅供学习交流使用,请勿将其用于违法目的。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |