聚沙成塔--爬虫系列(七)(妹子,快到碗里来)

2017-10-25

版权声明:本文为作者原创文章,可以随意转载,但必须在明确位置标明出处!!!

前面章节的代码可以看出,我们并没有把「好笑数」,「图像的url地址」取到,从糗百主页我们可以看到有些用户发表的段子里包含了图片,那我们怎么取到这些图片呢,本篇老谢将教会大家如何把网络上的图片取到本地,这个技能你们一定要get到。get到了以后再看到漂亮妹子的图片再也不用右键将图片保存在本地了,哈哈哈…;此种神功,我练定了。

东方不败-林青霞

练此神功口诀

通过糗事百科主页我们可以看出有些用户发表的段子带了图片,有些又没有图片只有文本内容,前面文章的代码我们只能取出每个用户的文本内容并不能取到图片内容,那么要怎么才能取到图片内容呢?

  • 口诀一:观察!观察!观察!
    重要的事说三遍,做爬虫最精髓的地方就是观察,外加一点猜测,观察什么呢,观察带图片发表的用户和只有文本用户在html源码中的区别,怎么在源码中快速找到它们的区别呢,当然是用F12开发者工具了,如果你是初学者请一定要看前面章节的内容。
  • 口诀二: 过滤,筛选
    观察过后,使用正则表达式过滤,筛选是个永恒的主题,当然后面我们还会讲到Beautify soup、xpath、lxml等第三方库,第三方库有它的优势,也有它的劣势,就拿Beautify Soup来说,如果我们访问的页面不标准或者不完整,那么你或许拿不到你想要的结果。这种第三方库解决不了的问题就只能用最原始的正则表达式了,所以学好正则表达式是你不得不具备的技能。

观察

通过F12开发者工具我们看看带图片的和不带图片的有什么区别,当我们通过快速定位是可以看到有图片的包含在一个…中,这个时候我们不要马上就取编写正则表达式,我们还得去看看没有图片的有没有包含这对元素,如果有那么我们编写代码的逻辑就会和没有的不一样了,通过观察我们发现不带图片的并没有包含…这对元素,如下图:

带图片和不带图片的差异

过滤

差异找到了剩下的就是过滤了,如何过滤呢,这里就需要我们稍稍东东脑子了,想想有的用户发表是带了图片的有的用户发表是没有带图片的,如果这个时候我们还像前面文章那样把正则表达式写在

1
pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>.*?<div class="thumb">.*?src="(.*?)".*?<i class="number">(.*?)</i>', re.S)

这个表达式里,那么我们肯定得不到我们想要的结果,想想是为什么,因为如果某个用户没有发表图片,那么我们使用这个表达式肯定连该用户发表的文本信息也拿不到了。所以我们必须的把匹配图片的正则表达式和之前的正则表达式分开来写

1
2
pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>.*?<i class="number">(.*?)</i>', re.S)
picture = re.compile(r'<div class="thumb">.*?src="(.*?)"', re.S)

编码实现

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
76
77
78
79
from urllib import request
from urllib import error
import re
import os
url = 'https://www.qiushibaike.com'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'
headers = {'User-Agent': user_agent}
def read_html(url, headers, codec):
'''[read_html]
[读取html页面内容]
Arguments:
url {[string]} -- [url地址]
headers {[dict]} -- [用户代理,这里是一个字典类型]
codec {[string]} -- [编码方式]
Returns:
[string] -- [页面内容]
'''
# 构建一个请求对象
try:
req = request.Request(url, headers=headers)
# 打开一个请求
response = request.urlopen(req)
# 读取服务器返回的页面数据内容
content = response.read().decode(codec)
return content
except error.URLError as e:
print(e.reason)
return None
def match_element(content, pattern):
'''[match_element]
[匹配元素]
Arguments:
content {[string]} -- [文本内容]
pattern {[object]} -- [匹配模式]
Returns:
[list] -- [匹配到的元素]
'''
# 匹配所有用户信息
userinfos = re.findall(pattern, content)
return userinfos
content = read_html(url, headers, 'utf-8')
pattern = re.compile(r'<div class="article block untagged mb15[\s\S]*?class="stats-vote".*?</div>', re.S)
if content:
userinfos = match_element(content, pattern)
if userinfos:
pattern = re.compile(r'<a href="(.*?)".*?<h2>(.*?)</h2>.*?<div class="content">(.*?)</div>.*?<i class="number">(.*?)</i>', re.S)
picture = re.compile(r'<div class="thumb">.*?src="(.*?)"', re.S)
for userinfo in userinfos:
item = match_element(userinfo, pattern)
pictures = match_element(userinfo, picture)
try:
if item:
userid, name, content, num = item[0]
# 去掉换行符,<span></span>,<br/>符号
userid = re.sub(r'\n|<span>|</span>|<br/>', '', userid)
name = re.sub(r'\n|<span>|</span>|<br/>', '', name)
content = re.sub(r'\n|<span>|</span>|<br/>', '', content)
if pictures:
path = './users/'
if not os.path.exists(path):
os.makedirs(path)
request.urlretrieve('http:' + pictures[0], path + os.path.basename(pictures[0]))
print((userid, name, content, num, pictures[0]))
else:
print((userid, name, content, num ))
except Exception as e:
print(e)

