原博主地址:http://cuiqingcai.com/1001.html
原博是python2.7写的,并且随着淘宝代码的改版,原博爬虫已经不可用。
参考 http://minstrel.top/TaoBaoMM 这位博主跟我一样最近正在学习爬虫。
1 定个小目标
lcw先生听说我即将爬取美女的照片,两眼都亮了。没错,我要给他福利了(其实女生也很喜欢美女)。
所以,定个最小的目标:
1.在F盘建立美女文件夹
2.文件夹下按照淘女郎美人库默认美人排序,抓取31个美女的信息(因为一页默认是30个人,不至于太少,也能太多要不然抓取时间太多,lcw的破电脑也装不下)
3.每个以美人名字命名的文件夹下,取10张照片(内容小,别介)
2 抓取过程
进入淘女郎首页之后,点击找模特,进入我们需要爬取的页面。可以看到页面上是默认tag在美人库上。也即是有30位默认的美人出现在页面。每一位有相应的照片以及个人信息。30位美人下方,是页码和总美人数的信息。因为我也是web出身。像这种信息和数量都有变化的信息展示,肯定不是静态页面。一般都是通过js动态加载而来。通过开发者工具(google浏览器,F12, Json handle插件 感谢小伙伴告诉我这个插件),动态监控network.在查找加载信息的http时,我犯了个错误,一直以为返回的信息应该是json信息。这是我们post返回结果最常见的格式,但事实是返回的xhr信息。这就是抓包工具用得少的下场,哭。
明确了type是xhr后,很快找到了这个:
https://mm.taobao.com/tstar/search/tstar_model.do?_input_charset=utf-8
在headers里面 fromdata中查看source可以看到参数列表:
q=&viewFlag=A&sortType=default&searchStyle=&searchRegion=city%3A&searchFansNum=¤tPage=2&pageSize=100
我们只需要currentPage这个参数。
https://mm.taobao.com/tstar/search/tstar_model.do?_input_charset=utf-8¤tPage=2可以立刻看到下一页的信息。
输入这个网址,借助json handle插件得到:
可以看到页面上清楚的显示了当前页面上30位佳丽的信息,以及当前页面,总共美女数目。
其实这些信息也可以通过正则匹配来得到,不过相对于直接使用js获取,肯定后者更快捷。
2 目标
1.找到模特总页数
2.找到所得页的模特的json格式的信息,这一步主要为了获取每个模特的id。只有知道id才能进入到她的主页。这里
有一个十分重要的信息,是之前我忽略了的。就是动态页面。爬虫通过url可以得到一个页面,也无法得到加载该页面
时通过参数动态加载的内容。所以,这里想要通过url直接得到模特的信息内容是不可能的。这是静态页面与动态
页面的区别。
3.找到相册总页数
4.找到所得相册页的json格式的信息。与第二步相似。得到每个相册的id.
5.通过相册id得到每个相册的url。通过该url爬取该相册下的内容。
(身为一个大写的直男,lcw同学在看到我的目标之后,提出了更进一步的要求:要筛选出理想身高理想颜值的mm。好,
后期为你实现。)
3 按照目标步骤写程序
3.1 根据https://mm.taobao.com/tstar/search/tstar_model.do?_input_charset=utf-8¤tPage=1可以得到上述
图片中展示的信息。程序如下:
#!usr/bin/env/python #encoding:UTF-8 import urllib import requests import json import os import re import time currentPage = 1 headers = { 'User-Agent': r'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT) ' r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3', 'Connection': 'keep-alive' } url = 'https://mm.taobao.com/tstar/search/tstar_model.do?_input_charset=utf-8' try: data = urllib.parse.urlencode({"currentPage":currentPage}).encode('utf-8') request = urllib.request.Request(url,data = data, headers = headers) res = urllib.request.urlopen(request).read().decode('gbk') print(res) except urllib.error.URLError as e: if hasattr(e, "reason"): print("连接失败,错误原因:",e.reason)
注意,这里的res是使用gbk。这个也是通过network中查看response得到的。
3.2 点击一位模特(以朱琳为例)的主页面,得到地址https://mm.taobao.com/self/model_album.htm?
spm=719.7800510.a312r.17.xcy6bS&user_id=176817195。spm不知道是什么暂时不管。user_id已经通过上面的json
得到了。那么怎么得到动态加载得到的相册信息呢。F12,刷新查看,得到返回值为xhr的一个请求地址:https://mm.taobao.com/self/album/open_album_list.htm?_charset=utf-8&user_id%20=176817195。
在地址栏输入上面的地址得到图片:
以上信息虽然不是json格式的信息,但是我们欣喜的发现,所需要的信息全部在这里。用爬虫获取上述url的html内容
相册ID和相册总页数都得到了。其实跟json差不多。
多说一句。本来我查看原始页面,不是这个中间请求页面时,想要根据下面这个获得总的相册页数:
查看:
的源码:
之前是希望通过上述代码得到总页面数'9'. 发现通过https://mm.taobao.com/self/model_album.htm?user_id=17681
7195无法获取上述Html.
只能找到加载该页面时的动态请求https://mm.taobao.com/self/album/open_album_list.htm?_charset=utf-8&user
_id%20=176817195的源码,从而得到:
老实说这种方法真的很笨。不知道有没有更简易的方式。
找到相册ID后。,通过https://mm.taobao.com/self/album_photo.htm?user_id=176817195&album_id=10000962815
点击进入相册,同样查找xhr文件,找到:https://mm.taobao.com/self/album/album_photo_list.htm?user_id=
176817195&album_id=301783179&album_flag=0,直接打开该连接源码找到:
https://mm.taobao.com/album/json/get_album_photo_list.htm?user_id=176817195&album_id=10000794223&page=1
这总算找到了相册的信息。
这个信息简直要什么有什么啊。
好了。
现在开始写程序:
#!usr/bin/env/python #encoding:UTF-8 import urllib import requests import json import os import re import time class MMSpider: def __init__(self): self.__code_type = 'gbk' self.__http = 'http:' # 美人库动态加载xhr数据的url self.__url = 'http://mm.taobao.com/tstar/search/tstar_model.do?_input_charset=utf-8' # 模特主页地址 self.__person_url = 'http://mm.taobao.com/self/aiShow.htm?userId=' #相册地址 self.__all_album_url = 'https://mm.taobao.com/self/album/open_album_list.htm?_charset=utf-8&user_id%20=' # 具体相册地址 self.__pic_url = "https://mm.taobao.com/album/json/get_album_photo_list.htm?user_id=" # 存储照片的基地址 self.__save_path = 'F:\Beauty' # 想要获取的页数 self.__total_page = 1 # 当前正在提取的页数 self.__currentPage = 1 # 找到具体album的id的正则表达式 self.__album_id_pattern = re.compile('''<h4>.*?album_id=(.*?)&''', re.S) # album有多少的的正则表达式 # 找到具体album的id的正则表达式 self.__album_page_pattern = re.compile('''<input name="totalPage" id="J_Totalpage" value="(.*?)"''', re.S) #根据动态请求获取需要的第X页的json数据,找出userId def get_person_dict(self, currentPage): try: data = { "currentPage":currentPage } data = urllib.parse.urlencode(data).encode('utf-8') request = urllib.request.Request(self.__url, data=data) response = urllib.request.urlopen(request) result = response.read().decode(self.__code_type) return json.loads(result) except urllib.error.URLError as e: print('美人动态加载信息出错',e.reason) # 根据得到的userID,找到相册的总页数 def get_album_page(self, userId): try: all_album_url = self.__all_album_url+str(userId) res = urllib.request.urlopen(all_album_url) html = res.read().decode(self.__code_type) return re.search(self.__album_page_pattern, html).group(1) except urllib.error.URLError as e: print('动态加载相册总信息出错',e.reason) return None # 由得到的相册总页数范围内指定的页数,获取该页所有相册的ID def get_album_ids(self, userId, page): try: all_album_url = self.__all_album_url + str(userId) + "&" + str(page) request = urllib.request.Request(all_album_url) response = urllib.request.urlopen(request) html = response.read().decode(self.__code_type) # 提取该页中album的id return re.findall(self.__album_id_pattern, html) except urllib.error.URLError as e: print("提取相册id出错了!", e.reason) # 找到一个相册内的图片有多少页 def get_pic_page(self, userId, albumId): try: # 先得到这个相册一共有多少页 url = self.__pic_url + str(userId) + "&album_id=" + str(albumId) response = urllib.request.urlopen(url) result = json.loads(response.read().decode(self.__code_type)) return result["totalPage"] except urllib.error.URLError as e: print(e.reason) return None # 根据相册图片页数获取指定页数的图片信息 def get_img_url(self, person, j, album_id): url = self.__pic_url + str(person["userId"]) \ + "&album_id=" + str(album_id) \ + "&page=" + str(j) try: response = urllib.request.urlopen(url, timeout=5) result = response.read().decode(self.__code_type) imgs_url = json.loads(result)["picList"] return imgs_url except TimeoutError as e: print('1',e.strerror) except urllib.error.URLError as e: print('2',e.reason) except BaseException as e: print('3',e.args) # 保存model的个人信息 def save(self, searchDOList): for person in searchDOList: dir_path = self.__save_path+'\\'+person['realName'] if self.mkdir(dir_path): txt_path = dir_path+'\\'+person['realName']+'.txt' self.write_txt(txt_path, person) self.save_imgs(person,dir_path) def mkdir(self, dir_path): if(os.path.exists(dir_path)): return False else: os.mkdir(dir_path) return True def write_txt(self,txt_path, person): person_url = self.__person_url+str(person['userId']) content = "姓名:" + person["realName"] + " 城市:" + person["city"] \ + "\n身高:" + str(person["height"]) + " 体重:" + str(person["weight"]) \ + "\n喜欢:" + str(person["totalFavorNum"]) \ + "\n个人主页:" + person_url with open(txt_path, 'w',encoding='utf-8')as file: print('正在保存%s的文字信息'%(person['realName'])) file.write(content) file.close() def save_imgs(self, person, dir_path): album_page = self.get_album_page(person['userId']) print(album_page) img_index = 1 for i in range(1, int(album_page)+1): album_ids =self.get_album_ids(person["userId"],i) for album_id in album_ids: pic_page = self.get_pic_page(person["userId"],album_id) for j in range(1, int(pic_page)+1): img_urls = self.get_img_url(person,j,album_id) for img_url in img_urls: try: url = self.__http+img_url["picUrl"] res = urllib.request.urlopen(url, timeout =5) with open(dir_path+'\\'+str(img_index)+'.jpg','wb') as file: file.write(res.read()) if img_index % 10 ==0: print('sleep 1 second') time.sleep(1) if img_index>=11: print('%s已保存11张辣照'%person['realName']) file.close() return img_index +=1 except TimeoutError as e: print('1',e.strerror) except urllib.error.URLError as e: print('2',e.reason) def start(self): print("开始!") opener = urllib.request.build_opener() opener.addheaders = [("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36")] urllib.request.install_opener(opener) for i in range(self.__total_page): dict_result = self.get_person_dict(self.__currentPage) searchDOList = dict_result["data"]["searchDOList"] # 保存所有本页中MM的信息 self.save(searchDOList) self.__currentPage += 1 if __name__=="__main__": spider = MMSpider() spider.start()