python爬虫实战(三):爬取淘女郎图片

引言

爬取淘女郎图片是为了给一个朋友找找穿衣的搭配,但是期间遇到了很多问题,所以就写了这篇文章记录一下解决方案。

分析

  • 我们要爬取的网页是:淘女郎,通过分析可以看到每个淘女郎的主页:

    )

  • 随便找一个淘女郎的个人主页,有大量的图片,而且几乎没有多余的无关图片,所以直接用正则表达式可以轻易提取出图片的原始链接

  • 那么我们爬完第一页,就要翻页了,尝试点击了一下“第二页”,网址竟然没变?!看来有麻烦了:翻页操作使用的是ajax异步加载的!那么我们的主要问题就是取解决如何获取ajax异步加载的内容了

爬取异步加载内容

方法一

使用selenium+phantomjs模拟点击操作,然后异步内容加载完成后直接从html中提取内容。

这个方法比较简单,主要涉及的就是两个程序的使用。

这里不详细讲了,之后会再涉及到的~

方法二

直接获取ajax返回的内容,从中分析出淘女郎的个人主页地址。

  • 首先我们要获取ajax请求的地址。我们尝试点击“下一页”,很容易就找到了这个请求:

    其中,form data中是筛选器中填写的内容,这里我没有调整筛选器,所以大部分都是默认,或者为空。currentPage就很明显了,代表请求的页数,这样根据地址,就可以直接获取异步加载的内容了。

  • 查看一下Response的内容:

    资料还挺全的~

    那么搜索一下,看看从哪里可以找到“宴宴gy”这个人的个人主页地址:

    • 先到浏览器点看“宴宴gy”的个人主页,把地址复制下来,再到Response中搜索

    但是,,,竟然没有搜索到!

    因为既然我们可以跳转到某个人的个人主页(https://mm.taobao.com/self/aiShow.htm?spm=719.7763510.1998643336.36.Mbfu09&userId=143534224 ),她个人主页的信息必然在异步返回的内容中,不可能会凭空产生。

    通过简单观察,我们发现,这个网址后面有一个userId,而Response中也有userId,而且是一样的!

    于是,产生一个假设,userId会决定我们进入哪个淘女郎的个人主页。

    很幸运,我尝试将userId替换成其他人的userId之后,证实了我的设想,那么这个问题就圆满解决了~

    这里,暂时不要考虑网址中的spm,这个东西只是淘宝用来定位的代号

爬虫实现

感觉爬虫分析过程才是最复杂的,都分析好了以后,代码还是很简单的,就不多说了~

本程序会在当前目录根据淘女郎的名字建立文件夹,存放对应图片。

由于每个淘女郎的照片太多了,笔记本硬盘空间不太够了,所以只好指定了页数爬取照片。如果想要爬取多页,只要在外层套一个关于页数的循环就好了~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# https://mm.taobao.com/search_tstar_model.htm
# ajax:https://mm.taobao.com/tstar/search/tstar_model.do?_input_charset=utf-8
# 个人主页:https://mm.taobao.com/self/aiShow.htm?spm=719.7763510.1998643336.71.Yx0DbQ&userId=

import os
import requests
import re

url_ajax = 'https://mm.taobao.com/tstar/search/tstar_model.do?_input_charset=utf-8'
url_base = 'https://mm.taobao.com/self/aiShow.htm?spm=719.7763510.1998643336.71.Yx0DbQ&userId='

params = {}
params['q'] = ''
params['viewFlag'] = 'A'
params['searchStyle'] = ''
params['searchRegion'] = 'city:'
params['searchFansNum'] = ''
params['currentPage'] = 2 # 页数
params['pageSize'] = 100

headers = {}
headers['User-Agent'] = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0'

resp = requests.get(url_ajax, params=params, headers=headers)

uni_str = resp.content.decode(resp.encoding) # unicode


pattern = re.compile('"city":"(.*?)".*?'
+ '"height":"(.*?)".*?,'
+ '"realName":"(.*?)".*?'
+ '"userId":(.*?),')

# mm的个人信息
items = re.findall(pattern, uni_str)

base_dir = os.getcwd()

for item in items:
city, height, name, id = item[0], item[1], item[2], item[3]
if (os.path.exists(name) == False):
os.mkdir(name)
path = os.path.join(base_dir, name)

url = url_base + id
mm_page = requests.get(url)

mm_str = mm_page.content.decode(resp.encoding) # unicode

mm_pattern = re.compile('img.*?src="(.*?)"')
images = re.findall(mm_pattern, mm_str)

num = 1

for image in images:
f_url = 'https:' + image.strip()
if f_url[-3:].lower() != 'jpg' and f_url[-4:].lower() != 'jpeg':
continue
print(f_url)
try:
di = requests.get(f_url)
f_path = os.path.join(path, str(num) + '.jpg')
# 图片下载,单线程,很慢
# 有些其他格式的图片也会变成jpg,可能无法打开
with open(f_path, 'wb') as si:
si.write(di.content)
num += 1
except:
print('Failed:' + f_url)

多进程

运行的时候,我发现速度太慢了,所以就增加了多进程运行~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# https://mm.taobao.com/search_tstar_model.htm
# ajax:https://mm.taobao.com/tstar/search/tstar_model.do?_input_charset=utf-8
# 个人主页:https://mm.taobao.com/self/aiShow.htm?spm=719.7763510.1998643336.71.Yx0DbQ&userId=
# 多进程版本

import os
import requests
import re
import multiprocessing

url_ajax = 'https://mm.taobao.com/tstar/search/tstar_model.do?_input_charset=utf-8'
url_base = 'https://mm.taobao.com/self/aiShow.htm?spm=719.7763510.1998643336.71.Yx0DbQ&userId='

params = {}
params['q'] = ''
params['viewFlag'] = 'A'
params['searchStyle'] = ''
params['searchRegion'] = 'city:'
params['searchFansNum'] = ''
params['currentPage'] = 4 # 页数
params['pageSize'] = 100

headers = {}
headers['User-Agent'] = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0'

resp = requests.get(url_ajax, params=params, headers=headers)

uni_str = resp.content.decode(resp.encoding) # unicode


pattern = re.compile('"city":"(.*?)".*?'
+ '"height":"(.*?)".*?,'
+ '"realName":"(.*?)".*?'
+ '"userId":(.*?),')

# mm的个人信息
items = re.findall(pattern, uni_str)

base_dir = os.getcwd()

def download(item):
city, height, name, id = item[0], item[1], item[2], item[3]
if (os.path.exists(name) == False):
os.mkdir(name)
path = os.path.join(base_dir, name)

url = url_base + id
mm_page = requests.get(url)

mm_str = mm_page.content.decode(resp.encoding) # unicode

mm_pattern = re.compile('img.*?src="(.*?)"')
images = re.findall(mm_pattern, mm_str)

num = 1

for image in images:
f_url = 'https:' + image.strip()
if f_url[-3:].lower() != 'jpg' and f_url[-4:].lower() != 'jpeg':
continue
print(f_url)
try:
di = requests.get(f_url)
f_path = os.path.join(path, str(num) + '.jpg')
# 图片下载,单线程,很慢
# 有些其他格式的图片也会变成jpg,可能无法打开
with open(f_path, 'wb') as si:
si.write(di.content)
num += 1
except:
print('Failed:' + f_url)

for item in items:
p = multiprocessing.Process(target=download, args=(item, ))
p.start()