Scrapy 功能整理

spider的功能整理

# -*- coding: utf-8 -*-
import scrapy
from Demo.items import DemoItem

class DemoSpider(scrapy.Spider):
    #爬虫名
    name = 'Demo'
    #爬虫的爬取域
    allowed_domains = ['XXX.com']
    #起始的URL列表
    start_urls = [baseURL + str(offset)]
    #rules匹配规则
    rules = (
        # 匹配 'ABC.php' (但不匹配 'DEF.php') 的链接并跟进链接
        Rule(LinkExtractor(allow=('ABC\.php', ), deny=('DEF\.php', ))),
        # 提取匹配 'item.php' 的链接并使用spider的parse_item方法进行分析
        Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item')
        )

    #默认的parse方法
    def parse(self, response):

    pass

    #处理response并返回处理的数据item以及跟进的URL
    def parse_item(self, response):
        item = DemoItem()
        #从response中获得数据集
        node_list = response.xpath("XXX")
        #提取数据,并将其转换为utf-8编码,然后传入item中
        for node in node_list:
            item["XXX"] =node.xpath("./XXX/text()").extract()[0]
            item["XXX"] =node.xpath("./XXX/a/@href").extract()[0]
            #有为空的情况?
            if len(node.xpath("./XXX/text()")):
                item["XXX"] =node.xpath("./XXX/text()").extract()[0]
            else:
                item["XXX"] = ""
            #返回item数据
            yield item
        #返回跟进的URL请求
        if len(response.xpath("XXX")) == 0:
            url = response.xpath("XXX/@href").extract()[0]
            yield scrapy.Request("XXX" + url,callback = self.parse)

        pass

选择器selectors整理

Scrapy选择器构建于 lxml 库之上,这意味着它们在速度和解析准确性上非常相似。

用法:
scrapy shell http://www.baidu.com
response.selector.xpath(‘//span/text()’).extract()

由于在response中使用XPath、CSS查询十分普遍,因此,Scrapy提供了两个实用的快捷方式:

>>> response.xpath("//title/text()")
[<Selector xpath='//title/text()' data='百度一下,你就知道'>]

>>> response.css('title::text')
[<Selector xpath='descendant-or-self::title/text()' data='百度一下,你就知道'>]

>>> response.xpath('//title/text()').extract()
['百度一下,你就知道']

>>> response.xpath('//title/text()').extract()[0]
'百度一下,你就知道'

>>> response.xpath('//title/text()').extract()[0] is None
False

xpath和css的用法(摘自scrapy帮助文档):

//HTML结构
</head>
 <body>
  <div id='images'>
   <a href='image1.html'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
   <a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
   <a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
   <a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
   <a href='image5.html'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
  </div>
 </body>
</html>
>>> response.xpath('//base/@href').extract()
[u'http://example.com/']

>>> response.css('base::attr(href)').extract()
[u'http://example.com/']

>>> response.xpath('//a[contains(@href, "image")]/@href').extract()
[u'image1.html',
 u'image2.html',
 u'image3.html',
 u'image4.html',
 u'image5.html']

>>> response.css('a[href*=image]::attr(href)').extract()
[u'image1.html',
 u'image2.html',
 u'image3.html',
 u'image4.html',
 u'image5.html']

>>> response.xpath('//a[contains(@href, "image")]/img/@src').extract()
[u'image1_thumb.jpg',
 u'image2_thumb.jpg',
 u'image3_thumb.jpg',
 u'image4_thumb.jpg',
 u'image5_thumb.jpg']

>>> response.css('a[href*=image] img::attr(src)').extract()
[u'image1_thumb.jpg',
 u'image2_thumb.jpg',
 u'image3_thumb.jpg',
 u'image4_thumb.jpg',
 u'image5_thumb.jpg']

结合正则表达式使用选择器:
由于.re() 方法返回unicode字符串的列表。所以无法构造嵌套式的 .re() 调用

>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')

[u'My image 1',
 u'My image 2',
 u'My image 3',
 u'My image 4',
 u'My image 5']

#返回单个数据re_first
>>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)')

u'My image 1'

注意区别:

# 得到整篇文档的p标签
>>> for p in divs.xpath('//p'):  
...     print p.extract()

# 得到divs内部的p标签
>>> for p in divs.xpath('.//p'):  
...     print p.extract()

#得到divs下的直系p标签
>>> for p in divs.xpath('p'):
...     print p.extract()

item整理

import scrapy

class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)

item字段就像是一个字典集合一样,可以通过键值的方式进行访问。通过键值的映射关系,提高了代码的正确性和可用性。

item.keys() //获得所有键
item.items() //获得键值集合
dict(item) //转换成字典对象

pipeline管道的整理

当Item在Spider中被收集之后,它将会被传递到Item Pipeline,一些组件会按照一定的顺序执行对Item的处理。

pipeline的作用
1. 清理HTML数据
2. 验证爬取的数据(检查item包含某些字段)
3. 查重(并丢弃)
4. 将爬取结果保存到数据库中

import json

class MyspiderPipeline(object):
    def __init__(self):
        self.f = open("demo.json","wb+")

    #该方法是必要的
    def process_item(self, item, spider):
        content = json.dumps(dict(item),ensure_ascii = False) + ",\n"
        self.f.write(content.encode("utf-8"))
        return item

    def colse_spider(self,spider):
        self.f.close()

