scrapy一览

scrapy

scrapy是一个爬取网站数据,提取结构性数据的框架。注意敲重点是框架。框架就说明了什么?——提供的组件丰富,scrapy的设计参考了Django,可见一斑。但是不同于Django的是scrapy的可拓展性也很强,所以说,你说你会用python写爬虫,不了解点scrapy。。。。

scrapy使用了Twisted异步网络库来处理网络通讯,整体架构如下图:

Scrapy主要包括了以下组件:

    • 引擎(Scrapy)
      用来处理整个系统的数据流处理, 触发事务(框架核心)
    • 调度器(Scheduler)
      用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
    • 下载器(Downloader)
      用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
    • 爬虫(Spiders)
      爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
    • 项目管道(Pipeline)
      负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
    • 下载器中间件(Downloader Middlewares)
      位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
    • 爬虫中间件(Spider Middlewares)
      介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
    • 调度中间件(Scheduler Middewares)
      介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

好介绍到这里。面试题来了:谈谈你对scrapy架构的理解:

上面就是我的理解,你的理解与我的可能不一样,以你的为准,面试的时候如果被问到了。千万要说,这东西怎么说都对。不说的话,一定会被面试官反问一句:你还有什么要补充的吗?。。。。

安装:

pass

直接开始写代码吧。

项目上手,利用scrapy爬取抽屉。读到这里你是不是有点失望了?为什么不是淘宝?不是腾讯视频?不是知乎?在此说明下抽屉虽小,五脏俱全嘛。目的是为了展示下scrapy的基本用法没必要将精力花在处理反爬虫上。

在开始我们的项目之前,要确定一件事就是我们要爬取什么数据?怎么存储数据?这个案例使用到的Mysql最为容器存储数据的。设计的表结构如下:

有了这些字段就能着手于spider的编写了,第一步首先写item类

 1 import datetime
 2 import re
 3 
 4 import scrapy
 5 from scrapy.loader import ItemLoader
 6 from scrapy.loader.processors import Join, MapCompose, TakeFirst
 7 
 8 
 9 def add_title(value):
10     """字段之后加上签名"""
11     return value + 'Pontoon'
12 
13 
14 class ArticleItemLoader(ItemLoader):
15     """重写ItemLoader,从列表中提取第一个字段"""
16     default_output_processor = TakeFirst()
17 
18 
19 class ChouTiArticleItem(scrapy.Item):
20     """初始化items"""
21     title = scrapy.Field(
22         input_processor=MapCompose(add_title)
23     )
24     article_url = scrapy.Field()
25     article_url_id = scrapy.Field()
26     font_img_url = scrapy.Field()
27     author = scrapy.Field()
28     sign_up = scrapy.Field()
29 
30     def get_insert_sql(self):
31         insert_sql = '''
32                         insert into chouti(title, article_url, article_url_id, font_img_url, author, sign_up) 
33                         VALUES (%s, %s, %s, %s, %s, %s)
34         '''
35         params = (self["title"], self["article_url"], self["article_url_id"], self["font_img_url"],
36                   self["author"], self["sign_up"],)
37         return insert_sql, params
items.py

 下一步解析数据(这里并没由对下一页进行处理,省事!)

 1 # -*- coding: utf-8 -*-
 2 import scrapy
 3 from scrapy.http import Request
 4 
 5 from ..items import ChouTiArticleItem, ArticleItemLoader
 6 from ..utils.common import get_md5
 7 
 8 
 9 class ChoutiSpider(scrapy.Spider):
10     name = 'chouti'
11     allowed_domains = ['dig.chouti.com/']
12     start_urls = ['https://dig.chouti.com/']
13     header = {
14         "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
15                       "Chrome/71.0.3578.98 Safari/537.36"
16     }
17 
18     def parse(self, response):
19         article_list = response.xpath("//div[@class='item']")
20         for article in article_list:
21             item_load = ArticleItemLoader(item=ChouTiArticleItem(), selector=article)   # 注意这里的返回值并不是response了
22             article_url = article.xpath(".//a[@class='show-content color-chag']/@href").extract_first("")
23             item_load.add_xpath("title", ".//a[@class='show-content color-chag']/text()")
24             item_load.add_value("article_url", article_url)
25             item_load.add_value("article_url_id", get_md5(article_url))
26             item_load.add_xpath("font_img_url", ".//div[@class='news-pic']/img/@src")
27             item_load.add_xpath("author", ".//a[@class='user-a']//b/text()")
28             item_load.add_xpath("sign_up", ".//a[@class='digg-a']//b/text()")
29             article_item = item_load.load_item()  # 解析上述定义的字段
30             yield article_item
spiders.py

