[关闭]
@nemos 2017-05-06T02:10:38.000000Z 字数 10946 阅读 1196

scrapy

py


快速入门

  1. $ scrapy startproject tutorial # 初始化一个项目
  1. import scrapy
  2. class MySpider(scrapy.Spider):
  3. name = "myspider" # 爬虫名,必须唯一
  4. start_urls = []
  5. # 初始化的url,上下选其一
  6. def start_requests(self):
  7. urls = []
  8. for url in urls:
  9. yield scrapy.Request(url=url, callback=self.parse)
  10. #必须返回一个Request对象的迭代器,显式指定开始爬取的url和解析的函数
  11. # 默认response处理函数
  12. def parse(self, response):
  13. # 消息派发,对response存储完后再取其中有用的url进行增量爬取
  14. self.process_data(response)
  15. yield scrapy.Request(response.url, callback=self.parse)
  16. def process_data(self, response):
  17. pass

命令行启动

  1. $ scrapy crawl myspider

详细说明

目录层次

  1. scrapy.cfg: 项目的配置文件
  2. tutorial/: 该项目的python模块, 在这里添加代码
  3. items.py: 项目中的item文件
  4. pipelines.py: 项目中的pipelines文件.
  5. settings.py: 项目全局设置文件.
  6. spiders/ 爬虫模块目录

脚本启动

  1. from scrapy.crawler import CrawlerProcess
  2. from scrapy.utils.project import get_project_settings
  3. from spiders.MySpider import MySpider
  4. # 获取settings.py模块的设置
  5. settings = get_project_settings()
  6. process = CrawlerProcess(settings=settings)
  7. # 指定要启动的爬虫
  8. process.crawl(MySpider)
  9. # 启动爬虫,会阻塞,直到爬取完成
  10. process.start()

Spider

  1. class MySpider(scrapy.Spider):
  2. name #唯一名称
  3. allowed_domains #指定域名范围
  4. start_urls #指定开始爬取的链接
  5. custom_settings #重写默认设置
  6. crawler #指定爬取者,被from_crawler()指定,Crawler的实例
  7. settings #指定设置,Settings的实例
  8. logger #指定logger
  9. #######################################################
  10. from_crawler(crawler, *args, **kwargs) #指定Crawler实例,并向__init__传递参数
  11. start_requests() #必须返回Requrst对象的迭代对象
  12. make_requests_from_url(url) #接受url返回Request对象,默认回调parse
  13. parse(response) #默认的回调,解析请求内容
  14. log(message[, level, component]) #设置logger
  15. closed(reason) #但爬虫被关闭时被调用
  16. ##########################
  17. CrawlSpider # 可以定制规则

Request

  1. # 构造参数
  2. class scrapy.http.Request(
  3. url[,
  4. callback, # 响应的函数
  5. method='GET',
  6. headers,
  7. body,
  8. cookies,
  9. meta,
  10. encoding='utf-8',
  11. priority=0, # 优先级
  12. dont_filter=False, # 请求完是否过滤,想多次请求指定True
  13. errback] # 请求失败的回调函数
  14. )
  15. # 实例属性
  16. request.url
  17. request.method
  18. request.headers
  19. request.body
  20. request.meta # 包含请求元数据的字典,当请求被复制时会被浅拷贝
  21. request.copy() # 返回一个请求的拷贝
  22. request.replace() # 返回一个替换过的请求,参数与初始化时相同
  1. # 用于表单验证
  2. class scrapy.http.FormRequest(url[, formdata, ...])
  3. #返回一个填充好的FormRequest实例
  4. scrapy.FormRequest.from_response(
  5. response[, # 需要填充的表单
  6. formname=None,
  7. formnumber=0,
  8. formdata=None, # 需要填充的数据
  9. formxpath=None,
  10. formcss=None,
  11. clickdata=None,
  12. dont_click=False,
  13. ...]
  14. )
  15. # 模拟登陆示例
  16. class LoginSpider(scrapy.Spider):
  17. name = 'example'
  18. start_urls = ['http://www.example.com/users/login.php']
  19. def parse(self, response):
  20. return scrapy.FormRequest.from_response(
  21. response,
  22. formdata={'username': 'john', 'password': 'secret'},
  23. callback=self.after_login)
  24. def after_login(self, response): #执行一些登陆后的操作
  25. if "authentication failed" in response.body: #检查是否成功登陆
  26. self.logger.error("Login failed")