执行结果

1
2
3
4
5
6
7
8
9
('/users/20735073/', '优雅文艺范', '我小徒弟问我:“师傅,你多大了?”“22啊,问这个干嘛”“那你结婚了吗”“没有啊”“你怎么还不结婚啊?现在像你这么大的二胎都会走路了”“那你多大了?”“22啊”“那你孩子呢?”“在家我婆婆带着呢,怎么了?”“。。。没事,我就问问”', '683')
('/users/30716689/', '苍老师首席女配音', '闺蜜怀孕中,抱怨老公回家就躲在客厅打游戏,经常闹别扭。一次偶然的机会,她遇到大师,大师说她家风水有问题,要改变房间格局。闺蜜听大师的安排,果然,老公回家就往卧室钻,不玩手机也不吵闹了。我好奇的问闺蜜,大师真那么灵验,你怎么改房间格局的?闺蜜笑笑,也没有什么,只是把客厅的路由器改在卧室窗台。', '988')
('/users/33065062/', '耶稣安拉宁有种乎', '初中时候,班里有个很漂亮女孩,属于班花那种,有次下课我往她脸上亲了一口就跑,其实我一点都不喜欢她,她男友是学校外面那种小混混,我就喜欢那种被追着打一个星期的感觉,死亡如风,常伴吾身。', '411')
('/users/28491276/', '不知取什么名903…', '组织淫才给我答案', '227', '//pic.qiushibaike.com/system/pictures/11966/119663321/medium/app119663321.jpg')
('/users/26635010/', '本少丶霸道', '这个OK么', '15', '//pic.qiushibaike.com/system/pictures/10590/105905488/medium/app105905488.jpg')
('/users/10144082/', '°小姐姐', '闺蜜看上了一支TF(汤姆福特)的口红,我们俩正在讨论,老公凑过来问:聊什么呢?我:口红,说了你也不懂。老公看到我手机上的图片激动的说:这个我知道我知道。TFboys,他们还卖口红啊。-_-||。。。。。', '1653')
('/users/5623502/', '"别扭丶!', '最近几天把衣柜翻了个底朝天都没找到我想穿的那件秋衣,问老公吧,逗逗他。“我那秋衣我怎么也找不到了,你是不是偷偷给谁了?”“你还找我要?还我给谁?你不定丢谁家了呢?”我擦,好想问候他祖宗十八代!', '4460')
('/users/30031169/', '空霖', '今天中午做了个终生难忘的梦梦见自己被关在监狱里 但我一直想不起做错了什么 梦里还记得进过两次监狱 后来有人说我在KTV喝醉了 听见有人唱歌说我 然后我就把他打了 吓得我醒了 还在想自己有没有进过监狱', '107')
('/users/31854592/', '流云徐徐', '陪4岁萝莉小侄女去超市买东西,小姑娘见到吃的东西就走不动路了,睁着大眼睛可怜兮兮地看着我让买零食。我就逗她:“点点,叔叔没钱,买不起。”随即大眼睛一阵暗淡,突然又亮起来!只见她跑到导购阿姨那边跟人聊天,五分钟以后欢快地带着阿姨过来。阿姨说:“小姑娘夸了你半天,想把你卖了换零食。”我:……。买买买,小吃货从小就惹不起!', '376')

爬取下来的图片
从上图可以看到我们已经把图片下载到本地磁盘了。

代码解析

本次代码新增了一个正则表达式,一个目录创建,一个从通过url地址下载的函数

  • picture = re.compile(r’
    .?src=”(.?)”‘, re.S):
    该表达式的作用是匹配到图片的url地址
  • makedirs:该函数是创建目录,于我们在linux系统中执行mkdir -p path1/path2/path3的功能是一样的,它可以层级递归的去创建目录,不需要我们手动每一级每一级的去创建目录, 看到这里是不是比windows创建目录方便多了。
  • urlretrieve:该函数是下载一个网络对象到本地文件,该函数返回一个元组,(local_filename, headers),函数定义如下:
    urllib.request.urlretrieve(url, filename=None, reporthook=None,data=None)
    url就是我们要从网络中下载的文件地址,filename是下载后存放在本地的文件名
  • os.path.basename(pictures[0]):该函数的作用是返回文件名,什么意思呢,pictures[0]取到是//pic.qiushibaike.com/system/pictures/11966/119663395/medium/app119663395.jpg地址,通过这个函数处理后我们得到将是app119663395.jpg

note: 作为初学者,学习一种语言就是要不断的敲代码,只有通过敲代码才能发现问题,然后修正问题,最后才能把别人的东西变成自己的,不然别人的永远都是别人的,一定要实践,切记!切记!

欢迎关注我的公众号:「爱做饭的老谢」,老谢一直在努力…

上一篇:聚沙成塔–爬虫系列(六)(请做个内外兼修的高手)
下一篇:聚沙成塔–爬虫系列(八)(请做个内外兼修的高手)