1. 预备工作
在花了两天半时间研究学习了urllib,requests,bs4,lxml等库之后,准备开始一个简单的实战——爬取“下厨房”网站首页所有的高清大图。
下厨房首页
首先F12观察一下这些图片的真实地址:
F12观察任意图片地址
观察到img =之后有一个链接地址,直接访问该地址:
显然这是一个小图,不是我要的高清图。再观察复制下来的链接
链接分析
不难发现,以@为界,链接可以分为前后两个部分。猜想前一部分是图片地址,后一部分则是对图片进行压缩的参数。试着删除@及其之后的部分,再访问。果然出现了一张高清大图。
高清大图真实地址
这下子基础部分基本弄清楚了,预备动作就到这里。
2. 开爬!
终于可以开始写我的第一只爬虫了,激动~
2.1 获取页面,解析结构
首先,通过requests发起请求
import requests
from lxml import etree
# 模拟浏览器访问
ua = 'Mozilla/5.0 ' \
'(Windows NT 10.0; Win64; x64) ' \
'AppleWebKit/537.36 (KHTML, like Gecko) ' \
'Chrome/80.0.3987.116 Safari/537.36'
header = {'User-Agent': ua}
r = requests.get("http://www.xiachufang.com/", headers=header)
# r = requests.get("http://httpbin.org/", headers=header)
print(r.text)
et = etree.HTML(r.text)
data = et.xpath('//img/@src')
for i in range(len(data)):
print(data[i])
print(len(data))
控制窗口的输出信息为:
控制台输出信息
可见一共输出了75个数据,但是其中包含很大一部分:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEXo6Og4/a9sAAAADUlEQVR42gECAP3/AAAAAgABUyucMAAAAABJRU5ErkJggg
这样的数据。显然这不是我想要的。打印r.text观察一下这些数据在页面中的位置。发现有
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEXo6Og4/a9sAAAADUlEQVR42gECAP3/AAAAAgABUyucMAAAAABJRU5ErkJggg"
data-src="http://i1.chuimg.com/b7933e0a29d54c53aab854f3ea65c9f3_750w_750h.jpg@2o_50sh_1pr_1l_60w_60h_1c_1e_90q_1wh"
alt="夏夏2013"
width="60"
height="60">
原来这些用户分享的图片,真实地址在“data-src”属性中,而非“src”属性。
查找所有的data-src
data = et.xpath('//img/@data-src')
for i in range(len(data)):
# print(data[i])
pass
print(len(data))
输出结果为25,也就是原本链接地址为src的被筛选掉了,那么怎么才能保存所有的图呢?
想到了一个比较low,但是可行的方案:
- 找到img标签中不包含data-src属性但包含src属性中的src值
- 找到img标签中包含data-src属性的data-src
- 合并前两步中得到的列表
在代码中体现为:
et = etree.HTML(r.text)
# 1. 找到img标签中不包含data-src属性但包含src属性中的src值
data1 = et.xpath("//img[not(@data-src) and @src]/@src")
# 2. 找到img标签中包含data-src属性的data-src
data2 = et.xpath("//img/@data-src")
# 3. 合并前两步中得到的列表
img_list= data1 + data2
for i in range(len(img_list)):
print(img_list[i])
# pass
观察输出结果:
不完美的结果
结果列表中存在空项。解决办法当然是去除空项:
# 删除空元素
while '' in img_list:
img_list.remove('')
再观察输出结果:
完美的结果
完美输出了69个图片链接。这一部分完成!
2.2 处理img_list列表
由预备结果中的分析可知,下一步要做的是对列表中每一项进行分割,只保留高清大图部分,删除后面的参数。也即,保留链接中@符号之前的部分。使用urllib中的parse对链接进行分析。
导入urlparse
from urllib.parse import urlparse
for img in img_list:
o = urlparse(img)
关于urlparse的用法,这里用一个例子说明:
o = urlparse.urlparse("http://www.google.com/search?hl=en&q=urlparse&btnG=Google+Search")
则
参数 | 值 |
---|---|
o.scheme | 'http' |
o.netloc | 'www.google.com' |
o.path | '/search' |
o.params | '' |
o.query | 'hl=en&q=urlparse&btnG=Google+Search' |
o.fragment | '' |
故可以对列表进行下面的处理:
for img in img_list:
o = urlparse(img)
filename = o.path[1:].split('@')[0]
url = "%s://%s/%s" % (o.scheme, o.netloc, filename)
print(url)
处理后的url
完美!
2.3 文件操作
有了真实的url,下面要做的就是下载图片并保存到本地了。使用文件操作,首先必须导入python内置的os模块。
import os
初始化文件夹,如果不存在就创建一个:
# 文件操作,初始化xiachufang_image文件夹
image_dir = os.path.join(os.curdir, 'xiachufang_image')
if not os.path.isdir(image_dir):
os.mkdir(image_dir)
2.4 最后一步
遍历访问处理后的url,并且以filename保存。(二进制写入)
for img in img_list:
o = urlparse(img)
filename = o.path[1:].split('@')[0]
filepath = os.path.join(image_dir, filename)
url = "%s://%s/%s" % (o.scheme, o.netloc, filename)
print(url)
resp = requests.get(url)
with open(filepath, 'wb') as f:
# 二进制以块写入
for chunk in resp.iter_content(1024):
f.write(chunk)
基本没有问题,但是少下载了几个图。再优化一下:
for img in img_list:
o = urlparse(img)
filename = o.path[1:].split('@')[0]
filepath = os.path.join(image_dir, filename)
if not os.path.isdir(os.path.dirname(filepath)):
os.mkdir(os.path.dirname(filepath))
url = "%s://%s/%s" % (o.scheme, o.netloc, filename)
print(url)
resp = requests.get(url)
with open(filepath, 'wb') as f:
# 二进制以块写入
for chunk in resp.iter_content(1024):
f.write(chunk)
看看文件夹,已经全部保存,欣赏一下美食吧~
总览
大图1
大图2
大图3 大图4
大图5
成就感和食欲均满满
3. 完整代码
贴出完整代码
import os
from urllib.parse import urlparse
import requests
from lxml import etree
# 模拟浏览器访问
ua = 'Mozilla/5.0 ' \
'(Windows NT 10.0; Win64; x64) ' \
'AppleWebKit/537.36 (KHTML, like Gecko) ' \
'Chrome/80.0.3987.116 Safari/537.36'
header = {'User-Agent': ua}
r = requests.get("http://www.xiachufang.com/", headers=header)
# 开始解析
et = etree.HTML(r.text)
# 1. 找到img标签中不包含data-src属性但包含src属性中的src值
data1 = et.xpath("//img[not(@data-src) and @src]/@src")
# 2. 找到img标签中包含data-src属性的data-src
data2 = et.xpath("//img/@data-src")
# 3. 合并前两步中得到的列表
img_list = data1 + data2
# 删除空元素
while '' in img_list:
img_list.remove('')
# 文件操作,初始化xiachufang_image文件夹
image_dir = os.path.join(os.curdir, 'xiachufang_image')
# if not os.path.isdir(image_dir):
# os.mkdir(image_dir)
for img in img_list:
o = urlparse(img)
filename = o.path[1:].split('@')[0]
filepath = os.path.join(image_dir, filename)
if not os.path.isdir(os.path.dirname(filepath)):
os.mkdir(os.path.dirname(filepath))
url = "%s://%s/%s" % (o.scheme, o.netloc, filename)
print(url)
resp = requests.get(url)
with open(filepath, 'wb') as f:
# 二进制以块写入
for chunk in resp.iter_content(1024):
f.write(chunk)
网友评论