scrapy爬虫

Scrapy框架结构及工作原理

图1
组件 描述 类型
ENGINE 引擎,框架的核心,其他所有组件在其控制下协同工作 内部组件
SCHEDULE 调度器,负责对SPIRDER提交的下载请求进行调度 内部组件
DOWNLOADER 下载器,负责下载页面(发送HTTP请求/接收HTTP响应) 内部组件
SPIRDER 爬虫,负责提取页面中的数据,并产生对新页面的下载请求 用户实现
MIDDLEWARE 中间件,负责对Request对象和Response对象进行处理 可选组件
ITEMPIPELINE 数据管道,负责对爬取到的数据进行处理 可选组件
三种对象
对象 描述
REQUEST Scrapy中的HTTP请求对象
RESPONSE Scrapy中的HTTP响应对象
ITEM 从页面中爬取的一项数据
几种对象在框架中的流动过程
  • 当spider要爬取某url地址的页面时,需使用该url构造一个request对象,提交给engine(图1中的1)。
  • request对象随后进入schedule按某种算法进行排队,之后的某个时刻schedule将其出队,送往Downloader(图1中的2,3,4)
  • DOWNLOADER根据Request对象中的URL地址发送一次HTTP请求到网站服务器, 之后用服务器返回的HTTP响应构造出一个Response对象, 其中包含页面的HTML文本(图1中的5)
  • Response对象最终会被递送给SPIDER的页面解析函数(构造Request对象时指定) 进行处理, 页面解析函数从页面中提27取数据, 封装成Item后提交给ENGINE, Item之后被送往ITEMPIPELINES进行处理, 最终可能由EXPORTER(图2-1中没有显示) 以某种数据格式写入文件(csv, json) ; 另一方面、页面解析函数还从页面中提取链接(URL) , 构造出新的Request对象提交给ENGINE(图1中的6、 7、8)
    理解了框架中的数据流,也就理解了Scrapy爬虫的工作原理

2、编写spider

2.1、request对象

1、Request对象用来描述一个HTTP请求, 下面是其构造器方法的参数列表

Request(url,
    callback, 
    headers, 
    body, meta, priority=0, errback)

2、scrapy.request( )的参数
官方文档:https://doc.scrapy.org/en/latest/topics/request-response.html

2.2、response对象
  • 当一个页面下载完成时,下载器依据HTTP响应头部中的Content-Type信息创建某个Response的子类对象。
  • 下面介绍HtmlResponse对象的常用属性,前两个方法用于提取数据,后一个方法用于构造绝对url。
    1、xpath(query) :使用XPath选择器在Response中提取数据,实际上它是response.selector.xpath方法的快捷方式
    2、css(query):使用CSS选择器在Response中提取数据,实际上它是response.selector.css方法的快捷方式
    3、urljoin(url):用于构造绝对url。当传入的url参数是一个相对地址时,根据response.url计算出相应的绝对url。
    例如,response.url为http://www.example.com/a,url为b/index.html,调用response.urljoin(url)的结果为 http://www.example.com/a/b/index.html。
    其他参数详情参见:书《精通Scrapy网络爬虫框架》P16
2.3 spider开发流程
  • Scrapy框架提出以下问题让用户在Spider子类中作答
    1、爬虫从哪个或哪些页面开始爬取?
    2、对于一个已下载的页面,提取其中的哪些数据?
    3、爬取完当前页面后,接下来爬取哪个或哪些页面?
  • 为爬虫设置其实爬取地址的方式
    1、定义start_urls属性
    2、实现start_requests方法
2.4 实现一个spider只需要下面4个步骤:
  • 步骤 01 继承scrapy.Spider。
  • 步骤 02 为Spider取名。
  • 步骤 03 设定起始爬取点。
  • 步骤 04 实现页面解析函数。

3、使用selector提取数据

3.1、selector对象
  • 提取数据的方法:
    调用Selector或SelectorLis对象的以下方法可将选中的内容提取:
    1、extract()
    2、re()
    3、extract_first() (SelectorList专有)
    4、re_first() (SelectorList专有)
3.2、Response内置selector
  • 页面测试
from scrapy.http import HtmlResponse
response = HtmlResponse(url='http://www.example.com', body=body, encoding='utf8')
response.selector
3.3、XPath

1、XPath即XML路径语言(XML Path Language),它是一种用来确定xml文档中某部分位置的语言。
2、xml文档的节点有多种类型,其中最常用的有以下几种:
2.1、根节点 整个文档树的根。
2.2、元素节点 html、body、div、p、a。
2.3、属性节点 href。
2.4、文本节点 Hello world、Click here。
3、节点间的关系有以下几种:
3.1、父子 body是html的子节点,p和a是div的子节点。反过来,div是p和a的父节点。
3.2、兄弟 p和a为兄弟节点。
3.3、祖先/后裔 body、div、p、a都是html的后裔节点;反过来html是body、div、p、a的祖先节点。
4、基础语法
详情参见:http://www.w3school.com.cn/xpath/xpath_syntax.asp

4、使用Item封装数据

4.1、Item和Field(利用BookItem代替字典)
  • Item基类
    自定义数据类(如BookItem)的基类。
  • Field类
    用来描述自定义数据类包含哪些字段(如name、price等)。
