python3学习笔记

初级python爬虫实战一——爬取下厨房首页高清图

2020-03-14  本文已影响0人  一个王二不小

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,但是可行的方案:

  1. 找到img标签中不包含data-src属性但包含src属性中的src值
  2. 找到img标签中包含data-src属性的data-src
  3. 合并前两步中得到的列表
    在代码中体现为:
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)
上一篇下一篇

猜你喜欢

热点阅读