m.mzitu.com网站爬虫的改进

m.mzitu.com网站爬虫的改进

友情提示:本文内容少儿不宜。

下午闲着没事又把之前写的mzitu.com网站的Python爬虫程序改进了一下,顺便再复习一下Python。

之前的那个程序只具有理论上的可运行性,因为我自己也没有完全运行过一遍,今天运行了几个小时,结果中间多次出现因为网络问题而中断。另外之前的程序里面又一个总页数需要手动确认,比较烦心,所以就增加了一个爬取总页数的函数。虽然很啰嗦,但是为了完整性,下面将再次完整地介绍这个改进版的程序。

全局取消证书验证

由于Python 升级到 2.7.9 之后引入了一个新特性,当使用urllib.urlopen打开一个 https 链接时,会验证一次 SSL 证书。而当目标网站使用的是自签名的证书时就会抛出此异常。因此需要使用下面的代码:

1
2
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

带请求的图片下载函数

编写一个带请求下载图片的函数,该函数需要两个参数,图片的链接和保存的名字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def download_pic(url, name):
import requests
headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) " \
"A®ppleWebKit/537.36 (KHTML, like Gecko) " \
"Chrome/66.0.3359.181 Safari/537.36",
"Accept": "image/webp,image/apng,image/*,*/*;q=0.8",
"Referer": "http://m.mzitu.com/138401",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"}
res = requests.get(url, headers=headers)
res.raise_for_status()
playFile = open(name, 'wb')
for chunk in res.iter_content(100000):
playFile.write(chunk)
playFile.close()

根据某套妹子图主页链接获取某个妹子的所有图片链接的函数

编写一个根据某套妹子图主页链接获取某个妹子的所有图片链接的函数

根据某套妹子图主页链接判断该套图的页数的函数

1
2
3
4
5
6
7
8
9
10
11
def get_page(url):
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
html = urlopen(url)
bsObj = BeautifulSoup(html, "html.parser")
page_temp = bsObj.findAll("span", {"class":"prev-next-page"})
for p in page_temp:
page = re.findall(r"1/(.+?)页", p.get_text())
print("该套图一共",page[0],"页")
return int(page[0])

编写一个函数获取一套图的所有链接

1
2
3
4
5
6
7
8
9
10
11
def get_pic_link(url):
from bs4 import BeautifulSoup
from urllib.request import urlopen
link = []
for i in range(1, get_page(url)):
url_temp = url + "/" + str(i)
html = urlopen(url_temp)
bsObj = BeautifulSoup(html, "html.parser")
for j in bsObj.figure.findAll("img"):
link.append(j.attrs["src"])
return link

获取总页数

这个函数就是新增的,用于爬取总页数的。也就是之前的那个183。现在最近的是187。

1
2
3
4
5
6
7
8
9
def get_total_pages():
from bs4 import BeautifulSoup
from urllib.request import urlopen
import re
url = "http://www.mzitu.com/"
html = urlopen(url)
bsObj = BeautifulSoup(html, "html.parser")
page_temp = bsObj.findAll("a", {"class":"page-numbers"})
return int(page_temp[3].get_text())

获取每个妹子的套图链接的函数并输出为一个mzitu.csv文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def get_all_link():
from bs4 import BeautifulSoup
from urllib.request import urlopen
import re
import pandas as pd
link = []
name = ['url']
for i in range(1, get_total_pages()):
url = "http://m.mzitu.com/page/" + str(i) + "/"
html = urlopen(url)
bsObj = BeautifulSoup(html, "html.parser")
link_list = bsObj.findAll("a", {"href": re.compile("http://m.mzitu.com/[0-9]")})
for m in link_list:
if m not in link:
link.append(m.attrs["href"])
print("第"+str(i)+"页完成!")
print("一共%d组图片" % len(link))
test = pd.DataFrame(columns = name, data = link)
test.to_csv("mzitu.csv")
return link

根据一个套图链接返回其名称

这个是用来创建子文件夹命名文件夹的。

