当我们想要抓取一个页面的内容时,要做的第一件事不是写代码,而是分析页面,确定这是一个静态页面还是动态页面。抓取静态页面的方法十分简单,直接解析html源码再进行分析解析即可,如果不太明白,可以参考我上篇文章Scrapy抓取豆瓣电影信息,这里我主要讲述一下如何抓取动态页面。
抓取动态页面有两种方法:
第一种方法是采用第三方工具,模拟浏览器的行为,从而加载数据。比如:Selenium、PhantomJs。这种方法的优点在于不必考虑动态页面的各种变化多端,但是性能低。
第二种方法是分析页面,找到对应的请求接口,直接获取数据。这种方法的优点在于性能高,但缺点也显而易见,就是获取API接口比较麻烦。
我这里采用第二种方式抓取动态页面。
1.浏览器打开页面 http://image.so.com/z?ch=beauty,查看网页源代码,发现源码中没有图片信息,这是一个动态加载的页面,而且是ajax异步请求动态页面。
2.分析页面,提取API接口,通过F12,审查元素可以找到。
3.打开上面的url,发现传入的是json格式的数据,所以之后我们获取到response响应,要先用json.loads()
解析数据。
4.观察API接口,可以发现有几个参数,ch、sn、listtype、temp,通过改变这些参数的值就能获取到不同的内容。
通过分析发现,ch参数代表图片分类,比如beauty就表示美女图片,sn表示图片的编号,比如0就表示1到30之间的图片,30就表示31到60之间的图片。
分析了这些参数,我们就确定了我们需要请求的url地址,此处的url不像静态页面中通过程序自动获取a标签中的href,而是需要我们自动设定,我们可以通过重写start_requests
方法指定需要获取的url。
5.编写我们的spider
# -*- coding: utf-8 -*-
from json import loads
import scrapy
from urllib.parse import urlencode
from image360.items import BeautyItem
class ImageSpider(scrapy.Spider):
name = 'image'
allowed_domains = ['image.so.com']
# 重写Spider中的start_requests方法:指定开始url
def start_requests(self):
base_url = 'http://image.so.com/zj?'
param = {'ch': 'beauty', 'listtype': 'new', 'temp': '1'}
# 可以根据需要爬取不同数量的图片,此处只爬取60张图片
for page in range(2):
param['sn'] = page * 30
full_url = base_url + urlencode(param)
yield scrapy.Request(url=full_url, callback=self.parse)
def parse(self, response):
# 获取到的内容是json数据
# 用json.loads()解析数据
# 此处的response没有content
model_dict = loads(response.text)
for elem in model_dict['list']:
item = BeautyItem()
item['title'] = elem['group_title']
item['tag'] = elem['tag']
item['height'] = elem['cover_width']
item['width'] = elem['cover_height']
item['url'] = elem['qhimg_url']
yield item
6.编写item,定义保存的字段
import scrapy
class BeautyItem(scrapy.Item):
title = scrapy.Field()
tag = scrapy.Field()
height = scrapy.Field()
width = scrapy.Field()
url = scrapy.Field()
7.编写pipeline,完成数据持久化操作,这里包括下载图片和保存图片信息到mongo中。
Scrapy提供了一个 item pipeline ,来下载属于某个特定项目的图片,比如,当你抓取产品时,也想把它们的图片下载到本地,就可以通过图片管道实现。这在ImagesPipenine类中实现,提供了一个方便并具有额外特性的方法,来下载并本地存储图片。这个类中提供了很多处理图片的方法,想要了解详细内容可以查看官方文档中文版
# -*- coding: utf-8 -*-
import logging
import pymongo
import scrapy
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline
logger = logging.getLogger('SaveImagePipeline')
# 继承ImagesPipenine类,这是图片管道
class SaveImagePipeline(ImagesPipeline):
""" 下载图片 """
def get_media_requests(self, item, info):
# 此方法获取的是requests 所以用yield 不要return
yield scrapy.Request(url=item['url'])
def item_completed(self, results, item, info):
""" 文件下载完成之后,返回一个列表 results 列表中是一个元组,第一个值是布尔值,请求成功会失败,第二个值的下载到的资源 """
if not results[0][0]:
# 如果下载失败,就抛出异常,并丢弃这个item
# 被丢弃的item将不会被之后的pipeline组件所处理
raise DropItem('下载失败')
# 打印日志
logger.debug('下载图片成功')
return item
def file_path(self, request, response=None, info=None):
""" 返回文件名 """
return request.url.split('/')[-1]
class SaveToMongoPipeline(object):
""" 保存图片信息到数据库 """
def __init__(self, mongodb_server, mongodb_port, mongodb_db, mongodb_collection):
self.mongodb_server = mongodb_server
self.mongodb_port = mongodb_port
self.mongodb_db = mongodb_db
self.mongodb_collection = mongodb_collection
def open_spider(self, spider):
# 当spider被开启时,这个方法被调用
self.connection = pymongo.MongoClient(self.mongodb_server, self.mongodb_port)
db = self.connection[self.mongodb_db]
self.collection = db[self.mongodb_collection]
def close_spider(self, spider):
# 当spider被关闭时,这个方法被调用。
self.connection.close()
# 依赖注入
@classmethod
def from_crawler(cls, crawler):
# cls() 会调用初始化方法
return cls(crawler.settings.get('MONGODB_SERVER'),
crawler.settings.get('MONGODB_PORT'),
crawler.settings.get('MONGODB_DB'),
crawler.settings.get('MONGODB_COLLECTION'))
def process_item(self, item, spider):
post = {'title': item['title'], 'tag': item['tag'],
'width': item['width'], 'height': item['height'], 'url': item['url']}
self.collection.insert_one(post)
return item
8.编写settings,完成配置,此处只把需要配置的内容写了出来。
BOT_NAME = 'image360'
SPIDER_MODULES = ['image360.spiders']
NEWSPIDER_MODULE = 'image360.spiders'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' \
'Chrome/67.0.3396.79 Safari/537.36'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# Configure maximum concurrent requests performed by Scrapy (default: 16)
CONCURRENT_REQUESTS = 1
# 随机延迟,设定随机延迟,使爬虫更像浏览器的行为
RANDOMIZE_DOWNLOAD_DELAY = True
# 延迟时间
DOWNLOAD_DELAY = 3
# 数据库信息
MONGODB_SERVER = '服务器地址'
MONGODB_PORT = 27017
MONGODB_DB = 'image360'
MONGODB_COLLECTION = 'image'
# 日志
LOG_LEVEL = 'DEBUG'
# 数字越小越先执行
ITEM_PIPELINES = {
'image360.pipelines.SaveImagePipeline': 300,
'image360.pipelines.SaveToMongoPipeline': 330,
}
# 配置图片保存地址,会自动创建文件夹
IMAGES_STORE = './resources'
9.启动spider
在启动spider之前,我们还需要安装几个包,pypiwin32、pilllow、pymongo
pip install pypiwin32
pip install pillow
pip install pymongo
安装完成之后就可以启动spider了
scrapy crawl image
10.查看结果:
获取到的图片返回的内容:
保存在指定路径的图片:
一个爬取动态页面并且下载图片的爬虫就完成了,其实写起来很简单,关键是需要分析API接口,重写start_requests方法。