爬虫篇—入门级——Scrapy爬虫框架

一、框架详解

爬虫篇—入门级——Scrapy爬虫框架
Scrapy是由Twisted写的一个受欢迎的python事件驱动网络框架,它使用的是非阻塞的异步处理。

【1】内部各组件的作用

**ScrapyEngine(scrapy引擎):**是用来控制整个系统的数据处理流程,并进行事务处理的触发。
**Scheduler(调度器):**用来接受引擎发过来的请求,压入队列中,并在引擎再次请求的时候返回它。它就像是一个URL的优先队列,由它来决定下一个要抓取的网址是什么,同时在这里会去除重复的网址。
**Downloader(下载器):**用于下载网页内容,并将网页内容返回给爬虫(Spiders)[Scrapy下载器是建立在Twisted这个高效的异步模形上的]
**Spiders(爬虫):**爬虫主要是干活的,用于从特定网页中爬取自己需要的信息,即所谓的实体(item),也可以从中提取URL,让Scrapy继续爬取下一个页面。
**Pipeline(项目管道):**负责处理爬虫从网页中爬取的实体,主要功能就是持久化实体,验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被送到项目管道,并经过几个特定的次序处理数据。
**Downloader Middlewares(下载器中间件):**位于scrapy引擎和下载器之间的框架,主要是处理scrapy引擎与下载器之间的请求及响应。设置代理ip和用户代理可以在这里设置。

**Spider Middlewares(爬虫中间件):**位于scrapy引擎和爬虫之间的框架,主要工作是处理爬虫的响应输入和请求输出。

**Scheduler Middlewares(调度中间件):**位于scrapy引擎和调度器之间的框架,主要是处理从scrapy引擎发送到调度器的请求和响应。

【2】Scrapy运行流程

  1. 引擎从调度器中取出一个URL用于接下来的抓取
  2. 引擎把URL封装成一个请求(Request)传给下载器
  3. 下载器把资源下载下来,并封装成一个响应(Response)
  4. 爬虫解析Response
  5. 解析出来的就是实体(item),则交给项目管道(Pipeline)进行进一步处理
  6. 解析出来的是链接(URL),则把URL交给调度器等待下一步的抓取

爬虫篇—入门级——Scrapy爬虫框架

二、项目案例

基于Scrapy 框架影视信息采集与分析

项目介绍

为了充分利用网上大数据资源,让用户能够方便利用影视信息,采用基于 Scrapy 框架的爬虫技术,开

了检索电影信息的搜索引擎。对豆瓣网站的影视信息进行爬取,以方便用户准确获取最新的电影信息。

项目代码

以“豆瓣电影”为爬取目 标,爬取网站中的影视信息。主要包括网站排名 “ Top250 ”和喜剧、动作类电影
的电影名称、电影评分、电影导演, 电影上映时间以及电影评语。
爬虫篇—入门级——Scrapy爬虫框架
【1】创建工程

scrapy startproject Douban

【2】创建爬虫程序

cd Douban/
scrapy genspider douban 'douban.com'

爬虫框架会自动创建目录和文件

【3】确定爬取目标《item.py》

class DoubanItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()#电影名字
    score = scrapy.Field()#电影评分
    quote = scrapy.Field()#电影评语
    director =scrapy.Field()#电影导演
    release_date = scrapy.Field() #上映时间
    comment_num = scrapy.Field()#评论数
    image_url = scrapy.Field()#电影图片的url地址
    detail_url = scrapy.Field() #电影详情页信息
    image_path =scrapy.Field() #下载的封面本地存储位置

【4】更改setting配置文件,
1、添加Useragent、禁止爬虫协议

# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'Douban (+http://www.yourdomain.com)'
#设置随机用户代理
from fake_useragent import UserAgent
ua =UserAgent()
USER_AGENT = ua.random
# Obey robots.txt rules
# ROBOTSTXT_OBEY = True
ROBOTSTXT_OBEY = False
ITEM_PIPELINES = {
    # 'Douban.pipelines.DoubanPipeline': 300,
    'scrapy.pipelines.images.ImagesPipeline': 1,
    'scrapy.pipelines.files.FilesPipeline': 2,
    'DouBan.pipelines.MyImagesPipeline': 2,
    'DouBan.pipelines.DoubanPipeline': 300,
    'DouBan.pipelines.JsonWriterPipeline': 200,  # 数字越小, 越先执行;
    'DouBan.pipelines.AddScoreNum': 100,  # 处理爬去的数据, 处理完成后才保存;
    'DouBan.pipelines.MysqlPipeline': 200,  # 处理爬去的数据, 处理完成后才保存;
}
FILES_STORE ='/tmp/files'
IMAGES_STORE = '/tmp/images'
IMAGES_EXPIRES = 30
# 图片缩略图
IMAGES_THUMBS = {
    'small': (250, 250),
    'big': (270, 270),
}
# 图片过滤器,最小高度和宽度
IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110

2、设置pipeline,
当Item在Spider中被收集之后,它将会被传递到Item Pipeline,这些Item Pipeline组件按定义的顺序处理Item。
每个Item Pipeline都是实现了简单方法的Python类,比如决定此Item是丢弃而存储。以下是item pipeline的一些典型应用:
1.验证爬取的数据(检查item包含某些字段,比如说name字段)
2.查重(并丢弃)
3.将爬取结果保存到文件或者数据库中