OK,这时我们的数据就被解析成了字典yield到了Pipline中了

 1 import MySQLdb
 2 import MySQLdb.cursors
 3 from twisted.enterprise import adbapi   # 将入库变成异步操作
 4 
 5 
 6 class MysqlTwistedPipleline(object):
 7     """抽屉Pipleline"""
 8     def __init__(self, db_pool):
 9         self.db_pool = db_pool
10 
11     @classmethod
12     def from_settings(cls, settings):
13         """内置的方法自动调用settings"""
14         db_params = dict(
15             host=settings["MYSQL_HOST"],
16             db=settings["MYSQL_DBNAME"],
17             user=settings["MYSQL_USER"],
18             password=settings["MYSQL_PASSWORD"],
19             charset="utf8",
20             cursorclass=MySQLdb.cursors.DictCursor,
21             use_unicode=True,
22         )
23         db_pool = adbapi.ConnectionPool("MySQLdb", **db_params)
24 
25         return cls(db_pool)
26 
27     def process_item(self, item, spider):
28         """使用twisted异步插入数据值数据库"""
29         query = self.db_pool.runInteraction(self.do_insert, item)    # runInteraction() 执行异步操作的函数
30         query.addErrback(self.handle_error, item, spider)     # addErrback() 异步处理异常的函数
31 
32     def handle_error(self, failure, item, spider):
33         """自定义处理异步插入数据的异常"""
34         print(failure)
35 
36     def do_insert(self, cursor, item):
37         """自定义执行具体的插入"""
38         insert_sql, params = item.get_insert_sql()
39         # chouti插入数据
40         cursor.execute(insert_sql, (item["title"], item["article_url"], item["article_url_id"],
41                                     item["font_img_url"], item["author"], item["sign_up"]))
piplines.py

再到settings.py中配置一下文件即可。

 1 ITEM_PIPELINES = {
 2     'ChoutiSpider.pipelines.MysqlTwistedPipleline': 3,
 3 
 4 }
 5 ...
 6 
 7 MYSQL_HOST = "127.0.0.1"
 8 MYSQL_DBNAME = "article_spider"    # 数据库名称
 9 MYSQL_USER = "xxxxxx"
10 MYSQL_PASSWORD = "xxxxx"
settings.py

ok练手的项目做完了,接下来正式进入正题。

scrapy—pipeline组件

pipeline是用来做数据的持久化的,内部源码中给我们提供一些方法,下面介绍几种常见的。

 1 from scrapy.exceptions import DropItem
 2 
 3 class CustomPipeline(object):
 4     def __init__(self,v):
 5         self.value = v
 6 
 7     def process_item(self, item, spider):
 8         # 操作并进行持久化
 9 
10         # return表示会被后续的pipeline继续处理
11         return item
12         
13         # raise DropItem()  表示将item丢弃,不会被后续pipeline处理
14 
15     @classmethod
16     def from_crawler(cls, crawler):
17         """
18         初始化时候,用于创建pipeline对象
19         :param crawler: 
20         :return: 
21         """
22         val = crawler.settings.getint('XXXXX')
23         return cls(val)     # 注意这种创建对象的方式,在scrapy源码中会大量遇见
24 
25     def open_spider(self,spider):
26         """
27         爬虫开始执行时,调用
28         :param spider: 
29         :return: 
30         """
31         print('spider start')
32 
33     def close_spider(self,spider):
34         """
35         爬虫关闭时,被调用
36         :param spider: 
37         :return: 
38         """
39         print('spider close')
pipeline.py

scrapy—url去重的策略

url去重的问题用于分布式爬虫。

猜你喜欢

转载自www.cnblogs.com/pontoon/p/10247589.html