1
2
3
4
5
6
7
8
9
def get_title(url):
from bs4 import BeautifulSoup
from urllib.request import urlopen
html = urlopen(url)
bsObj = BeautifulSoup(html, "html.parser")
title = ""
for i in bsObj.findAll("h2", {"class":"blog-title"}):
title = i.get_text()
return title

根据给定字符串创建字文件夹并设定子文件夹为工作目录的函数

这个函数的思路是,首先根据套图的标题创建子文件夹,然后把工作目录设定为子文件夹。

1
2
3
4
5
6
7
8
9
10
11
12
def make_and_cd(foldername):
import os
def mkdir(path):
folder = os.path.exists(path)
if not folder: # 判断是否存在文件夹如果不存在则创建为文件夹
os.makedirs(path) # makedirs 创建文件时如果路径不存在会创建这个路径
print("--- 创建新文件夹... ---")
print("--- 创建完成 ---")
else:
print("--- 文件夹已存在! ---")
mkdir(foldername)
os.chdir(foldername)

开始全站爬取

为了方便出错后再次运行,我在主程序里面增加了两个判断:

  1. if speed > 22.6中的22.6为上次运行出错最后时的进度;
  2. if not os.path.exists(title)是在判断某一组套图是否已经被下载过了。
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
import os
os.chdir("/Users/mr.cheng/Desktop/Python网络数据采集")
make_and_cd("mztu")
cwd = os.getcwd()
all_links = get_all_link()
total_length = len(all_links)
progress = 0
for i in all_links:
progress += 1
speed = progress / total_length * 100
## 下面两个判断语句都是为了减少出错后再次运行的重复运行量。22.6为上次运行出错最后的进度
if speed > 22.6:
title = get_title(i)
if not os.path.exists(title):
print("正在下载:", title)
make_and_cd(title)
m = 1
for j in get_pic_link(i):
download_pic(j, name = str(m)+".jpg")
m += 1
print("下载成功")
os.chdir(cwd)
progress += 1
speed = progress/total_length * 100
print("已完成", speed, "%")

通过多次运行,现在我已经运行28%了,目测今晚可以运行完成, 估计总共会有20万张妹子图。。。

题外话

之所以今天又弄这个,是因为发现了这个界面:http://m.mzitu.com/all/ ,我天真的以为这个页面上有所有的套图标题、链接、发布日期,然而我发现自己错了,写了下面的代码爬好之后:

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
"""
下面的程序可以获取"每日更新"页面上的2890套图片的日期、标题、链接,显然这样获得的不是全部
"""
# 获取所有套图的名称和主页链接
from urllib.request import urlopen
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd
import re
url = "http://m.mzitu.com/all/"
html = urlopen(url)
bsObj = BeautifulSoup(html, "html.parser")

## 日期
date = bsObj.findAll("span", {"class":"archive-time"})
dateSeries = np.array([])
for d in date:
dateSeries = np.append(dateSeries, d.get_text())
dateSeries = pd.Series(dateSeries)

## 标题
title = bsObj.findAll("a", {"class":"clear"})
titleSeries = np.array([])
for t in title:
titleSeries = np.append(titleSeries, t.get_text())
titleSeries = pd.Series(titleSeries)

## 链接
link = bsObj.findAll("a", {"class":"clear"})
linkSeries = np.array([])
for l in link:
linkSeries = np.append(linkSeries, l.attrs['href'])
linkSeries = pd.Series(linkSeries)

## 把日期、标题、链接合并为一个数据框
df = pd.DataFrame([dateSeries, titleSeries, linkSeries])
df = df.T
df.columns = ['date', 'title', 'link']
df.to_csv("meizitu.csv")

结果只得到了2800 多个链接,显然这不是全部,因为根据之前我的程序可以得到10000+的链接。但是还是非常有趣的,因为最后我得到的是一个数据框。滑稽。。。

# Python

评论

程振兴

程振兴 @czxa.top
截止今天,我已经在本博客上写了607.9k个字了!

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×