from scrapy import Item, Field
class BookeItem(Item):
    name = Field()
    price = Field()
# Item支持字典接口,因此BookItem在使用上和Python字典类似,可按以下方式创建BookItem对象:
# 方法一:
>>> book1 = BookItem(name='Needful Things', price=45.0)
>>> book1
{'name': 'Needful Things', 'price': 45.0}
# 方法二:
>>> book2 = BookItem()
>>> book2
{}
>>> book2['name'] = 'Life of Pi'
>>> book2['price'] = 32.5
{'name': 'Life of Pi', 'price': 32.5}
  • 修改之前的BooksSpider,使用BookItem替代Python字典,代码如下:
 # 改用parse_book作为回调函数
    def parse_book(self, response):
        # 提取数据
        # 每一本书的信息在
中 # css()方法找到所有这样的article元素,并依次迭代 for book in response.css('article.product_pod'): # 书名信息在article>h3>a元素的title属性里 name = book.xpath('./h3/a/@title').extract_first() # 书价信息在

的TEXT中 price = book.css('p.price_color::text').extract_first() book = BookeItem() book['name'] = name book['price'] = price yield book

4.2、扩展Item子类
  • 新建一个ForeignBookItem类,增添一个译者字段,其他的继承BookItem类,代码如下:
from scrapy import Item, Field

class BookeItem(Item):
    name = Field()
    price = Field()

class ForeignBookItem(BookeItem):
    translator=Field()
4.3、Field元数据
  • field()的元数据可以是列表、字符串或者函数
class BookItem(Item):
authors = Field(serializer=lambda x: '|'.join(x))

5、使用Item Pipeline处理数据

  1. 以下是Item Pipeline的几种典型应用:
    1. 清洗数据
    2. 验证数据的有效性
    3. 过滤掉重复的数据
    4. 将数据存入数据库
5.1、Item Pipline

#######5.1.1、实现Item Pipline

  1. item pipeline常用方法:
    1. process_item(item, spider):处理数据
    2. open_spider(self, spider):打开Spider打开时(处理数据前)回调该方法,通常该方法用于在开始处理数据之前完成某些初始化工作,如连接数据库。
    3. close_spider(self, spider):关闭Spider关闭时(处理数据后)回调该方法,通常该方法用于在处理完所有数据之后完成某些清理工作,如关闭数据库。
    4. from_crawler(cls, crawler):调回创建Item Pipeline对象时回调该类方法。通常,在该方法中通过crawler.settings读取配置,根据配置创建Item Pipeline对象。
      代码如下:
class PriceConverterPipline(object):
    exchange_rate = 8.5309

    def process_item(self, item, spider):
        # 提取item的price字段
        # 去掉前面的英镑符号,转换为float类型,乘以汇率
        price = float(item['price'][1:]) * self.exchange_rate

        # 保留两位小数,赋值回item的price字段
        item['price'] = '¥%.2f' % price
        return item

上述代码中的process_item方法实现非常简单, 将书籍的英镑价格转换为浮点数, 乘以汇率并保留2位小数, 然后赋值回item的price字段, 最后返回被处理过
的item。
#######5.1.2、启用Item Pipline
在Scrapy中, Item Pipeline是可选的组件, 想要启用某个(或某些) Item Pipeline, 需要在配置文件settings.py中进行配置:

ITEM_PIPELINES = {
   'scrapyStudy.pipelines.PriceConverterPipline': 300,
}

其中每一项的键是每一个Item
Pipeline类的导入路径, 值是一个0~1000的数字,同时启用多个Item Pipeline时, Scrapy根据这些数值决定各Item Pipeline处理数据的先后次序, 数值小的在前。
#######5.2.1 过滤重复数据
以书名作为主键(实际应以ISBN编号为主键, 但是仅爬取了书名和价格) 进行去重, 实现DuplicatesPipeline代码如下:

class DuplicatesPipeline(object):
    def __init__(self):
        self.book_set = set()

    def process_item(self, item, spider):
        name = item['name']
        if name in self.book_set:
            raise DropItem("Duplicate book find : %s%" % item)
        self.book_set.add(name)
        return item

#######5.2.2 将数据存储到Mysql
代码如下:

class MysqlPipline(object):

    def open_spider(self, spider):
        db = 'spider'
        host = '192.168.100.173'
        port = 3306
        user = 'root'
        passwd = '05zf*+VCT0YeIfx'
        self.db_conn = pymysql.connect(host=host, port=port, db=db, user=user, passwd=passwd, charset='utf8')
        self.db_cur = self.db_conn.cursor()

    def close_spider(self, spider):
        # 爬取完全部的数据后调用commit
        # self.db_conn.commit()
        self.db_conn.close()

    def process_item(self, item, spider):
        if isinstance(item, BookeItem):
            self.insert_book(item)
        return item

    def insert_book(self, item):
        values = (
            item['name'],
            item['price'])
        sql = 'INSERT IGNORE INTO spider_book_test (name,price) VALUES (%s,%s)'
        self.db_cur.execute(sql, values)
        self.db_conn.commit()

你可能感兴趣的:(scrapy爬虫)