Scrapy框架简介

Scrapy架构图


image.png

image.png

一、新建项目

scrapy startproject myspider

创建爬虫项目

scrapy startproject jobboleproject

新建爬虫文件

scrapy genspider jobbole jobbole.com

二、明确目标:

  1. 在items.py中添加字段
    根据目标网站分析需要提取的数据,在item.py文件中添加字段
    打开jobboleproject文件下的item.py文件
    可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field的类属性来定义一个Item(可以理解成类似于ORM的映射关系)。
    接下来,创建一个JobboleprojectItem 类,和构建item模型(model)。
  2. 制作爬虫
    打开 jobboleproject/spider目录里的 [jobbole.py]编写代码
import scrapy
class JobboleSpider(scrapy.Spider):

    name = 'jobbole'
    allowed_domains = ['jobbole.com']
    start_urls = ['http://blog.jobbole.com/all-posts/']

def parse(self, response):
    pass
  • name = "" :这个爬虫的识别名称,必须是唯一的,在不同的爬虫必须定义不同的名字。
  • allow_domains = [] 是搜索的域名范围,也就是爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的URL会被忽略。
  • start_urls = () :爬取的URL元祖/列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成。
  • parse(self, response) :解析的方法,每个初始URL完成下载后将被调用,调用的时候传入从每一个URL传回的Response对象来作为唯一参数,主要作用如下:
    负责解析返回的网页数据(response.body),提取结构化数据(生成item)
    生成需要下一页的URL请求。
  1. 分析网站结构取数据,在parse方法中做数据的提取
from jobboleproject.items import JobboleprojectItem

1.获取图片和文章详情的链接
def parse(self, response):
    
    # css选择器获取当前列表页面的所有的节点
    post_nodes = response.css("#archive .floated-thumb .post-thumb a")

    # 如果不是完整的域名 需要拼接完整 response.url + post_url
    # 获取到的URL可能不是一个域名,也可能是具体的文章需要使用parse函数from urllib import parse
    for post_node in post_nodes:
        image_url = post_node.css("img::attr(src)").extract_first("")
        post_url = post_node.css("::attr(href)").extract_first("")
        full_url = response.urljoin(post_url)
        #meta参数对应的是一个字典,用来传递数据
        yield scrapy.Request(url=full_url, meta={"front_image_url": image_url},
        callback=self.parse_detail)

yield

的作用就是把一个函数变成一个 generator(生成器),带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,带有yeild的函数遇到yeild的时候就返回一个迭代值,下次迭代时, 代码从 yield 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行, 直到再次遇到 yield。

通俗的讲就是:在一个函数中,程序执行到yield语句的时候,程序暂停,返回yield后面表达式的值,在下一次调用的时候,从yield语句暂停的地方继续执行,如此循环,直到函数执行完。

Scrapy Item pipeline(管道文件)使用

编写item pipeline,item pipiline组件是一个独立的Python类,其中process_item()方法必须实现:

import something

class SomethingPipeline(object):
    def __init__(self):
        # 可选实现,做参数初始化等
        # doing something

    def process_item(self, item, spider):
        # item (Item 对象) – 被爬取的item
        # spider (Spider 对象) – 爬取该item的spider
        # 这个方法必须实现,每个item pipeline组件都需要调用该方法,
        # 这个方法必须返回一个 Item 对象,被丢弃的item将不会被之后的pipeline组件所处理。
        return item

    def open_spider(self, spider):
        # spider (Spider 对象) – 被开启的spider
        # 可选实现,当spider被开启时,这个方法被调用。

    def close_spider(self, spider):
        # spider (Spider 对象) – 被关闭的spider
        # 可选实现,当spider被关闭时,这个方法被调用

