爬取淘宝高清图片
老婆总是为每天搭配什么衣服烦恼,每天早上对穿什么衣服是各种纠结,我就在想,何不看一下淘宝上的模特都是怎么穿的呢,正好在学python scrapy 爬虫。何不把淘宝上的高清图爬下来呢。
环境配置:python3+scrapy
一 写 spider下tb.py
1,写start_requests函数
1 def start_requests(self): 2 return [scrapy.Request(url="https://www.taobao.com/", callback=self
def start_requests(self): return [scrapy.Request(url="https://www.taobao.com/", callback=self.start_search)]
从淘宝首页开始,这里我没有写headers是因为我会在middlewares中写随机更换UA和IP的middlewares.py,回调函数是start_search,这一步比较简单
2、下一步:start_search函数,这一步会让我选择爬取的关键字,然后进入淘宝的搜索列表页面,
def start_search(self,response): keyword = input("please input what do you what to seach ?").strip() keyword = urllib.request.quote(keyword) for i in range(1,2): # 这里可以优化,可以写一个自动判断是否还有下一页的函数 url = "https://s.taobao.com/search?q=" + keyword + "&s=" + str(i * 44) yield scrapy.Request(url=url,callback=self.parse_search_page) time.sleep(20)
这一步就遇到困难了,因难一,淘宝会不定时跳转到登录页面。我尝试了很多方法都没有完成淘宝的登录,这个后续要继续学习,困难二,淘宝的网页大部分是非常动太加载,得到的response 中根本根本不能用xpath和css做选择,不过可以用到正则,
下面是淘宝部分网页
<link rel="dns-prefetch" href="//res.mmstat.com" /> <link href="//img.alicdn.com/tps/i3/T1OjaVFl4dXXa.JOZB-114-114.png" rel="apple-touch-icon-precomposed" /> <style> blockquote,body,button,dd,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,hr,input,legend,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0}body,button,
全是动态加载,不过这样也好,直接用正刚提取,我发现详情页面是用uid 来标示的,所以我直接以正则表达式提取Uid
淘宝原页面代码如下:
从上图可以看出taobao把这一页的商品的Nid都放在一个列表中,这就好办了啊,用uids = re.compile('auctionNids\"\:\[\"(.*?)\"\]').findall(html)[0].split(",")这个正则把所有的列表取出来,然后拼接商品详情页面
for uid in uids
detailUrl = "https://detail.tmall.com/item.htm?id=" + uid
在这里就出错了,部是返回500的错误,排查了好久,终于发现,淘宝详情页面分两种,一种是淘宝,一种是天猫,他俩的详情页面是不相同的,这就必须要到源码去找了
下面是部分源码:
从源码去可以看到,这里面 isTmall 就是指是淘宝还是天猫,当然可以用正则表达式把这个字段提取出来,但是这个提取出来后,怎么会和前面提取的Nid 一一对应呢,不一一对应也是会出错的,而且源码中没有isTmall 这样一个列表,所以只能重新把新的方法,不能由上方的那个列表来获取Nid,通过几次的试验,下面这个正刚可以取出来,
uids_and_isTmail = re.compile(r'"nid":"(.*?)".*?"isTmall":(.*?),').findall(html)
这个正则可以取出nid号,还可以取出isTmall的值,这样就可以把详情页面的url拼接起来
uids_and_isTmail = re.compile(r'"nid":"(.*?)".*?"isTmall":(.*?),').findall(html) for uid_and_isTmail in uids_and_isTmail: if uid_and_isTmail[1] == "true": detailUrl = "https://detail.tmall.com/item.htm?id=" + str(uid_and_isTmail[0]) else: detailUrl = "https://item.taobao.com/item.htm?id=" + str(uid_and_isTmail[0])
这样把详情页面的代码拼接好后,加上异常处理,就可以让下一个函数来处理这些详情页面
parse_search_page函数代码如下:
def parse_search_page(self, response): """处理搜索页面""" html = response.body.decode("utf8", "ignore") try: # 查找uid 和是否属于天猫,因为淘宝和天猫的详情页面不一样,得到是一个tupe uids_and_isTmail = re.compile(r'"nid":"(.*?)".*?"isTmall":(.*?),').findall(html) for uid_and_isTmail in uids_and_isTmail: if uid_and_isTmail[1] == "true": detailUrl = "https://detail.tmall.com/item.htm?id=" + str(uid_and_isTmail[0]) else: detailUrl = "https://item.taobao.com/item.htm?id=" + str(uid_and_isTmail[0]) yield scrapy.Request(detailUrl, callback=self.parsePictureUrl) time.sleep(10)#友好的爬虫 except Exception as e: print(e)
接下就要编写详情页面返回的数据的函数:
我想要的是高清大图,也就是淘宝中商品详情的图片,通过查源码发现,这些图片也是动态加载的,源码中根本找不到这些高清大图的url,经过抓包分析后,发现加载动态高清图的网页存在源码中,
这里descUrl就是高清大图的url,提取到这一步就简单了,直接用re 提取就行了,pictureUrl = re.compile('descUrl.*?:.*?//(.*?)\'').findall(html)[0]
结里这里去访问时又出错了,在浏览器里打开网页能打开,scrapy 就是会报500的错,排查了好久发现,浏览器会自动加一个http://,加的这个http://不会在地址栏中显示,但是实际请求的网页会加上这个,所以又要拼接Url,代码如下:
def parsePictureUrl(self, response): """通过详情页面得到存放高清图片的网址""" html = response.body.decode("utf8", "ignore") try: pictureUrl = re.compile('descUrl.*?:.*?//(.*?)\'').findall(html)[0] #必须加http才能访问 pictureUrl = "http://" + pictureUrl yield scrapy.Request(pictureUrl, callback=self.parsePicture) except Exception as e: print(e)
这个函数返回一个的数据,里面就有各个高清图的详细下载网址,源码截图
img src 里面就是存放的各个图片的下载网址,这里可以用xpath 或css进行提取,我这里还是用的正则进行提取:
def parsePicture(self, response): """打开存放高清图片的网址后得到是一个json文件,里面有各个高清图片的详细网址,得到这些详细网址,然后交由scrapy下载""" item = TbItem() html = response.body.decode("utf8","ignore") try: downPictureUrlList = re.compile('src=.*?\"(.*?)\"').findall(html) for downPictureUrl in downPictureUrlList: item["img"] = [downPictureUrl] yield item except: print("can not find down page")
到此spider 的代码写完了,淘宝的高清图片隐藏很深,需要进行三层才能到真正的下载地址,这里面也有很多坑,接下来就是Item.py的代码
item.py 很简单,我暂时只保存图片,就只有一个字段,之后可以添加
class TbItem(scrapy.Item): img=scrapy.Field()
二 、接下来是settings 中代码
1,设置自动下载的字段和保存的位置,
import os img_dir=os.path.join(os.path.abspath(os.path.dirname(__file__)),"images") print(img_dir) IMAGES_URLS_FIELD='img' IMAGES_STORE=img_dir
2,加下自动下载图片的类
ITEM_PIPELINES = { # 'taobao.pipelines.TaobaoPipeline': 300, 'scrapy.pipelines.images.ImagesPipeline':1 }
3,ROBOTSTXT_OBEY = False
# Obey robots.txt rules ROBOTSTXT_OBEY = False
4,设置随机更换UA和IP的类
DOWNLOADER_MIDDLEWARES = { 'taobao.middlewares.RandomIpAndUserAgentMiddleware': 543, 'taobao.middlewares.TaobaoSpiderMiddleware': None, }
5,因为是scrapy 自动下载图片,所以不用自已写pipelines,但是要加上scrapy 的自动下载类
ITEM_PIPELINES = { # 'taobao.pipelines.TaobaoPipeline': 300, 'scrapy.pipelines.images.ImagesPipeline':1 }
到此settings设置完成,
在middleware中设置随机更换UA和IP的类会在另外一篇博客中写到,这里不赘述。
三 、到些整个爬虫代码完结,下面把整个spider.py附上,方便查看:
1 # -*- coding: utf-8 -*- 2 import scrapy 3 import re 4 import time 5 import urllib.request 6 from taobao.items import TbItem 7 8 9 class TbSpider(scrapy.Spider): 10 name = 'tb' 11 allowed_domains = ['tabao.com', "detail.tmall.com", "s.taobao.com", "item.taobao.com", "dsc.taobaocdn.com", 12 "img.alicdn.com"] 13 start_urls = ['https://www.taobao.com/'] 14 15 16 def start_requests(self): 17 return [scrapy.Request(url="https://www.taobao.com/", callback=self.start_search)] 18 19 20 def start_search(self,response): 21 keyword = input("please input what do you what to seach ?").strip() 22 keyword = urllib.request.quote(keyword) 23 for i in range(1,2): # 这里可以优化,可以写一个自动判断是否还有下一页的函数 24 url = "https://s.taobao.com/search?q=" + keyword + "&s=" + str(i * 44) 25 yield scrapy.Request(url=url,callback=self.parse_search_page) 26 time.sleep(20) 27 28 def parse_search_page(self, response): 29 """处理搜索页面""" 30 html = response.body.decode("utf8", "ignore") 31 try: 32 # 查找uid 和是否属于天猫,因为淘宝和天猫的详情页面不一样,得到是一个tupe 33 uids_and_isTmail = re.compile(r'"nid":"(.*?)".*?"isTmall":(.*?),').findall(html) 34 for uid_and_isTmail in uids_and_isTmail: 35 if uid_and_isTmail[1] == "true": 36 detailUrl = "https://detail.tmall.com/item.htm?id=" + str(uid_and_isTmail[0]) 37 else: 38 detailUrl = "https://item.taobao.com/item.htm?id=" + str(uid_and_isTmail[0]) 39 yield scrapy.Request(detailUrl, callback=self.parsePictureUrl) 40 time.sleep(10) 41 except Exception as e: 42 print(e) 43 44 def parsePictureUrl(self, response): 45 """通过详情页面得到存放高清图片的网址""" 46 html = response.body.decode("utf8", "ignore") 47 try: 48 pictureUrl = re.compile('descUrl.*?:.*?//(.*?)\'').findall(html)[0] 49 #必须加http才能访问 50 pictureUrl = "http://" + pictureUrl 51 yield scrapy.Request(pictureUrl, callback=self.parsePicture) 52 except Exception as e: 53 print(e) 54 55 def parsePicture(self, response): 56 """打开存放高清图片的网址后得到是一个json文件,里面有各个高清图片的详细网址,得到这些详细网址,然后交由scrapy下载""" 57 item = TbItem() 58 html = response.body.decode("utf8","ignore") 59 try: 60 downPictureUrlList = re.compile('src=.*?\"(.*?)\"').findall(html) 61 for downPictureUrl in downPictureUrlList: 62 item["img"] = [downPictureUrl] 63 yield item 64 except: 65 print("can not find down page")
四、下面对这次代码做总结:
学到的知识:
一,对整个basic spider的详细处理流程有了个清楚的认识。明白了scrapy 各函数的数据流程,
二,学会看网页源代码,淘宝网页都是动态加载,要想得到你要的东西得经过好几层的挖掘,但是总会有规律。
三,学会用异常处理。异常处理太重要了,他让程序不致于因一个url出错而停止。
四,scrapy的调试,做爬虫时,会调试真的很重要。
还需要学习的知识:
一,scrapy 日志系统,怎么记录scrapy 的日志,
二,学会模拟登录,我上次模拟登录知乎都没有出错,这次出错,不知道是什么原因,
三,学会数据库处理,但学习入mysql 再学习入mongodb
程序的不足
一、只是把图年保存到本地,后期会加入到保存到数据库的代码
二、目前只是测试了服装类,其他类未测试
三、目前只是能保存图片,如果在浏览图片时看到某个图片所展示的衣服很好看,不能根据该图片追踪到淘宝店铺,不能筛选同类型的图片
最后附上github :https://github.com/573320328/taobao