现在不少网站对于图片资源进行懒加载, 可以有效限制爬虫.所谓的懒加载, 是当我们打开浏览器, 当界面进入可视化区域之后, 图片资源就会进行请求然后展式出来.所以我们直接对url进行请求是拿不到.对于懒加载的图片, 我们要怎么解决呢?

解决站长之家图片懒加载的方法的解决思路

在B站看的是 路飞学城 IT 这个B站号发布的爬虫学习视频,七月份的时候发布的, 我学习的时候是十一月中旬, 视频里讲解的不少爬虫案例所对应的网站都已经更新了反爬手段了.
现在我们要爬取站长之家的图片模块, 因为当时站长之家采取的是伪属性src2 做真正的url, 图片所在的位置还没进入用户的可视区, 就不会发送请求, 如果我们拿走src 属性, 是请求不到真实的图片地址的. 当用户鼠标滚动下去, 页面翻滚, 图片进入可视化区域, src2就会变成src 属性, 请求拿到图片并展式到当前.这就是图片懒加载.
由于视频发上去也有快半年了, 网站也进行了反爬维护, 所以按照视频里是 做不出的. 我当时也是想了一会, 在页面上抓包了,用src, src2, 甚至img标签的父类都提取不到, 提取为空.

此时我仔细看了看第一个url 的 response , 我看到有返回每个图片的详情页的, 详情页就是改首页某个 图片的详情页, 里面第一个图片就是首页的图片. 这个时候我想到的就是拿到每一个图片的详情页地址, 然后请求详情页之后拿到第一张图片, 就可以实现需求了.

然后我就在第一个url的响应体里定位了每个图片 url的详情页, 用 scrapy crawl spiderName 发送请求试试,结果是定位不到,只能定位到外层的一个大div , 通过打印div ,能看到详情页的url 藏在里面,但是用 xpath 拿不到, 图片的 src 是不在这个url的响应体里.
此时,就想到了之前课程里学到的正则 , 通过正则表达式, 取出图片的 详情页, 然后通过对详情页发送请求, 就拿到了详情页里第一个图片的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
import scrapy
import re
from ..items import LoadpictureItem

class ZhanzhangSpider(scrapy.Spider):
name = 'zhanzhang'
# allowed_domains = ['www.baidu.com']

start_urls = ['https://sc.chinaz.com/tupian/']

def use_re(self, str_conent):
# "通过正则解析图片的详情页,然后再去详情页里拿到图片的下载地址"
ex = 'a target="_blank" href="(.*)htm" alt'
ele = re.findall(ex, str_conent)[0]
ele = "http:" + ele + "htm"
return ele

# 拿到详情页的第一张图片, 其实就是首页展式的那个图片
def parse_detail(self, reponse):
img_url = reponse.xpath('//div[@class="imga"]/a/img/@src').extract_first()
img_url = 'http:' + img_url
item = LoadpictureItem()
item['src'] = img_url
#不要忘记返回 item , 不然管道类里就拿不到
yield item

def parse(self, response):
picture_list = response.xpath('//div[@id="container"]/div').extract()
for pic in picture_list:
ele = self.use_re(pic)
yield scrapy.Request(url=ele, callback=self.parse_detail)


item 和管道类

item

1
2
3
4
5
6
7
8
9
10
11
12
13
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class LoadpictureItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
src = scrapy.Field()

下载图片的管道类

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
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter


# class LoadpicturePipeline:
# def process_item(self, item, spider):
# return item

from scrapy.pipelines.images import ImagesPipeline
import scrapy

class picturePipeLine(ImagesPipeline):
# 根据图片的 url 去下载图片
def get_media_requests(self, item, info):
yield scrapy.Request(item['src'])

# 指定图片的名字
def file_path(self, request, response=None, info=None, *, item=None):
pic_name = request.url.split('/')[-1]
return pic_name

# 返回 item, 给下一个管道类用
def item_completed(self, results, item, info):
return item

设置文件里修改

1
2
3
4
5
6
7
8
9
10
11
12
#还有 UA 伪装, 我就不写了
ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'

#指定图片存储的目录
IMAGES_STORE = './imgs_girls'

ITEM_PIPELINES = {
# 'Loadpicture.pipelines.LoadpicturePipeline': 300,
'Loadpicture.pipelines.picturePipeLine': 301,
}

总结

反爬也是一个乐在其中的事情, 因为能够加深自己的理解. 如果只是对着教程弄, 别人反爬, 你就完全不会了,说明你是没有真的学进去, 好的学习态度是先自己解决, 尝试去做思考, 最终把问题解决了, 就是把内容给吃进去了, 光是对着别人敲一次就以为自己学会了,那是很肤浅的.