启用一个Item Pipeline组件 为了启用Item Pipeline组件,必须将它的类添加到 settings.py文件ITEM_PIPELINES 配置,(0-1000随意设置,数值越低,组件的优先级越高

定制图片下载管道

Scrapy提供了一个 item pipeline ,来下载属于某个特定项目的图片,这条管道,被称作图片管道,在 ImagesPipeline 类中实现,提供了一个方便并具有额外特性的方法,来下载并本地存储图片:

Pillow 是用来生成缩略图,并将图片归一化为JPEG/RGB格式,因此为了使用图片管道,你需要安装这个库。

使用图片管道 当使用 ImagesPipeline ,典型的工作流程如下所示:
在settings.py中设置 IMAGES_STORE 设置为一个有效的文件夹,用来存储下载的图片

IMAGES_STORE = '/xxx/xxx/xxx'

from scrapy.contrib.pipeline.images import ImagesPipeline
import scrapy
from scrapy.exceptions import DropItem
import os
from scrapy.utils.project import get_project_settings

#下载图片

image_store = get_project_settings().get('IMAGES_STORE')

class jobboleImagePipline(ImagesPipeline):

    def get_media_requests(self, item, info):
        #根据图片地址,构建请求
        yield scrapy.Request(item['coverImage'])

    def item_completed(self, results, item, info):
        print(results)
        #获取图片下载成功的请求路径
        image_paths = [x['path'] for ok, x in results if ok]
        #判断图片是否下载成功
        if not image_paths:
            #出现错误,例如没有图片,可以丢弃item
            raise DropItem("Item contains no images")
        else:
            #替换图片的名称,自定义图片名称
            os.rename(image_store+'/'+image_paths[0],
                    image_store+'/'+item['title']+'.jpg')
            #将图片地址赋值给item
            item['localImagePath'] = image_store+'/'+item['title']+'.jpg'
            return item

爬虫数据持久化保存

settings.py文件: 设置文件,在这里设置User-Agent,激活管道文件等...

ITEM_PIPELINES = {
    'douban.pipelines.DoubanPipeline': 300,
}

MONGODB 主机名
MONGODB_HOST = '127.0.0.1'
MONGODB 端口号
MONGODB_PORT= 27017
数据库名称
MONGODB_DBNAME = "Douban"
存储数据的表名称
MONGODB_SHEETNAME= "doubanmovies"

MongDB插入数据

import pymongo
# from scrapy.utils.project import
class ChinazprojectPipeline(object):
    def __init__(self,host,port,dbname):
        #创建mongdb数据库连接
        self.mongo_client = pymongo.MongoClient(
            host=host,port=port
        )
        #选择要操作的数据库
        self.db = self.mongo_client[dbname]

    @classmethod
    def from_crawler(cls,crawler):
        host = crawler.settings['MONGO_HOST']
        prot = crawler.settings['MONGO_PORT']
        db = crawler.settings['MONGO_DB']

        return cls(host,prot,db)

    def process_item(self, item, spider):
        """
        这个方法是必须实现的,爬虫文件中所有的item都会经过这个方法
        :param item: 爬虫文件传递过来的item对象
        :param spider: 爬虫文件实例化的对象
        :return:
        """
        #选择要操作的集合
        col_name = item.get_mongdb_collectionName()
        col = self.db[col_name]
        #插数据
        dict_data = dict(item)

        try:
            col.insert(dict_data)
            print('数据插入成功')
        except Exception as err:
            print('数据插入失败',err)

        return item
    def open_spider(self,spider):
        print(spider.name,'爬虫开始')

    def close_spider(self,spider):
        self.mongo_client.close()
        print(spider.name,'爬虫结束')

Mysql数据库插入数据
settings.py文件: 设置文件,在这里设置User-Agent,激活管道文件等...

ITEM_PIPELINES = {
    'douban.pipelines.DoubanPipeline': 300,
}

#关于数据库的相关配置
MYSQL_HOST = '127.0.0.1'
MYSQL_PORT = 3306
MYSQL_USER = ''
MYSQL_PWD = ''
MYSQL_DB = ''
class ChinazprojectPipeline(object):
    def __init__(self):
        """
        初始化方法
        """
        # self.file = open('chinaz.json','a')
        # 创建数据库链接
        self.client = pymysql.Connect(
            '127.0.0.1', 'root', '1234', 'chinaz', 3306, charset='utf8'
        )
        # 创建游标
        self.cursor = self.client.cursor()

    def open_spider(self, spider):
        """
        爬虫启动的时候会调用一次
        :param spider:
        :return:
        """
        print('爬虫开启了')

    def process_item(self, item, spider):
        """
        这个方法是必须实现的,爬虫文件中所有的item都会经过这个方法
        :param item: 爬虫文件传递过来的item对象
        :param spider: 爬虫文件实例化的对象
        :return:
        """
        # 存储到本地json文件
        # import json
        # json_data = json.dumps(data_dict,ensure_ascii=False)
        # self.file.write(json_data+'\n')
        # if isinstance(item,ChinaprojectWebInfoItem):
        #     print('网站信息')
        #     tablename = 'webinfo'
        # elif isinstance(item,ChinazprojectTagItem):
        #     print('网站分类信息')
        #     tablename = 'tag'

        data_dict = dict(item)

        sql,data = item.get_insert_sql_data(data_dict)
        try:
            # self.cursor.execute(sql, list(data_dict.values()))
            # self.client.commit()
            self.cursor.execute(sql,data)
            self.client.commit()
        except Exception as err:
            self.client.rollback()
            print(err)

        # 如果有多个管道文件,一定要注意return item,否则下个管道无法接收
        print('经过了管道')
        return item

    def close_spider(self, spider):
        """
        爬虫结束的时候会调用一次
        :param spider:
        :return:
        """
        # self.file.close()
        self.cursor.close()
        self.client.close()
        print('爬虫结束')

实现Mysql异步插入(数据量非常大时,采用异步插入)

from twisted.enterprise import adbapi


class ChinazprojectPipeline(object):

    def __init__(self,dbpool):
        self.dbpool = dbpool


    @classmethod
    def from_crawler(cls,crawler):
        db_parmars = {
            'host':crawler.settings['MYSQL_HOST'],
            'user':crawler.settings['MYSQL_USER'],
            'passwd':crawler.settings['MYSQL_PWD'],
            'db':crawler.settings['MYSQL_DB'],
            'port':crawler.settings['MYSQL_PORT'],
            'charset':crawler.settings['MYSQL_CHARSET']
        }
        dbpool = adbapi.ConnectionPool('pymysql',**db_parmars)
        return cls(dbpool)

    def process_item(self,item,spider):
        query = self.dbpool.runInteraction(
            self.insert_data_to_mysql,
            item
        )
        query.addErrback(
            self.insert_err,
            item
        )
        return item

    def insert_data_to_mysql(self,cursor,item):
        data_dict = dict(item)
        sql,data = item.get_insert_sql_data(data_dict)
        cursor.execute(sql,data)


    def insert_err(self,failure,item):
        print(failure,'插入失败')

Scrapy Shell

启动

scrapy shell "http://hr.tencent.com/position.php?&start=0#a"

scrapy shell -s USER_AGENT="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36"  "http://hr.tencent.com/position.php?&start=0#a"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容