Response

  1. # 构造方法
  2. class scrapy.http.Response(url[, status=200, headers=None, body=b'', flags=None, request=None])
  3. # 实例属性
  4. response.url # 请求的url
  5. response.status # 响应状态码
  6. response.headers # 响应头,使用get()获得第一个,getlist()获得所有
  7. response.body # 响应体
  8. response.request # 响应的请求实例
  9. response.meta # 相当与request.meta
  10. response.flags # 响应的标记
  11. response.copy() # 返回响应的拷贝
  12. response.replace() # 返回替换过的响应对象
  13. response.urljoin(url) # 构造一个绝对路径
  1. class scrapy.http.TextResponse(url[, encoding[, ...]])
  2. text_response.text # 响应体,作为unicode返回,同response.body.decode(response.encoding)
  3. text_response.encoding # 指定的编码
  4. text_response.selector # 选择器支持css和xpath
  5. text_response.css() # 快捷方式
  6. text_response.xpath() # 快捷方式
  7. text_response.body_as_unicode() # 同text

Selector

selector相当于节点,通过选择再到子节点的集合,所有可以支持链式调用。
注意css和xpath的内部实现是相同的所以可以混合使用。

  1. # 构造
  2. class scrapy.selector.Selector(
  3. response=None, # 从response中实例
  4. text=None, # 同上
  5. type=None
  6. )
  7. # 使用
  8. # response实例的快捷方法
  9. response.css() #返回SelectorList
  10. response.xpath() #选择不到对象会返回None
  11. response.css('img').xpath('@src') # 混合使用
  12. html = '<html><body><span>good</span><span>buy</span></body></html>'
  13. selector = Selector(text=html)
  14. nodes = selector.xpath('//span')
  15. for node in nodes:
  16. node.extract() # 取出内容返回list
  17. node.extract_first() # 取得list的第一个元素

css教程
xpathe教程
xpath教程2


Item & Loader

用于结构化数据
使用

  1. # item.py
  2. from scrapy import Item, Field
  3. class MyItem(Item):
  4. title = Field()
  5. link = Field()
  6. desc = Field()
  7. # spider.py
  8. class MySpider(scrapy.Spider):
  9. ...
  10. def parse(self, response):
  11. for sel in response.xpath('//ul/li'):
  12. item = MyItem()
  13. item['title'] = sel.xpath('a/text()').extract()
  14. item['link'] = sel.xpath('a/@href').extract()
  15. tem['desc'] = sel.xpath('text()').extract()
  16. print dmoz_item

使用ItemLoader加载数据

  1. from scrapy.loader import ItemLoader
  2. from myproject.items import MyItem
  3. def parse(self, response):
  4. loader = ItemLoader(item=MyItem, response=response)
  5. loader.add_xpath('name', '//div[@class="xxx"]')
  6. loader.add_css('stock', 'p#xxx]')
  7. loader.add_value('last_updated', 'today') # you can also use literal values
  8. return loader.load_item() # 填充item

定制ItemLoader
数据流动方向:
xpath或css解析 > input_processor > loader > output_processor > item

  1. from scrapy.contrib.loader import ItemLoader
  2. from scrapy.contrib.loader.processor import TakeFirst, MapCompose, Join
  3. class ProductLoader(ItemLoader):
  4. # 默认的输入/出处理器
  5. default_input_processor = TakeFirst()
  6. default_output_processor = TakeFirst()
  7. # in为输入时调用的处理器,out同理
  8. name_in = MapCompose(unicode.title)
  9. name_out = Join()

上下文字典,loader声明,创建,调用时都能使用到一个上下文字段用于处理上下文相关数据

  1. # 修改实例
  2. loader = ItemLoader(product)
  3. loader.context[‘unit’] = cm
  4. # 实例化时传入
  5. loader = ItemLoader(product, unit=’cm’)
  6. # 处理器支持
  7. class ProductLoader(ItemLoader):
  8. length_out = MapCompose(parse_length, unit=’cm’)

内置处理器
class scrapy.loader.processors 模块


Pipeline

