老婆總是為每天搭配什么衣服煩惱,每天早上對穿什么衣服是各種糾結,我就在想,何不看一下淘寶上的模特都是怎么穿的呢,正好在學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