将数据写入mongoDB(摘自scrapy帮助文档

import pymongo

class MongoPipeline(object):

    collection_name = 'scrapy_items'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

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

    def process_item(self, item, spider):
        self.db[self.collection_name].insert(dict(item))
        return item

去重操作:

from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):

    def __init__(self):
        self.ids_seen = set()

    def process_item(self, item, spider):
        if item['id'] in self.ids_seen:
            raise DropItem("Duplicate item found: %s" % item)
        else:
            self.ids_seen.add(item['id'])
            return item

settings.py中通过设置ITEM_PIPELINES决定管道优先级:
数字越小优先级越高

ITEM_PIPELINES = {
    'myproject.pipelines.PricePipeline': 300,
    'myproject.pipelines.JsonWriterPipeline': 800,
}

模拟用户登录

import scrapy

class LoginSpider(scrapy.Spider):
    name = 'example.com'
    start_urls = ['http://www.example.com/users/login.php']

    def parse(self, response):
        return scrapy.FormRequest.from_response(
            response,
            formdata={'username': 'john', 'password': 'secret'},
            callback=self.after_login
        )

    def after_login(self, response):
        # check login succeed before going on
        if "authentication failed" in response.body:
            self.logger.error("Login failed")
            return

邮件的发送

在spider中执行结束后发送邮件(前提是获取了授权码)
参考:http://blog.csdn.net/you_are_my_dream/article/details/60868329

def closed(self, reason):# 爬取结束的时候发送邮件  
        from scrapy.mail import MailSender  

        mailer = MailSender(  
            smtphost = "smtp.163.com",  # 发送邮件的服务器  
            mailfrom = "***********@163.com",   # 邮件发送者  
            smtpuser = "***********@163.com",   # 用户名  
            smtppass = "***********",  # 发送邮箱的密码不是你注册时的密码,而是授权码!!!切记!  
            smtpport = 25   # 端口号  
        )  
        body = u""" 
        发送的邮件内容 
        """  
        subject = u'发送的邮件标题'  
        # 如果说发送的内容太过简单的话,很可能会被当做垃圾邮件给禁止发送。  
        mailer.send(to=["****@qq.com", "****@qq.com"], subject = subject.encode("utf-8"), body = body.encode("utf-8"))  

下载及处理文件和图片

Scrapy为下载item中包含的文件提供了一个可重用的 item pipelines .
这些pipeline有些共同的方法和结构(我们称之为media pipeline)。一般来说你会使用Files Pipeline或者
Images Pipeline.

大致流程

  1. 在一个爬虫里,你抓取一个项目,把其中图片的URL放入 file_urls 组内。
  2. 项目从爬虫内返回,进入项目管道。
    当项目进入 FilesPipeline,file_urls 组内的URLs将被Scrapy的调度器和下载器安排下载,当优先级更高,会在其他页面被抓取前处理。项目会在这个特定的管道阶段保持“locker”的状态,直到完成文件的下载(或者由于某些原因未完成下载)。
  3. 当文件下载完后,另一个字段(files)将被更新到结构中。这个组将包含一个字典列表,其中包括下载文件的信息,比如下载路径、源抓取地址(从 file_urls 组获得)和图片的校验码(checksum)。
  4. files 列表中的文件顺序将和源 file_urls 组保持一致。如果某个图片下载失败,将会记录下错误信息,图片也不会出现在 files 组中。

注意:使用图片管道( Images Pipeline)需要先安装Pillow 库。

启用 Media Pipeline

首先在settings.py中添加ITEM_PIPELINES 字段(设定优先级),FILES_STORE字段(设置文件存放位置),IMAGES_STORE字段(图片存放位置)。

import os
import scrapy
from scrapy.pipelines.images import ImagesPipeline
from Douyu.settings import IMAGES_STORE as img_store

#继承ImagesPipeline类,对图片进行处理
class DouyuPipeline(ImagesPipeline):

    def get_media_requests(self,item,info):
        imglink = item["imglink"]
        yield scrapy.Request(imglink)

    def item_completed(self, results, item, info):
        imagepath = [x["path"] for ok,x in results if ok]
        os.rename(img_store + imagepath[0],img_store + item["nickname"] + ".jpg")
        return item

对于图片会根据它的链接地址生成哈希值,然后图片会被命名为该哈希值然后存储在一个full文件夹下。

若要生成缩略图,需要增设:

IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (270, 270),
}

scrapy的架构

架构组件

scrapy架构

Scrapy Engine
引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。

调度器(Scheduler)
调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。

下载器(Downloader)
下载器负责获取页面数据并提供给引擎,而后提供给spider。

Spiders
Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。 更多内容请看 Spiders 。

Item Pipeline
Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)。

下载器中间件(Downloader middlewares)
下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。

Spider中间件(Spider middlewares)
Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。

数据流(Data flow)过程

Scrapy中的数据流由执行引擎控制,其过程如下:

  1. 引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)。
  2. 引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。
  3. 引擎向调度器请求下一个要爬取的URL。
  4. 调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)。
  5. 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。
  6. 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。
  7. Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
  8. 引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度器。
  9. (从第二步)重复直到调度器中没有更多地request,引擎关闭该网站。

参考

Scrapy 1.0 文档

使用scrapy的mail模块发送邮件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值