数据深度处理

  1. # 过滤重复的链接
  2. from scrapy.exceptions import DropItem
  3. class DuplicatesPipeline(object):
  4. def __init__(self):
  5. self.links = set()
  6. # process_item为固定的回调函数名
  7. def process_item(self, item, spider):
  8. if item['link'] in self.links:
  9. # 跑出DropItem表示丢掉数据
  10. raise DropItem("Duplicate item found: %s" % item)
  11. else:
  12. self.links.add(item['link'])
  13. return item
  1. # 保存数据
  2. from Database import Database
  3. class DatabasePipeline(object):
  4. def __init__(self):
  5. self.db = Database
  6. def process_item(self, item, spider):
  7. if self.db.item_exists(item['id']):
  8. self.db.update_item(item)
  9. else:
  10. self.db.insert_item(item)

process_item外还有
open_spider, spider_closed
配置
setting.py全局配置

  1. ITEM_PIPELINES = {
  2. 'pipelines.DuplicatesPipeline.DuplicatesPipeline': 1,
  3. 'pipelines.DatabasePipelin.DatabasePipelin': 2,
  4. }

单独配置

  1. class MySpider(Spider):
  2. # 自定义配置
  3. custom_settings = {
  4. 'ITEM_PIPELINES': {
  5. 'tutorial.pipelines.DatabasePipeline.DatabasePipelin': 1,
  6. },
  7. }

Rule

对连接规则的提取

  1. from scrapy.spiders import CrawlSpider, Rule
  2. from scrapy.linkextractors import LinkExtractor
  3. class LianjiaSpider(CrawlSpider):
  4. name = "lianjia"
  5. allowed_domains = ["lianjia.com"]
  6. start_urls = [
  7. "http://bj.lianjia.com/ershoufang/"
  8. ]
  9. rules = [
  10. # 匹配正则表达式,处理下一页
  11. Rule(LinkExtractor(allow=(r'http://bj.lianjia.com/ershoufang/pg\s+$',)), callback='parse_item'),
  12. # 匹配正则表达式,结果加到url列表中,设置请求预处理函数
  13. # Rule(FangLinkExtractor(allow=('http://www.lianjia.com/client/', )), follow=True, process_request='add_cookie')
  14. ]
  15. def parse_item(self, response):
  16. pass
  1. class scrapy.spiders.Rule(
  2. link_extractor,
  3. callback=None, # link_ex的回调函数
  4. cb_kwargs=None, # 附加参数
  5. follow=None, # 是否对请求完的链接应用当前的规则
  6. process_links=None, # 处理所有连接的回调用于过滤
  7. process_request=None # 链接请求预处理添加headers之类的
  8. )
  1. class scrapy.linkextractors.lxmlhtml.LxmlLinkExtractor(
  2. allow=(), # 提取满足正则的连接
  3. deny=(), # 排除满足正则的链接
  4. allow_domains=(), #
  5. deny_domains=(),
  6. deny_extensions=None, # 排除的后缀名
  7. restrict_xpaths=(), # 满足xpath的链接
  8. restrict_css=(), # 同上
  9. tags=('a', 'area'), # 提取指定标签下的连接
  10. attrs=('href', ), # 提取拥有满足属性的连接
  11. canonicalize=True, # 是否规范化
  12. unique=True, # 链接去重
  13. process_value=None # 值处理函数
  14. )

Middleware

  1. import random
  2. class RandomUserAgentMiddleware(object):
  3. """Randomly rotate user agents based on a list of predefined ones"""
  4. def __init__(self, agents):
  5. self.agents = agents
  6. # 从crawler构造,USER_AGENTS定义在crawler的配置的设置中
  7. @classmethod
  8. def from_crawler(cls, crawler):
  9. return cls(crawler.settings.getlist('USER_AGENTS'))
  10. # 从settings构造,USER_AGENTS定义在settings.py中
  11. @classmethod
  12. def from_settings(cls, settings):
  13. return cls(settings.getlist('USER_AGENTS'))
  14. def process_request(self, request, spider):
  15. # 设置随机的User-Agent
  16. request.headers.setdefault('User-Agent', random.choice(self.agents))

