您的位置:首页 > 编程语言 > Python开发

[python3.6]爬虫实战之爬取淘女郎图片

2017-10-28 17:28 671 查看
原博主地址: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[/code]在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()





                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: