目标网站:http://images.so.com/ (摄影专栏)
Scrapy抓站:360照片
1. 新建项目
scrapy startproject images360(名目名)
2. 新建Spider
直接用Pycharm打开上一步所创建的项目,在最下面的Terminal处执行该命令:
scrapy genspider images images.so.com
3. 分析目标网站的种种
- 进入目标网站所要爬取的摄影专题的页面下
- 下拉滚动栏查看图片,显而易见可知是Ajax请求数据的方式,我以前的博客中有对该请求方式的详细说明,f12直接查看XHR下的请求信息:
可以看到请求方式是GET方式,其是带有参数的请求方式,分析参数得:
ch指所选的专题名称,sn指该页显示30张图片,listtype指一种排序方式,temp指不知名参数,可变参数只有sn,所以该参数作为翻页更新请求的突破点。 - 查看数据所在地及其返回格式:
可得数据返回格式为JSON,很好处理。
4. 构造请求
在 images.py 文件下重写定义 start_requests() 方法:
# 此方法用于生成初始请求,它必须返回一个迭代的对象。
# 此方法会默认使用 start_urls 中的URL来构造 Request,而且 Request 是GET请求方式
# 若启动时以 POST 请求方式来访问某个站点,可以直接重写这个方法,发送 POST请求时,使用 FormRequest 即可
def start_requests(self):
# 设置请求头,因为之 get 请求,其请求参数跟在 url 地址后 ,可以这样设置
data = {
'ch':'photography',
'listtype':'new'
}
base_url = 'https://image.so.com/zjl?'
# 生成 50 次的请求
for page in range(1,self.settings.get('MAX_PAGE')+1):
data['sn'] = page * 30
# 使用 urlencode() 方法,将字典转换为 url 的 GET 参数
params = urlencode(data)
url = base_url + params
print(url)
yield Request(url,self.parse)
在 setting.py 中定义最大爬取页数的变量,以及修改 ROBOTSTXT_OBEY 的值:
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# 爬取的页数
MAX_PAGE = 50
5. 定义提取信息的字段
修改 Item.py ,定义自己的字段类:
import scrapy
from scrapy import Field
class ImageItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
collection = table = 'images'
# 照片的 id
id = Field()
# 照片类型
pic_desc = Field()
# 照片地址
url = Field()
# 照片名字
title = Field()
# 照片缩略图
thumb = Field()
6. 编写 Spider 方法
修改 images.py ,重写 parse() 方法:
def parse(self, response):
result = json.loads(response.text)
for image in result.get('list'):
item = ImageItem()
item['id'] = image.get('id')
item['pic_desc'] = image.get('pic_desc')
item['title'] = image.get('title')
item['url'] = image.get('qhimg_url')
item['thumb'] = image.get('qhimg_thumb')
yield item
7. 存储信息
7.1 Mysql保存
执行该功能的是 Item Pipeline 项目管道,所以在 pipelines.py 中重写方法并配置连接数据库的相关信息:
- 建立数据库,表,字段等信息
- 实现自己 PymysqlPipeline
class PymysqlPipeline(object):
#连接数据库
def __init__(self):
self.connect = pymysql.connect(
host = 'localhost',
database = 'image_360',
user = 'root',
password = '123456',
charset = 'utf8',
port = 3306
)
# 创建游标对象
self.cursor = self.connect.cursor()
# 此方法是必须要实现的方法,被定义的 Item Pipeline 会默认调用这个方法对 Item 进行处理
def process_item(self,item,spider):
cursor = self.cursor
sql = 'insert into image_information(id,pic_desc,title,url,thumb) values (%s,%s,%s,%s,%s)'
cursor.execute(sql,(
item['id'],item['pic_desc'],item['title'],item['url'],item['thumb']
))
# 提交数据库事务
self.connect.commit()
return item
7.2 本地文件保存
同样在 pipelines.py 添加如下的代码即可:
# 文件下载和图片下载,支持异步和多线程
class ImagePipeline(ImagesPipeline):
# 该方法返回保存的每张图片的名字
def file_path(self, request, response=None, info=None):
url = request.url
file_name = url.split('/')[-1]
return file_name
# 单个 Item 完成下载时的处理方法,并不是每张图片都可以下载成功,所以如果某张图片下载失败就不需要保存到数据库
def item_completed(self, results, item, info):
image_path = [x['path'] for ok,x in results if ok]
if not image_path:
raise DropItem('Image Downloaded Failed')
return item
def get_media_requests(self, item, info):
yield Request(item['url'])
8. 执行程序
在最下面的Terminal处执行该命令:
scrapy crawl images
9. 最终的效果图
以上就是整个的流程,比昨天有学到的新东西。