DownloaderMiddleware 下载中间件
注意所有函数都是惰性的

  1. class scrapy.downloadermiddlewares.DownloaderMiddleware
  2. def process_request(request, spider):
  3. yield Request # 返回请求,并向下传递
  4. yield None # 忽略次中间件并传递到下一个中间件
  5. yield Response # 直接中断,返回请求
  6. raise IgnoreRequest # 调用self.process_exception(),无则调用Request.errback
  7. def process_response(request, reponse, spider):
  8. yield Request # 中断并传递请求到下载器
  9. yield Response # 传递到下一个中间件
  10. raise IgnoreRequest # 调用Request.errback() 无则忽略
  11. def process_exception(request, exception, spider):
  12. yield None
  13. yield Response
  14. yield Request
  15. # setting.py
  16. DOWNLOADER_MIDDLEWARES = {
  17. 'myproject.middlewares.CustomDownloaderMiddleware': 543,
  18. # None 指定关闭
  19. 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
  20. }

内置的下载中间件

  1. class scrapy.downloadermiddlewares.cookies.CookiesMiddleware
  2. # setting.py
  3. COOKIES_ENABLED
  4. COOKIES_DEBUG
  5. class scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware
  6. # setting.py
  7. DEFAULT_REQUEST_HEADERS
  8. class scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware
  9. # setting.py
  10. DOWNLOAD_TIMEOUT
  11. # 认证相关
  12. class scrapy.contrib.downloadermiddleware.httpauth.HttpAuthMiddleware
  13. # 缓存相关
  14. class scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware

单spider多cookies

  1. # 通过meta字段传递
  2. for i, url in enumerate(urls):
  3. yield scrapy.Request("http://www.example.com", meta={'cookiejar': i},
  4. callback=self.parse_page)
  5. # 转发时需要重新指明
  6. def parse_page(self, response):
  7. # do some processing
  8. return scrapy.Request("http://www.example.com/otherpage",
  9. meta={'cookiejar': response.meta['cookiejar']},
  10. callback=self.parse_other_page)

Setting

Cache

  1. # setting.py
  2. # 打开缓存
  3. HTTPCACHE_ENABLED = True
  4. # 设置缓存过期时间(单位:秒)
  5. HTTPCACHE_EXPIRATION_SECS = 0
  6. # 缓存路径(默认为:.scrapy/httpcache)
  7. HTTPCACHE_DIR = 'httpcache'
  8. # 忽略的状态码
  9. HTTPCACHE_IGNORE_HTTP_CODES = []
  10. # 缓存模式(文件缓存)
  11. HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

多线程

  1. # 默认Item并发数:100
  2. CONCURRENT_ITEMS = 100
  3. # 默认Request并发数:16
  4. CONCURRENT_REQUESTS = 16
  5. # 默认每个域名的并发数:8
  6. CONCURRENT_REQUESTS_PER_DOMAIN = 8
  7. # 每个IP的最大并发数:0表示忽略
  8. CONCURRENT_REQUESTS_PER_IP = 0

异常处理

  1. from scrapy.spidermiddlewares.httperror import HttpError
  2. from twisted.internet.error import DNSLookupError
  3. from twisted.internet.error import TimeoutError, TCPTimedOutError
  4. def errback(self, failure): #在Request初始化时指定的回调函数
  5. if failure.check(HttpError): #检查错误类型
  6. failure.value.response #响应的实例
  7. failure.request #请求的实例

meta

传递数据

  1. def parse_page1(self, response):
  2. item = MyItem()
  3. item['main_url'] = response.url
  4. request = scrapy.Request("http://www.example.com/some_page.html",
  5. callback=self.parse_page2)
  6. request.meta['item'] = item #使用meta传递了item
  7. return request
  8. def parse_page2(self, response):
  9. item = response.meta['item']
  10. item['other_url'] = response.url
  11. return item

指定特殊键

键名 含义
dont_redirect
dont_retry
handle_httpstatus_list
handle_httpstatus_all
dont_merge_cookies (see cookies parameter of Request constructor)
cookiejar
dont_cache
redirect_urls
bindaddress
dont_obey_robotstxt
download_timeout
download_maxsize
download_latency
proxy

引用

框架层次

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

Scrapy运行流程大概如下:

引擎从调度器中取出一个链接(URL)用于接下来的抓取
引擎把URL封装成一个请求(Request)传给下载器
下载器把资源下载下来,并封装成应答包(Response)
爬虫解析Response
解析出实体(Item),则交给实体管道进行进一步的处理
解析出的是链接(URL),则把URL交给调度器等待抓取

参考

scrapy学习笔记


添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注