我在这里通过对爬取到的数据进行处理(加1),之后存入mysql数据库,获取图片并进行下载

import json

import pymysql
import scrapy
from scrapy.exceptions import DropItem


class DoubanPipeline(object):
    def process_item(self, item, spider):
        return item

class AddScoreNum(object):
    """在原有的评分上加一"""
    def process_item(self,item,spider):
        if item['score']:
            score = float(item['score'])
            item['score'] = str(score + 1)
            return item
        else:
            raise Exception("没有爬去到score")

        class JsonWriterPipeline(object):
            """爬虫之前打开文件对象, 爬虫之后, 关闭文件对象"""

            def open_spider(self, spider):
                self.file = open('douban.json', 'w')

            def process_item(self, item, spider):
                # dict(item): 将item对象转成字典
                # json.dumps: 将字典序列化成json字符串;
                # indent=4: 存储是缩进为4;
                # ensure_ascii=False: 解决中文乱码问题
                line = json.dumps(dict(item), indent=4, ensure_ascii=False)
                self.file.write(line)
                return item

            def close_spider(self, spider):
                self.file.close()

        class MysqlPipeline(object):
            """编写MySQL存储插件"""

            def open_spider(self, spider):
                # 连接数据库
                self.connect = pymysql.connect(
                    host='127.0.0.1',  # 数据库地址
                    port=3306,  # 数据库端口
                    db='DoubanProject',  # 数据库名
                    user='root',  # 数据库用户名
                    passwd='westos',  # 数据库密码
                    charset='utf8',  # 编码方式
                    use_unicode=True,
                    autocommit=True
                )
                # 通过cursor执行增删查改--游标
                self.cursor = self.connect.cursor()
                self.cursor.execute("create table if not exists douBanTop("
                                    "title varchar(50) unique, "
                                    "score float , "
                                    "quote varchar(100), "
                                    "director varchar(100), "
                                    "comment_num int, "
                                    "release_date varchar(10));")

            def process_item(self, item, spider):
                insert_sqli = "insert into douBanTop(title, score, quote,director) values ('%s', '%s', '%s', '%s')" % (
                    item['title'], item['score'], item['quote'], item['director'],)
                print(insert_sqli)
                try:
                    self.cursor.execute(insert_sqli)
                    # 提交sql语句
                    self.connect.commit()
                except Exception as e:
                    self.connect.rollback()
                return item  # 必须实现返回

            def close_spider(self, spider):
                self.connect.commit()
                self.cursor.close()
                self.connect.close()

        class MyImagesPipeline(ImagesPipeline):

            def get_media_requests(self, item, info):  # 單個的item對象;
            
                #自动获取请求并下载图片
              
                print("item: ", item)
                yield scrapy.Request(item['image_url'])


            def item_completed(self, results, item, info):
                """
                :param results:
                    [(True,  {'url': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1454261925.jpg',
                        'path': 'full/e9cc62a6d6a0165314b832b1f31a74ca2487547a.jpg',
                        'checksum': '5d77f59d4d634b795780b2138c1bf572'})]
                :param item:
                :param info:
                :return:
                """
                # for result in results:
                #     print("result: ", result)
                image_paths = [x['path'] for isok, x in results if isok]
                # print("image_paths: ", image_paths[0])
                if not image_paths:
                    raise DropItem("Item contains no images")

                item['image_path'] = image_paths[0]
                return item

【5】编写爬虫spider《douban.py》

# -*- coding: utf-8 -*-
import scrapy
from scrapy import Request

from Douban.items import DoubanItem
class DoubanSpider(scrapy.Spider):
    #爬虫名字,不可重复
    name = 'douban'
    allowed_domains = ['douban.com']
    start_urls = ['http://douban.com/',
                  'https://movie.douban.com/top250']

    url ='https://movie.douban.com/top250'
    def parse(self, response):
        item =DoubanItem()
        #<ol class="grid_view">
        movies = response.xpath('//ol[@class="grid_view"]/li')
        for movie in movies:
            #电影名称<span class="title">肖申克的救赎</span>
            item['title'] = movie.xpath('.//span[@class="title"]/text()').extract()[0]
            #电影评分
            item['score'] = movie.xpath('.//span[@class="rating_num"]/text()').extract()[0]
            #电影评语
            quote = movie.xpath('.//span[@class="inq"]/text()').extract()
            item['quote'] = quote[0] if quote else ''
            #电影导演
            info = movie.xpath('.//div[@class="bd"]/p/text()').extract()

            director = info[0].split('主演')[0].strip()

            item['director'] = director
            #电影图片的url地址
            item['image_url'] = movie.xpath('.//div[@class="pic"]/a/img/@src').extract()[0]
            # 电影详情页信息
            item['detail_url'] = movie.xpath('.//div[@class="hd"]//a/@href').extract()[0]

            yield item

        #<a href="?start=25&amp;filter=">后页&gt;</a>
        nextLink = response.xpath('.//span[@class="next"]/link/@href').extract()  # 返回的是列表
        if nextLink:
            nextLink = nextLink[0]
            print('Next Link: ', nextLink)
            yield Request(self.url + nextLink, callback=self.parse)


爬虫篇—入门级——Scrapy爬虫框架
爬虫篇—入门级——Scrapy爬虫框架

上一篇:burpsuit之Spider、Scanner、Intruder模块


下一篇:scrapy框架结构