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.
大致流程
- 在一个爬虫里,你抓取一个项目,把其中图片的URL放入 file_urls 组内。
- 项目从爬虫内返回,进入项目管道。
当项目进入 FilesPipeline,file_urls 组内的URLs将被Scrapy的调度器和下载器安排下载,当优先级更高,会在其他页面被抓取前处理。项目会在这个特定的管道阶段保持“locker”的状态,直到完成文件的下载(或者由于某些原因未完成下载)。 - 当文件下载完后,另一个字段(files)将被更新到结构中。这个组将包含一个字典列表,其中包括下载文件的信息,比如下载路径、源抓取地址(从 file_urls 组获得)和图片的校验码(checksum)。
- 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 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中的数据流由执行引擎控制,其过程如下:
- 引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)。
- 引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。
- 引擎向调度器请求下一个要爬取的URL。
- 调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)。
- 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。
- 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。
- Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
- 引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度器。
- (从第二步)重复直到调度器中没有更多地request,引擎关闭该网站。