细读http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html

目录

Scrapy介绍

安装

基本命令

项目结构以及爬虫应用介绍

简单使用示例

选择器

数据格式化、持久化

中间件

自定义命令

自定义扩展(涉及信号)

避免重复访问(去重)

settings说明

其他

TinyScrapy(自定义框架)

示例

补充

  - 数据采集器

  - log

Scrapy介绍

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中。
其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。

Scrapy 使用了 Twisted异步网络库来处理网络通讯。整体架构大致如下

Scrapy Engine

引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。 详细内容查看下面的数据流(Data Flow)部分。

调度器(Scheduler)

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

下载器(Downloader)

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

Spiders

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

Item Pipeline

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

下载器中间件(Downloader middlewares)

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

Spider中间件(Spider middlewares)

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

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,引擎关闭该网站。

安装

  1. Linux
  2. pip3 install scrapy
  3.  
  4. Windows
  5. a. pip3 install wheel
  6. b. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
  7. c. 进入下载目录,执行 pip3 install Twisted17.1.0cp35cp35mwin_amd64.whl
  8. d. pip3 install scrapy
  9. e. 下载并安装pywin32https://sourceforge.net/projects/pywin32/files/ 或者 pip install pywin32

安装(linux、windows)

基本命令

  1. 1. scrapy startproject 项目名称
  2. - 在当前目录中创建中创建一个项目文件(类似于Django
  3.  
  4. 2. scrapy genspider [-t template] <name> <domain>
  5. - 创建爬虫应用
  6. 如:
  7. scrapy gensipider -t basic oldboy oldboy.com
  8. scrapy gensipider -t xmlfeed autohome autohome.com.cn
  9. PS:
  10. 查看所有命令:scrapy gensipider -l
  11. 查看模板命令:scrapy gensipider -d 模板名称
  12.  
  13. 3. scrapy list
  14. - 展示爬虫应用列表
  15.  
  16. 4. scrapy crawl 爬虫应用名称
  17. - 运行单独爬虫应用
  18. scrapy crawl xxx --nolog
  19.  
  20. 5.scrapy shell 进入shell

基本命令(创建项目、运行爬虫,类似于Django)

  1. 全局命令
  2.  
  3. startproject 创建项目
  4.  
  5. genspider: scrapy genspider [-t template] <name> <domain>生成爬虫,-l 查看模板; -t 指定模板,name爬虫名,domain域名
  6.  
  7. settings 查看设置
  8.  
  9. runspider 运行爬虫(运行一个独立的python文件,不必创建项目)
  10.  
  11. shell scrapy shell [url]进入交互式命令行,可以方便调试
  12.  
  13. spider=SPIDER 忽略爬虫自动检测,强制使用指定的爬虫
  14.  
  15. -c 评估代码,打印结果并退出:
  16.  
  17. $ scrapy shell --nolog http://www.example.com/ -c '(response.status, response.url)'
  18. (200, 'http://www.example.com/')
  19. 1
  20. 2
  21. no-redirect 拒绝重定向
  22.  
  23. nolog 不打印日志
  24.  
  25. response.status 查看响应码
  26.  
  27. response.url
  28.  
  29. response.text; response.body 响应文本;响应二进制
  30.  
  31. view(response) 打开下载到本地的页面,方便分析页面(比如非静态元素)
  32.  
  33.  
  34. fetch 查看爬虫是如何获取页面的,常见选项如下:
  35.  
  36. spider=SPIDER 忽略爬虫自动检测,强制使用指定的爬虫
  37. headers 查看响应头信息
  38. no-redirect 拒绝重定向
  39. view 同交互式命令中的view
  40.  
  41. version
  42.  
  43. 项目命令
  44.  
  45. crawl : scrapy crawl <spider> 指定爬虫开始爬取(确保配置文件中ROBOTSTXT_OBEY = False
  46. check: scrapy check [-l] <spider>检查语法错误
  47. list 爬虫list
  48. edit 命令行模式编辑爬虫(没啥用)
  49. parse: scrapy parse <url> [options] 爬取并用指定的回掉函数解析(可以验证我们的回调函数是否正确)
  50. callback 或者 -c 指定回调函数
  51. bench 测试爬虫性能

命令

项目结构以及爬虫应用介绍

  1. project_name/
  2. scrapy.cfg
  3. project_name/
  4. __init__.py
  5. items.py # 定义Item,类似于Django的Model
  6. pipelines.py # 定义持久化类
  7. settings.py # settings
  8. spiders/ # 所有自定义的爬虫存放文件夹
  9. __init__.py
  10. 爬虫1.py
  11. 爬虫2.py
  12. 爬虫3.py
  13.  
  14. 文件说明:
  15.  
  16. scrapy.cfg 项目的主配置信息。(真正爬虫相关的配置信息在settings.py文件中)
  17. items.py 设置数据存储模板,用于结构化数据,如:DjangoModel
  18. pipelines 数据处理行为,如:一般结构化的数据持久化
  19. settings.py 配置文件,如:递归的层数、并发数,延迟下载等
  20. spiders 爬虫目录,如:创建文件,编写爬虫规则

项目结构 及 说明

样例:

  1. import scrapy
  2.  
  3. class XiaoHuarSpider(scrapy.spiders.Spider):
  4. name = "xiaohuar" # 爬虫名称 *****
  5. allowed_domains = ["xiaohuar.com"] # 允许的域名
  6. start_urls = [
  7. "http://www.xiaohuar.com/hua/", # 其实URL
  8. ]
  9.  
  10. def parse(self, response):
  11. # 访问起始URL并获取结果后的回调函数

爬虫1.py

  1. import sys,os
  2. import io
  3. sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')

windows编码

简单使用示例

  1. import scrapy
  2. from scrapy.selector import HtmlXPathSelector
  3. from scrapy.http.request import Request
  4.  
  5. class DigSpider(scrapy.Spider):
  6. # 爬虫应用的名称,通过此名称启动爬虫命令
  7. name = "dig"
  8.  
  9. # 允许的域名
  10. allowed_domains = ["chouti.com"]
  11.  
  12. # 起始URL
  13. start_urls = [
  14. 'http://dig.chouti.com/',
  15. ]
  16.  
  17. has_request_set = {}
  18.  
  19. def parse(self, response):
  20. print(response.url)
  21.  
  22. hxs = HtmlXPathSelector(response)
  23. page_list = hxs.select('//div[@id="dig_lcpage"]//a[re:test(@href, "/all/hot/recent/\d+")]/@href').extract()
  24. for page in page_list:
  25. page_url = 'http://dig.chouti.com%s' % page
  26. key = self.md5(page_url)
  27. if key in self.has_request_set:
  28. pass
  29. else:
  30. self.has_request_set[key] = page_url
  31. obj = Request(url=page_url, method='GET', callback=self.parse)
  32. yield obj
  33.  
  34. @staticmethod
  35. def md5(val):
  36. import hashlib
  37. ha = hashlib.md5()
  38. ha.update(bytes(val, encoding='utf-8'))
  39. key = ha.hexdigest()
  40. return key

爬虫1.py

执行此爬虫文件,则在终端进入项目目录执行如下命令:

1
scrapy crawl dig --nolog

对于上述代码重要之处在于:

  • Request是一个封装用户请求的类,在回调函数中yield该对象表示继续访问
  • HtmlXpathSelector用于结构化HTML代码并提供选择器功能

选择器

  1. nodeName 选取此节点的所有节点
  2. / 从根节点选取
  3. // 从匹配选择的当前节点选择文档中的节点,不考虑它们的位置
  4. . 选择当前节点
  5. .. 选取当前节点的父节点
  6. @ 选取属性
  7. * 匹配任何元素节点
  8. @* 匹配任何属性节点
  9. Node() 匹配任何类型的节点

常用的路径表达式

  1. .class .color 选择class=”color”的所有元素
  2. #id #info 选择id=”info”的所有元素
  3. * * 选择所有元素
  4. element p 选择所有的p元素
  5. element,element div,p 选择所有div元素和所有p元素
  6. element element div p 选择div标签内部的所有p元素
  7. [attribute] [target] 选择带有targe属性的所有元素
  8. [arrtibute=value] [target=_blank] 选择target=”_blank”的所有元素

CSS选择器

  1. contains a[contains(@href, "link")] 属性href中包含linka标签
  2.  
  3. starts-with a[starts-with(@href, "link") 属性href中以link开头的a标签
  4.  
  5. retest a[re:test(@id, "i\d+") 属性id中格式是i\d+的a标签
  6.  
  7. 。。。

css高级用法

  1. 不带extr。。。的 # 结果为obj
  2. extract() # 提取为list
  3. extract_first() # 提取第一个

提取

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. from scrapy.selector import Selector, HtmlXPathSelector
  4. from scrapy.http import HtmlResponse
  5. html = """<!DOCTYPE html>
  6. <html>
  7. <head lang="en">
  8. <meta charset="UTF-8">
  9. <title></title>
  10. </head>
  11. <body>
  12. <ul>
  13. <li class="item-"><a id='i1' href="link.html">first item</a></li>
  14. <li class="item-0"><a id='i2' href="llink.html">first item</a></li>
  15. <li class="item-1"><a href="llink2.html">second item<span>vv</span></a></li>
  16. </ul>
  17. <div><a href="llink2.html">second item</a></div>
  18. </body>
  19. </html>
  20. """
  21. response = HtmlResponse(url='http://example.com', body=html,encoding='utf-8')
  22. # hxs = HtmlXPathSelector(response)
  23. # print(hxs)
  24. # hxs = Selector(response=response).xpath('//a') # 查找整个html中所有a标签
  25. # print(hxs)
  26. # hxs = Selector(response=response).xpath('//a[2]') # 查找整个html中所有a标签的第二个元素
  27. # print(hxs)
  28. # hxs = Selector(response=response).xpath('//a[@id]') # 查找整个html中具有id的所有a标签
  29. # print(hxs)
  30. # hxs = Selector(response=response).xpath('//a[@id="i1"]') # 查找整个html中id为i1的a标签
  31. # print(hxs)
  32. # hxs = Selector(response=response).xpath('//a[@href="link.html"][@id="i1"]') # 查找整个html中href为link.html以及id为i1的a标签
  33. # print(hxs)
  34. # hxs = Selector(response=response).xpath('//a[contains(@href, "link")]') # 查找整个html中href包含link字段的所有a标签
  35. # print(hxs)
  36. # hxs = Selector(response=response).xpath('//a[starts-with(@href, "link")]') # 查找整个html中href以link开头的所有a标签
  37. # print(hxs)
  38. # hxs = Selector(response=response).xpath('//a[re:test(@id, "i\d+")]') # 查找整个html中id格式为i\d+的所有a标签
  39. # print(hxs)
  40. # hxs = Selector(response=response).xpath('//a[re:test(@id, "i\d+")]/text()').extract() # 查找整个html中id格式为i\d+的所有a标签的text值,并提出为string的列表
  41. # print(hxs)
  42. # hxs = Selector(response=response).xpath('//a[re:test(@id, "i\d+")]/@href').extract() # 查找整个html中id格式为i\d+的所有a标签的href值,并提出为string的列表
  43. # print(hxs)
  44. # hxs = Selector(response=response).xpath('/html/body/ul/li/a/@href').extract() # 查找response里,路径为/html/body/ul/li/a的href值,并提出为string的列表
  45. # print(hxs)
  46. # hxs = Selector(response=response).xpath('//body/ul/li/a/@href').extract_first()查找response的后代里,路径为body/ul/li/a的href值,并提出第一个值
  47. # print(hxs)
  48.  
  49. # ul_list = Selector(response=response).xpath('//body/ul/li')
  50. # for item in ul_list:
  51. # v = item.xpath('./a/span') # 相对路径
  52. # # 或
  53. # # v = item.xpath('a/span')
  54. # # 或
  55. # # v = item.xpath('*/a/span')
  56. # print(v)

示例

  1. # 找div,class=part2的标签,获取share-linkid属性
  2. hxs = Selector(response)
  3. linkid_list = hxs.xpath("//div[@class='part2']/@share-linkid").extract()
  4. # print(linkid_list)

之前做的真实示例

补充:

  1. '''
  2. In [326]: text="""
  3. ...: <div>
  4. ...: <a>1a</a>
  5. ...: <p>2p</p>
  6. ...: <p>3p</p>
  7. ...: </div>"""
  8. '''
  9.  
  10. # css写法
  11. #完整子节点列表,从第一个子节点开始计数,并且满足子节点tag限定
  12. In [332]: sel.css(‘a:nth-child(1)‘).extract()
  13. Out[332]: [‘<a>1a</a>‘]
  14. #完整子节点列表,从最后一个子节点开始计数,并且满足子节点tag限定
  15. In [333]: sel.css(‘a:nth-last-child(1)‘).extract()
  16. Out[333]: []
  17.  
  18. In [340]: sel.css(‘a:first-child‘).extract()
  19. Out[340]: [‘<a>1a</a>‘]
  20.  
  21. In [341]: sel.css(‘a:last-child‘).extract()
  22. Out[341]: []
  23. # 上述 -child 修改为 -of-type ,仅对 过滤后的相应子节点列表 进行计数
  24. # 这句话待验证
  25.  
  26. # xpath写法
  27. In [345]: sel.xpath(‘//div/*‘).extract()
  28. Out[345]: [‘<a>1a</a>‘, ‘<p>2p</p>‘, ‘<p>3p</p>‘]
  29.  
  30. In [346]: sel.xpath(‘//div/node()‘).extract()
  31. Out[346]: [‘\n ‘, ‘<a>1a</a>‘, ‘\n ‘, ‘<p>2p</p>‘, \n ‘, ‘<p>3p</p>‘, \n‘]
  32. In [356]: sel.xpath(‘//div/node()[1]‘).extract() #包括纯文本
  33. Out[356]: [‘\n ‘]
  34.  
  35. In [352]: sel.xpath(‘//div/p[last()]‘).extract()
  36. Out[352]: [‘<p>3p</p>‘]
  37.  
  38. In [353]: sel.xpath(‘//div/p[last()-1]‘).extract()
  39. Out[353]: [‘<p>2p</p>‘]

第几个子节点 css和xpath写法

  1. 排除一个属性的节点可以使用//tbody/tr[not(@class)]来写
  2. 排除一个或者两个属性可以使用//tbody/tr[not(@class or @id)]来选择。
  3. 排查某属性值可使用//tbody/tr[not(@class='xxx')]

not

  1. 一、节点的前后节点:
  2.  
  3. 当前节点的祖先节点:
  4. //*[title=""]/ancestor::*
  5.  
  6. 当前节点的父节点:
  7. //*[title=""]/parent::*
  8. //*[title=""]/..
  9.  
  10. 当前节点的开始标签之前的所有节点 /preceding
  11.  
  12. 当前节点的结束标签之后的所有节点 /following
  13.  
  14. 当前节点之后的兄弟节点 /following-sibling::*
  15.  
  16. 当前节点之前的兄弟节点 /preceding-sibling::*
  17.  
  18. 当前节点的所有后代(子,孙) /descendant
  19.  
  20. 包含指定文本:span[contains(text(), "指定文本内容")]
  21.  
  22. 取最后一个子元素 //div[@class='box-nav']/a[last()]
  23.  
  24. 二、选取包含指定文本的标签前面的某个兄弟节点
  25.  
  26. html代码如下:
  27.  
  28. <div class="tittle_x F_Left">
  29.  
  30.         <a href="http://www.ccidnet.com/">首页</a>
  31.         <em>&gt;</em>
  32.         <a href="http://www.ccidnet.com/news/">新闻</a>
  33.         <em>&gt;</em>
  34.         <a href="http://www.ccidnet.com/news/focus/">焦点直击</a>
  35.          <em>&gt;</em>
  36.         <a href="#">正文 </a>
  37. </div>
  38.  
  39. 如上图与代码,我想选择“正文”前面的“焦点直击”为类型,那么可以这样写:
  40.  
  41. 类型: //div[@class='tittle_x F_Left']/a[contains(text(),'正文')]/preceding-sibling::a[1]
  42.  
  43. 三、选取指定节点之前的不带标签的文本
  44.  
  45. 例如:选class="bb"前面的文本:“这是文本。”
  46.             <span>
  47.                 <span class="aa">文本</span>                
  48.                 这是文本。
  49.                 <a class="bb">文本</a>
  50.             </span>
  51.  
  52. 可以这样写: //span[@class='aa'][2]/following::text()[1]
  53. ---------------------
  54. 作者:那个南墙
  55. 来源:CSDN
  56. 原文:https://blog.csdn.net/baidu_38414830/article/details/70325232
  57. 版权声明:本文为博主原创文章,转载请附上博文链接!

关系选择 即祖先父母后代兄弟

  1. 获取某标签的多个子标签的text
  2. xpath("string(.)")
  3.  
  4. response.xpath("//div[@class='tpc_content do_not_catch']")[0].xpath("string(.)").extract_first()
  5.  
  6. # 这个是获取子标签text的列表
  7. response.xpath("//div[@class='tpc_content do_not_catch']")[0].xpath("text()").extract()
  8.  
  9. 获取单个标签的text
  10. css("::text")
  11. xpath("xxxx/text()")

获取多个子标签的text

  1. 1.CSS写法
  2.  
  3. 1.1 获取属性值:
  4. 标签名::attr(属性名)
  5.  
  6. 例:response.css('base::attr(href)')
  7. 1.2 获取元素内容
  8. 标签名::text
  9.  
  10. 例:response.css('title::text')
  11. 1.3遇到有相同的标签时,需要在[ ]中加限定内容:
  12. 标签名[]::attr(属性名)
  13.  
  14. 例:response.css('a[href*=image]::attr(href)')
  15. 2.XPath方法
  16.  
  17. 2.1 获取属性值:
  18. //标签名/@属性名
  19.  
  20. 例:response.xpath('//base/@href')
  21. 2.2 获取元素内容:
  22. //标签名/text()
  23.  
  24. 例:response.xpath('//title/text()')
  25. 2.3 遇到有相同的标签时,需要在[ ]中加限定内容:
  26.  
  27. //标签名[contains(@属性名,"标签名")]/@属性名
  28.  
  29. 例:response.xpath('//div[@id="images"]/a/text()')
  30. 注意:这里id对应的属性值必须用双引号,在scrapyshell命令模式中,单引号一直报语法错误

补充

  1. xpath中没有提供对class的原生查找方法。但是 stackoverflow 看到了一个很有才的回答:
  2.  
  3. This selector should work but will be more efficient if you replace it with your suited markup:
  4. 这个表达式应该是可行的。不过如果你把class换成更好识别的标识执行效率会更高
  5.  
  6. //*[contains(@class, 'Test')]
  7.  
  8. But since this will also match cases like class="Testvalue" or class="newTest".
  9.  
  10. 但是这个表达式会把类似 class="Testvalue" 或者 class="newTest"也匹配出来。
  11.  
  12. //*[contains(concat(' ', @class, ' '), ' Test ')]
  13.  
  14. If you wished to be really certain that it will match correctly, you could also use the normalize-space function to clean up stray whitespace characters around the class name (as mentioned by @Terry)
  15.  
  16. 如果您希望确定它能够正确匹配,则还可以使用 normalize-space 函数清除类名周围的空白字符(如@Terry所述)
  17.  
  18. //*[contains(concat(' ', normalize-space(@class), ' '), ' Test ')]
  19.  
  20. Note that in all these versions, the * should best be replaced by whatever element name you actually wish to match, unless you wish to search each and every element in the document for the given condition.
  21.  
  22. 请注意在所有这些版本里,除非你想要在所有元素里搜索带有这些条件的元素,否则你最好把*号替换成你想要匹配的具体的元素名(标签名)。

一些函数

数据格式化、持久化

爬取的数据可在parse中直接处理。也可以使用Item进行格式化,交给pipelines进行持久化处理。

  1. import scrapy
  2. from scrapy.selector import HtmlXPathSelector
  3. from scrapy.http.request import Request
  4. from scrapy.http.cookies import CookieJar
  5. from scrapy import FormRequest
  6.  
  7. class XiaoHuarSpider(scrapy.Spider):
  8. # 爬虫应用的名称,通过此名称启动爬虫命令
  9. name = "xiaohuar"
  10. # 允许的域名
  11. allowed_domains = ["xiaohuar.com"]
  12.  
  13. start_urls = [
  14. "http://www.xiaohuar.com/list-1-1.html",
  15. ]
  16. # custom_settings = {
  17. # 'ITEM_PIPELINES':{
  18. # 'spider1.pipelines.JsonPipeline': 100
  19. # }
  20. # }
  21. has_request_set = {}
  22.  
  23. def parse(self, response):
  24. # 分析页面
  25. # 找到页面中符合规则的内容(校花图片),保存
  26. # 找到所有的a标签,再访问其他a标签,一层一层的搞下去
  27.  
  28. hxs = HtmlXPathSelector(response)
  29.  
  30. items = hxs.select('//div[@class="item_list infinite_scroll"]/div')
  31. for item in items:
  32. src = item.select('.//div[@class="img"]/a/img/@src').extract_first()
  33. name = item.select('.//div[@class="img"]/span/text()').extract_first()
  34. school = item.select('.//div[@class="img"]/div[@class="btns"]/a/text()').extract_first()
  35. url = "http://www.xiaohuar.com%s" % src
  36. from ..items import XiaoHuarItem
  37. obj = XiaoHuarItem(name=name, school=school, url=url)
  38. yield obj
  39.  
  40. urls = hxs.select('//a[re:test(@href, "http://www.xiaohuar.com/list-1-\d+.html")]/@href')
  41. for url in urls:
  42. key = self.md5(url)
  43. if key in self.has_request_set:
  44. pass
  45. else:
  46. self.has_request_set[key] = url
  47. req = Request(url=url,method='GET',callback=self.parse)
  48. yield req
  49.  
  50. @staticmethod
  51. def md5(val):
  52. import hashlib
  53. ha = hashlib.md5()
  54. ha.update(bytes(val, encoding='utf-8'))
  55. key = ha.hexdigest()
  56. return key

spiders/xiahuar.py

  1. import scrapy
  2.  
  3. class XiaoHuarItem(scrapy.Item):
  4. name = scrapy.Field()
  5. school = scrapy.Field()
  6. url = scrapy.Field()

items.py

  1. import json
  2. import os
  3. import requests
  4.  
  5. class JsonPipeline(object):
  6. def __init__(self):
  7. self.file = open('xiaohua.txt', 'w')
  8.  
  9. def process_item(self, item, spider):
  10. v = json.dumps(dict(item), ensure_ascii=False)
  11. self.file.write(v)
  12. self.file.write('\n')
  13. self.file.flush()
  14. return item
  15.  
  16. class FilePipeline(object):
  17. def __init__(self):
  18. if not os.path.exists('imgs'):
  19. os.makedirs('imgs')
  20.  
  21. def process_item(self, item, spider):
  22. response = requests.get(item['url'], stream=True)
  23. file_name = '%s_%s.jpg' % (item['name'], item['school'])
  24. with open(os.path.join('imgs', file_name), mode='wb') as f:
  25. f.write(response.content)
  26. return item

pipelines.py

  1. ITEM_PIPELINES = {
  2. 'spider1.pipelines.JsonPipeline': 100,
  3. 'spider1.pipelines.FilePipeline': 300,
  4. }
  5. # 每行后面的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内。

settings.py

对于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. # 表示将item丢弃,不会被后续pipeline处理
  14. # raise DropItem()
  15.  
  16. @classmethod
  17. def from_crawler(cls, crawler):
  18. """
  19. 初始化时候,用于创建pipeline对象
  20. :param crawler:
  21. :return:
  22. """
  23. val = crawler.settings.getint('MMMM')
  24. return cls(val)
  25.  
  26. def open_spider(self,spider):
  27. """
  28. 爬虫开始执行时,调用
  29. :param spider:
  30. :return:
  31. """
  32. print('')
  33.  
  34. def close_spider(self,spider):
  35. """
  36. 爬虫关闭时,被调用
  37. :param spider:
  38. :return:
  39. """
  40. print('')

自定义pipeline

中间件

  1. class SpiderMiddleware(object):
  2.  
  3. def process_spider_input(self,response, spider):
  4. """
  5. 下载完成,执行,然后交给parse处理
  6. :param response:
  7. :param spider:
  8. :return:
  9. """
  10. pass
  11.  
  12. def process_spider_output(self,response, result, spider):
  13. """
  14. spider处理完成,返回时调用
  15. :param response:
  16. :param result:
  17. :param spider:
  18. :return: 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)
  19. """
  20. return result
  21.  
  22. def process_spider_exception(self,response, exception, spider):
  23. """
  24. 异常调用
  25. :param response:
  26. :param exception:
  27. :param spider:
  28. :return: None,继续交给后续中间件处理异常;含 Response 或 Item 的可迭代对象(iterable),交给调度器或pipeline
  29. """
  30. return None
  31.  
  32. def process_start_requests(self,start_requests, spider):
  33. """
  34. 爬虫启动时调用
  35. :param start_requests:
  36. :param spider:
  37. :return: 包含 Request 对象的可迭代对象
  38. """
  39. return start_requests

爬虫中间件

  1. class DownMiddleware1(object):
  2. def process_request(self, request, spider):
  3. """
  4. 请求需要被下载时,经过所有下载器中间件的process_request调用
  5. :param request:
  6. :param spider:
  7. :return:
  8. None,继续后续中间件去下载;
  9. Response对象,停止process_request的执行,开始执行process_response
  10. Request对象,停止中间件的执行,将Request重新调度器
  11. raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
  12. """
  13. pass
  14.  
  15. def process_response(self, request, response, spider):
  16. """
  17. spider处理完成,返回时调用
  18. :param response:
  19. :param result:
  20. :param spider:
  21. :return:
  22. Response 对象:转交给其他中间件process_response
  23. Request 对象:停止中间件,request会被重新调度下载
  24. raise IgnoreRequest 异常:调用Request.errback
  25. """
  26. print('response1')
  27. return response
  28.  
  29. def process_exception(self, request, exception, spider):
  30. """
  31. 当下载处理器(download handler)或 process_request() (下载中间件)抛出异常
  32. :param response:
  33. :param exception:
  34. :param spider:
  35. :return:
  36. None:继续交给后续中间件处理异常;
  37. Response对象:停止后续process_exception方法
  38. Request对象:停止中间件,request将会被重新调用下载
  39. """
  40. return None

下载器中间件

  1. 首先需要明确:
  2.  
  3. 请求是引擎发出来的,不是爬虫发出来的
  4. 引擎从爬虫拿url,给调度器去重,同时会从调度器的任务队列里取出一个任务,给下载器
  5. 下载器下载完以后,下载器把response返回给引擎
  6. 先说process_request(request, spider)
  7.  
  8. 当设置了很多中间件的时候,会按照setting里的设置,按照从小到大执行
  9. 假如有两个中间件的等级一样,这两个中间都会被执行。(执行顺序没有得出有效结论)
  10. 即使某个中间件的设置时错的,比如,故意在代理中间件里给一个错误的ip,依然不会中断中间件的执行,也就是,scrapy无法检测代理中的操作是否合法。
  11. 经过中间件故意的错误的加代理,下载器仍然去执行这个任务了,只不过根据另一个中间件:RetryMiddleware 的设定去处理了这个请求(默认的是,请求连续失败三次退出任务)
  12. 当这个请求第一次失败时候,依然会再次经过设置的中间件。
  13. 第一个发出error信号的不是引擎,是scraper,它是连接引擎、爬虫、下载器的一个东西。。。。然后引擎才发出错误信号
  14. (重点)每一个任务,也就是每一个请求,不管在什么情况下,只要设置了中间件,就会孜孜不倦的去通过这些中间件,然后到达下载器
  15. 然后说process_response(request, response, spider)
  16.  
  17. 因为获取的响应是从下载器到引擎的,所以response经过中间件的顺序刚好与request相反
  18. 是从大到小执行的
  19. ---------------------
  20. 作者:fiery_heart
  21. 来源:CSDN
  22. 原文:https://blog.csdn.net/fiery_heart/article/details/82229871
  23. 版权声明:本文为博主原创文章,转载请附上博文链接!

下载中间件的总结(转)

  1. class MyMiddleware(object):
  2. # Not all methods need to be defined. If a method is not defined,
  3. # scrapy acts as if the spider middleware does not modify the
  4. # passed objects.
  5.  
  6. @classmethod
  7. def from_crawler(cls, crawler):
  8. # This method is used by Scrapy to create your spiders.
  9. s = cls()
  10. crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
  11. crawler.signals.connect(s.item_scraped, signal=signals.item_scraped)
  12. crawler.signals.connect(s.spider_closed, signal=signals.spider_closed)
  13. crawler.signals.connect(s.spider_error, signal=signals.spider_error)
  14. crawler.signals.connect(s.spider_idle, signal=signals.spider_idle)
  15.  
  16. return s
  17.  
  18. # 当spider开始爬取时发送该信号。该信号一般用来分配spider的资源,不过其也能做任何事。
  19. def spider_opened(self, spider):
  20. spider.logger.info('pa chong kai shi le: %s' % spider.name)
  21. print('start','')
  22.  
  23. def item_scraped(self,item, response, spider):
  24. global hahaha
  25. hahaha += 1
  26. # 当某个spider被关闭时,该信号被发送。该信号可以用来释放每个spider在 spider_opened 时占用的资源。
  27.  
  28. def spider_closed(self,spider, reason):
  29. print('-------------------------------all over------------------------------------------')
  30. global hahaha
  31. print(spider.name,' closed')
  32.  
  33. # 当spider的回调函数产生错误时(例如,抛出异常),该信号被发送。
  34. def spider_error(self,failure, response, spider):
  35. code = response.status
  36. print('spider error')
  37.  
  38. # 当spider进入空闲(idle)状态时该信号被发送。空闲意味着:
  39. # requests正在等待被下载
  40. # requests被调度
  41. # items正在item pipeline中被处理
  42. def spider_idle(self,spider):
  43. for i in range(10):
  44. print(spider.name)
  45.  
  46. '''
  47. start 1
  48. jiandan
  49. jiandan
  50. jiandan
  51. jiandan
  52. jiandan
  53. jiandan
  54. jiandan
  55. jiandan
  56. jiandan
  57. jiandan
  58. -------------------------------all over------------------------------------------
  59. jiandan closed
  60. '''

信号在中间件的使用

自定义命令

  • 在spiders同级创建任意目录,如:commands
  • 在其中创建 crawlall.py 文件 (此处文件名就是自定义的命令)
  1. from scrapy.commands import ScrapyCommand
  2. from scrapy.utils.project import get_project_settings
  3.  
  4. class Command(ScrapyCommand):
  5.  
  6. requires_project = True
  7.  
  8. def syntax(self):
  9. return '[options]'
  10.  
  11. def short_desc(self):
  12. return 'Runs all of the spiders'
  13.  
  14. def run(self, args, opts):
  15. spider_list = self.crawler_process.spiders.list()
  16. for name in spider_list:
  17. self.crawler_process.crawl(name, **opts.__dict__)
  18. self.crawler_process.start()

crawlall.py

  • 在settings.py 中添加配置 COMMANDS_MODULE = '项目名称.目录名称'
  • 在项目目录执行命令:scrapy crawlall

自定义扩展(涉及信号)

自定义扩展时,利用信号在指定位置注册制定操作

  1. 信号:
  2. engine_started = object()
  3. engine_stopped = object()
  4. spider_opened = object()
  5. spider_idle = object()
  6. spider_closed = object()
  7. spider_error = object()
  8. request_scheduled = object()
  9. request_dropped = object()
  10. response_received = object()
  11. response_downloaded = object()
  12. item_scraped = object()
  13. item_dropped = object()
  14.  
  15. 用法
  16. crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened) # 当开始spider时,执行本类中用户自定义的spider_opened方法

信号的类别 和 用法

  1. from scrapy import signals
  2.  
  3. class MyExtension(object):
  4. def __init__(self, value):
  5. self.value = value
  6.  
  7. @classmethod
  8. def from_crawler(cls, crawler):
  9. val = crawler.settings.getint('MMMM')
  10. ext = cls(val)
  11.  
  12. crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
  13. crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)
  14.  
  15. return ext
  16.  
  17. def spider_opened(self, spider):
  18. print('open')
  19.  
  20. def spider_closed(self, spider):
  21. print('close')

自定义扩展

信号在扩展的使用

跟中间件类似,先自己创建一个py文件(名字自定义)放在项目目录里,再在settings文件中添加extension。

  1. # -*- coding:utf-8 -*-
  2. from scrapy import signals
  3.  
  4. class MyExtension(object):
  5. def __init__(self,**kwargs):
  6. self.__dict__.update(kwargs)
  7.  
  8. @classmethod
  9. def from_crawler(cls, crawler):
  10. ext = cls(a=1,b=2,x=11,y=12)
  11. crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
  12. crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)
  13.  
  14. return ext
  15.  
  16. def spider_opened(self, spider):
  17. print('open')
  18. print(self.a)
  19. print(self.b)
  20.  
  21. def spider_closed(self, spider):
  22. print('close')
  23. print(self.x)
  24. print(self.y)

项目名称/extension.py

  1. EXTENSIONS = {
  2. # 'scrapy.extensions.telnet.TelnetConsole': None,
  3. "cl.extensions.MyExtension":200,
  4. }

settings.py

  1. open
  2. 1
  3. 2
  4. close
  5. 11
  6. 12

注1:信号可在中间件使用,见中间件部分。

注2:信号可配合数据采集器,见后面笔记。

避免重复访问(去重)

  1. scrapy默认使用 scrapy.dupefilter.RFPDupeFilter 进行去重,相关去重配置有:
  2.  
  3. DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter'
  4. DUPEFILTER_DEBUG = False
  5. JOBDIR = "保存范文记录的日志路径,如:/root/" # 最终路径为 /root/requests.seen

Scrapy中默认配置

  1. class RepeatUrl:
  2. def __init__(self):
  3. self.visited_url = set()
  4.  
  5. @classmethod
  6. def from_settings(cls, settings):
  7. """
  8. 初始化时,调用
  9. :param settings:
  10. :return:
  11. """
  12. return cls()
  13.  
  14. def request_seen(self, request):
  15. """
  16. 检测当前请求是否已经被访问过
  17. :param request:
  18. :return: True表示已经访问过;False表示未访问过
  19. """
  20. if request.url in self.visited_url:
  21. return True
  22. self.visited_url.add(request.url)
  23. return False
  24.  
  25. def open(self):
  26. """
  27. 开始爬去请求时,调用
  28. :return:
  29. """
  30. print('open replication')
  31.  
  32. def close(self, reason):
  33. """
  34. 结束爬虫爬取时,调用
  35. :param reason:
  36. :return:
  37. """
  38. print('close replication')
  39.  
  40. def log(self, request, spider):
  41. """
  42. 记录日志
  43. :param request:
  44. :param spider:
  45. :return:
  46. """
  47. print('repeat', request.url)

自定义URL去重操作

settings说明

http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/settings.html

  1. # -*- coding: utf-8 -*-
  2.  
  3. # Scrapy settings for step8_king project
  4. #
  5. # For simplicity, this file contains only settings considered important or
  6. # commonly used. You can find more settings consulting the documentation:
  7. #
  8. # http://doc.scrapy.org/en/latest/topics/settings.html
  9. # http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
  10. # http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
  11.  
  12. # 1. 爬虫名称
  13. BOT_NAME = 'step8_king'
  14.  
  15. # 2. 爬虫应用路径
  16. SPIDER_MODULES = ['step8_king.spiders']
  17. NEWSPIDER_MODULE = 'step8_king.spiders'
  18.  
  19. # Crawl responsibly by identifying yourself (and your website) on the user-agent
  20. # 3. 客户端 user-agent请求头
  21. # USER_AGENT = 'step8_king (+http://www.yourdomain.com)'
  22.  
  23. # Obey robots.txt rules
  24. # 4. 禁止爬虫配置
  25. # ROBOTSTXT_OBEY = False
  26.  
  27. # Configure maximum concurrent requests performed by Scrapy (default: 16)
  28. # 5. 并发请求数
  29. # CONCURRENT_REQUESTS = 4
  30.  
  31. # Configure a delay for requests for the same website (default: 0)
  32. # See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
  33. # See also autothrottle settings and docs
  34. # 6. 延迟下载秒数
  35. # DOWNLOAD_DELAY = 2
  36.  
  37. # The download delay setting will honor only one of:
  38. # 7. 单域名访问并发数,并且延迟下次秒数也应用在每个域名
  39. # CONCURRENT_REQUESTS_PER_DOMAIN = 2
  40. # 单IP访问并发数,如果有值则忽略:CONCURRENT_REQUESTS_PER_DOMAIN,并且延迟下次秒数也应用在每个IP
  41. # CONCURRENT_REQUESTS_PER_IP = 3
  42.  
  43. # Disable cookies (enabled by default)
  44. # 8. 是否支持cookie,cookiejar进行操作cookie
  45. # COOKIES_ENABLED = True
  46. # COOKIES_DEBUG = True
  47.  
  48. # Disable Telnet Console (enabled by default)
  49. # 9. Telnet用于查看当前爬虫的信息,操作爬虫等...
  50. # 使用telnet ip port ,然后通过命令操作
  51. # TELNETCONSOLE_ENABLED = True
  52. # TELNETCONSOLE_HOST = '127.0.0.1'
  53. # TELNETCONSOLE_PORT = [6023,]
  54.  
  55. # 10. 默认请求头
  56. # Override the default request headers:
  57. # DEFAULT_REQUEST_HEADERS = {
  58. # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  59. # 'Accept-Language': 'en',
  60. # }
  61.  
  62. # Configure item pipelines
  63. # See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
  64. # 11. 定义pipeline处理请求
  65. # ITEM_PIPELINES = {
  66. # 'step8_king.pipelines.JsonPipeline': 700,
  67. # 'step8_king.pipelines.FilePipeline': 500,
  68. # }
  69.  
  70. # 12. 自定义扩展,基于信号进行调用
  71. # Enable or disable extensions
  72. # See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
  73. # EXTENSIONS = {
  74. # # 'step8_king.extensions.MyExtension': 500,
  75. # }
  76.  
  77. # 13. 爬虫允许的最大深度,可以通过meta查看当前深度;0表示无深度
  78. # DEPTH_LIMIT = 3
  79.  
  80. # 14. 爬取时,0表示深度优先Lifo(默认);1表示广度优先FiFo
  81.  
  82. # 后进先出,深度优先
  83. # DEPTH_PRIORITY = 0
  84. # SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleLifoDiskQueue'
  85. # SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.LifoMemoryQueue'
  86. # 先进先出,广度优先
  87.  
  88. # DEPTH_PRIORITY = 1
  89. # SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
  90. # SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'
  91.  
  92. # 15. 调度器队列
  93. # SCHEDULER = 'scrapy.core.scheduler.Scheduler'
  94. # from scrapy.core.scheduler import Scheduler
  95.  
  96. # 16. 访问URL去重
  97. # DUPEFILTER_CLASS = 'step8_king.duplication.RepeatUrl'
  98.  
  99. # Enable and configure the AutoThrottle extension (disabled by default)
  100. # See http://doc.scrapy.org/en/latest/topics/autothrottle.html
  101.  
  102. """
  103. 17. 自动限速算法
  104. from scrapy.contrib.throttle import AutoThrottle
  105. 自动限速设置
  106. 1. 获取最小延迟 DOWNLOAD_DELAY
  107. 2. 获取最大延迟 AUTOTHROTTLE_MAX_DELAY
  108. 3. 设置初始下载延迟 AUTOTHROTTLE_START_DELAY
  109. 4. 当请求下载完成后,获取其"连接"时间 latency,即:请求连接到接受到响应头之间的时间
  110. 5. 用于计算的... AUTOTHROTTLE_TARGET_CONCURRENCY
  111. target_delay = latency / self.target_concurrency
  112. new_delay = (slot.delay + target_delay) / 2.0 # 表示上一次的延迟时间
  113. new_delay = max(target_delay, new_delay)
  114. new_delay = min(max(self.mindelay, new_delay), self.maxdelay)
  115. slot.delay = new_delay
  116. """
  117.  
  118. # 开始自动限速
  119. # AUTOTHROTTLE_ENABLED = True
  120. # The initial download delay
  121. # 初始下载延迟
  122. # AUTOTHROTTLE_START_DELAY = 5
  123. # The maximum download delay to be set in case of high latencies
  124. # 最大下载延迟
  125. # AUTOTHROTTLE_MAX_DELAY = 10
  126. # The average number of requests Scrapy should be sending in parallel to each remote server
  127. # 平均每秒并发数
  128. # AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
  129.  
  130. # Enable showing throttling stats for every response received:
  131. # 是否显示
  132. # AUTOTHROTTLE_DEBUG = True
  133.  
  134. # Enable and configure HTTP caching (disabled by default)
  135. # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
  136.  
  137. """
  138. 18. 启用缓存
  139. 目的用于将已经发送的请求或相应缓存下来,以便以后使用
  140.  
  141. from scrapy.downloadermiddlewares.httpcache import HttpCacheMiddleware
  142. from scrapy.extensions.httpcache import DummyPolicy
  143. from scrapy.extensions.httpcache import FilesystemCacheStorage
  144. """
  145. # 是否启用缓存策略
  146. # HTTPCACHE_ENABLED = True
  147.  
  148. # 缓存策略:所有请求均缓存,下次在请求直接访问原来的缓存即可
  149. # HTTPCACHE_POLICY = "scrapy.extensions.httpcache.DummyPolicy"
  150. # 缓存策略:根据Http响应头:Cache-Control、Last-Modified 等进行缓存的策略
  151. # HTTPCACHE_POLICY = "scrapy.extensions.httpcache.RFC2616Policy"
  152.  
  153. # 缓存超时时间
  154. # HTTPCACHE_EXPIRATION_SECS = 0
  155.  
  156. # 缓存保存路径
  157. # HTTPCACHE_DIR = 'httpcache'
  158.  
  159. # 缓存忽略的Http状态码
  160. # HTTPCACHE_IGNORE_HTTP_CODES = []
  161.  
  162. # 缓存存储的插件
  163. # HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
  164.  
  165. """
  166. 19. 代理,需要在环境变量中设置
  167. from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware
  168.  
  169. 方式一:使用默认
  170. os.environ
  171. {
  172. http_proxy:http://root:woshiniba@192.168.11.11:9999/
  173. https_proxy:http://192.168.11.11:9999/
  174. }
  175. 方式二:使用自定义下载中间件
  176.  
  177. def to_bytes(text, encoding=None, errors='strict'):
  178. if isinstance(text, bytes):
  179. return text
  180. if not isinstance(text, six.string_types):
  181. raise TypeError('to_bytes must receive a unicode, str or bytes '
  182. 'object, got %s' % type(text).__name__)
  183. if encoding is None:
  184. encoding = 'utf-8'
  185. return text.encode(encoding, errors)
  186.  
  187. class ProxyMiddleware(object):
  188. def process_request(self, request, spider):
  189. PROXIES = [
  190. {'ip_port': '111.11.228.75:80', 'user_pass': ''},
  191. {'ip_port': '120.198.243.22:80', 'user_pass': ''},
  192. {'ip_port': '111.8.60.9:8123', 'user_pass': ''},
  193. {'ip_port': '101.71.27.120:80', 'user_pass': ''},
  194. {'ip_port': '122.96.59.104:80', 'user_pass': ''},
  195. {'ip_port': '122.224.249.122:8088', 'user_pass': ''},
  196. ]
  197. proxy = random.choice(PROXIES)
  198. if proxy['user_pass'] is not None:
  199. request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
  200. encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass']))
  201. request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass)
  202. print "**************ProxyMiddleware have pass************" + proxy['ip_port']
  203. else:
  204. print "**************ProxyMiddleware no pass************" + proxy['ip_port']
  205. request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
  206.  
  207. DOWNLOADER_MIDDLEWARES = {
  208. 'step8_king.middlewares.ProxyMiddleware': 500,
  209. }
  210.  
  211. """
  212.  
  213. """
  214. 20. Https访问
  215. Https访问时有两种情况:
  216. 1. 要爬取网站使用的可信任证书(默认支持)
  217. DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
  218. DOWNLOADER_CLIENTCONTEXTFACTORY = "scrapy.core.downloader.contextfactory.ScrapyClientContextFactory"
  219.  
  220. 2. 要爬取网站使用的自定义证书
  221. DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
  222. DOWNLOADER_CLIENTCONTEXTFACTORY = "step8_king.https.MySSLFactory"
  223.  
  224. # https.py
  225. from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory
  226. from twisted.internet.ssl import (optionsForClientTLS, CertificateOptions, PrivateCertificate)
  227.  
  228. class MySSLFactory(ScrapyClientContextFactory):
  229. def getCertificateOptions(self):
  230. from OpenSSL import crypto
  231. v1 = crypto.load_privatekey(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.key.unsecure', mode='r').read())
  232. v2 = crypto.load_certificate(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.pem', mode='r').read())
  233. return CertificateOptions(
  234. privateKey=v1, # pKey对象
  235. certificate=v2, # X509对象
  236. verify=False,
  237. method=getattr(self, 'method', getattr(self, '_ssl_method', None))
  238. )
  239. 其他:
  240. 相关类
  241. scrapy.core.downloader.handlers.http.HttpDownloadHandler
  242. scrapy.core.downloader.webclient.ScrapyHTTPClientFactory
  243. scrapy.core.downloader.contextfactory.ScrapyClientContextFactory
  244. 相关配置
  245. DOWNLOADER_HTTPCLIENTFACTORY
  246. DOWNLOADER_CLIENTCONTEXTFACTORY
  247.  
  248. """
  249.  
  250. """
  251. 21. 爬虫中间件
  252. class SpiderMiddleware(object):
  253.  
  254. def process_spider_input(self,response, spider):
  255. '''
  256. 下载完成,执行,然后交给parse处理
  257. :param response:
  258. :param spider:
  259. :return:
  260. '''
  261. pass
  262.  
  263. def process_spider_output(self,response, result, spider):
  264. '''
  265. spider处理完成,返回时调用
  266. :param response:
  267. :param result:
  268. :param spider:
  269. :return: 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)
  270. '''
  271. return result
  272.  
  273. def process_spider_exception(self,response, exception, spider):
  274. '''
  275. 异常调用
  276. :param response:
  277. :param exception:
  278. :param spider:
  279. :return: None,继续交给后续中间件处理异常;含 Response 或 Item 的可迭代对象(iterable),交给调度器或pipeline
  280. '''
  281. return None
  282.  
  283. def process_start_requests(self,start_requests, spider):
  284. '''
  285. 爬虫启动时调用
  286. :param start_requests:
  287. :param spider:
  288. :return: 包含 Request 对象的可迭代对象
  289. '''
  290. return start_requests
  291.  
  292. 内置爬虫中间件:
  293. 'scrapy.contrib.spidermiddleware.httperror.HttpErrorMiddleware': 50,
  294. 'scrapy.contrib.spidermiddleware.offsite.OffsiteMiddleware': 500,
  295. 'scrapy.contrib.spidermiddleware.referer.RefererMiddleware': 700,
  296. 'scrapy.contrib.spidermiddleware.urllength.UrlLengthMiddleware': 800,
  297. 'scrapy.contrib.spidermiddleware.depth.DepthMiddleware': 900,
  298.  
  299. """
  300. # from scrapy.contrib.spidermiddleware.referer import RefererMiddleware
  301. # Enable or disable spider middlewares
  302. # See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
  303. SPIDER_MIDDLEWARES = {
  304. # 'step8_king.middlewares.SpiderMiddleware': 543,
  305. }
  306.  
  307. """
  308. 22. 下载中间件
  309. class DownMiddleware1(object):
  310. def process_request(self, request, spider):
  311. '''
  312. 请求需要被下载时,经过所有下载器中间件的process_request调用
  313. :param request:
  314. :param spider:
  315. :return:
  316. None,继续后续中间件去下载;
  317. Response对象,停止process_request的执行,开始执行process_response
  318. Request对象,停止中间件的执行,将Request重新调度器
  319. raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
  320. '''
  321. pass
  322.  
  323. def process_response(self, request, response, spider):
  324. '''
  325. spider处理完成,返回时调用
  326. :param response:
  327. :param result:
  328. :param spider:
  329. :return:
  330. Response 对象:转交给其他中间件process_response
  331. Request 对象:停止中间件,request会被重新调度下载
  332. raise IgnoreRequest 异常:调用Request.errback
  333. '''
  334. print('response1')
  335. return response
  336.  
  337. def process_exception(self, request, exception, spider):
  338. '''
  339. 当下载处理器(download handler)或 process_request() (下载中间件)抛出异常
  340. :param response:
  341. :param exception:
  342. :param spider:
  343. :return:
  344. None:继续交给后续中间件处理异常;
  345. Response对象:停止后续process_exception方法
  346. Request对象:停止中间件,request将会被重新调用下载
  347. '''
  348. return None
  349.  
  350. 默认下载中间件
  351. {
  352. 'scrapy.contrib.downloadermiddleware.robotstxt.RobotsTxtMiddleware': 100,
  353. 'scrapy.contrib.downloadermiddleware.httpauth.HttpAuthMiddleware': 300,
  354. 'scrapy.contrib.downloadermiddleware.downloadtimeout.DownloadTimeoutMiddleware': 350,
  355. 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': 400,
  356. 'scrapy.contrib.downloadermiddleware.retry.RetryMiddleware': 500,
  357. 'scrapy.contrib.downloadermiddleware.defaultheaders.DefaultHeadersMiddleware': 550,
  358. 'scrapy.contrib.downloadermiddleware.redirect.MetaRefreshMiddleware': 580,
  359. 'scrapy.contrib.downloadermiddleware.httpcompression.HttpCompressionMiddleware': 590,
  360. 'scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware': 600,
  361. 'scrapy.contrib.downloadermiddleware.cookies.CookiesMiddleware': 700,
  362. 'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 750,
  363. 'scrapy.contrib.downloadermiddleware.chunked.ChunkedTransferMiddleware': 830,
  364. 'scrapy.contrib.downloadermiddleware.stats.DownloaderStats': 850,
  365. 'scrapy.contrib.downloadermiddleware.httpcache.HttpCacheMiddleware': 900,
  366. }
  367.  
  368. """
  369. # from scrapy.contrib.downloadermiddleware.httpauth import HttpAuthMiddleware
  370. # Enable or disable downloader middlewares
  371. # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
  372. # DOWNLOADER_MIDDLEWARES = {
  373. # 'step8_king.middlewares.DownMiddleware1': 100,
  374. # 'step8_king.middlewares.DownMiddleware2': 500,
  375. # }
  376.  
  377. settings

settings.py参数说明

补充:这里有一些没写到的:https://www.cnblogs.com/Chenjiabing/p/6907251.html

  1. 暂停和恢复爬虫
  2. 初学者最头疼的事情就是没有处理好异常,当爬虫爬到一半的时候突然因为错误而中断了,但是这时又不能从中断的地方开始继续爬,顿时感觉心里日了狗,但是这里有一个方法可以暂时的存储你爬的状态,当爬虫中断的时候继续打开后依然可以从中断的地方爬,不过虽说持久化可以有效的处理,但是要注意的是当使用cookie临时的模拟登录状态的时候要注意cookie的有效期
  3.  
  4. 只需要在setting.pyJOB_DIR=file_name其中填的是你的文件目录,注意这里的目录不允许共享,只能存储单独的一个spdire的运行状态,如果你不想在从中断的地方开始运行,只需要将这个文件夹删除即可
  5.  
  6. 当然还有其他的放法:scrapy crawl somespider -s JOBDIR=crawls/somespider-1,这个是在终端启动爬虫的时候调用的,可以通过ctr+c中断,恢复还是输入上面的命令

暂停和恢复爬虫

  1. class xxxSpider(scrapy.Spider):
  2. name = 'xxx'
  3. '''略'''
  4. def parse(self, response):
  5. print(response.url)
  6. print(self.settings["ABCD"])

其他

待补充

TinyScrapy

解读原理之循序渐进twisted

  1. from twisted.web.client import getPage, defer
  2. from twisted.internet import reactor
  3.  
  4. def all_done(arg):
  5. reactor.stop()
  6.  
  7. def callback(contents):
  8. print(contents)
  9.  
  10. deferred_list = []
  11.  
  12. url_list = ['http://www.bing.com', 'http://www.baidu.com', ]
  13. for url in url_list:
  14. deferred = getPage(bytes(url, encoding='utf8'))
  15. deferred.addCallback(callback) # 单个deferred完成任务时执行的callback
  16. deferred_list.append(deferred)
  17.  
  18. dlist = defer.DeferredList(deferred_list)
  19. dlist.addBoth(all_done) # 所有deferred返回结果完成任务时执行的callback
  20.  
  21. reactor.run()

1、执行多个url爬取任务,成功后各自执行callback,以及整个大任务执行callback

  1. from twisted.web.client import getPage, defer
  2. from twisted.internet import reactor
  3.  
  4. def all_done(arg):
  5. reactor.stop()
  6.  
  7. def onedone(response):
  8. print(response)
  9.  
  10. @defer.inlineCallbacks
  11. def task(url):
  12. deferred = getPage(bytes(url, encoding='utf8'))
  13. deferred.addCallback(onedone)
  14. yield deferred
  15.  
  16. deferred_list = []
  17.  
  18. url_list = ['http://www.bing.com', 'http://www.baidu.com', ]
  19. for url in url_list:
  20. deferred = task(url) # 把下面的两个操作封装进task,同样是每个任务单独执行callback
  21. # deferred = getPage(url)
  22. # deferred.addCallback(onedone)
  23. deferred_list.append(deferred)
  24.  
  25. dlist = defer.DeferredList(deferred_list)
  26. dlist.addBoth(all_done) # 所有deferred返回结果完成任务时执行的callback
  27.  
  28. reactor.run()

2、把每个任务及执行callback封装到一个函数里

  1. from twisted.web.client import getPage, defer
  2. from twisted.internet import reactor
  3.  
  4. def all_done(arg):
  5. reactor.stop()
  6.  
  7. def onedone(response):
  8. print(response)
  9.  
  10. @defer.inlineCallbacks
  11. def task():
  12. deferred2 = getPage(bytes("http://www.baidu.com", encoding='utf8'))
  13. deferred2.addCallback(onedone)
  14. yield deferred2
  15.  
  16. deferred1 = getPage(bytes("http://www.google.com", encoding='utf8')) # 访问一个无法访问的url,因此无法执行callback
  17. deferred1.addCallback(onedone)
  18. yield deferred1
  19.  
  20. ret = task()
  21. ret.addBoth(all_done) # 所有deferred返回结果完成任务时执行的callback
  22.  
  23. reactor.run()

3、执行的任务中存在无法访问的url,导致不能执行该单独任务的callback

  1. from twisted.web.client import getPage, defer
  2. from twisted.internet import reactor
  3.  
  4. def all_done(arg):
  5. reactor.stop()
  6.  
  7. def onedone(response):
  8. print(response)
  9.  
  10. @defer.inlineCallbacks
  11. def task():
  12. deferred2 = getPage(bytes("http://www.baidu.com", encoding='utf8'))
  13. deferred2.addCallback(onedone)
  14. yield deferred2
  15.  
  16. stop_deferred = defer.Deferred() # 此为任务卡死的deferred,如果不手动执行callback,则程序会一直卡在这
  17. stop_deferred.callback("aasdfsdf") # 手动执行callback
  18. yield stop_deferred
  19.  
  20. ret = task()
  21. ret.addBoth(all_done) # 所有deferred返回结果完成任务时执行的callback
  22.  
  23. reactor.run()

4、对卡死的任务手动执行callback

  1. from twisted.web.client import getPage, defer
  2. from twisted.internet import reactor
  3.  
  4. running_list = []
  5. stop_deferred = None
  6.  
  7. def all_done(arg):
  8. reactor.stop()
  9.  
  10. def onedone(response,url):
  11. print(response)
  12. running_list.remove(url) # 把正在运行的任务列表去除本callback对应的已完成的url
  13.  
  14. def check_empty(response):
  15. if not running_list: # 正在运行的任务列表,如果为空,在把卡死任务手动执行callback
  16. stop_deferred.callback(None)
  17.  
  18. @defer.inlineCallbacks
  19. def open_spider(url): # 把爬虫任务封装到这
  20. deferred2 = getPage(bytes(url, encoding='utf8')) # 执行任务
  21. deferred2.addCallback(onedone, url) # callback
  22. deferred2.addCallback(check_empty) # 检查正在运行的任务是否为空,是,则手动执行callback
  23. yield deferred2
  24.  
  25. @defer.inlineCallbacks
  26. def stop(url):
  27. global stop_deferred # 执行卡死任务
  28. stop_deferred = defer.Deferred()
  29. yield stop_deferred
  30.  
  31. @defer.inlineCallbacks
  32. def task(url):
  33. yield open_spider(url) # 启动爬虫任务
  34. yield stop(url) # 启动卡死任务
  35.  
  36. running_list.append("http://www.baidu.com")
  37. ret = task("http://www.baidu.com")
  38. ret.addBoth(all_done)
  39.  
  40. reactor.run()

5、监控正在运行的任务列表,适时手动执行callback

  1. from twisted.web.client import getPage, defer
  2. from twisted.internet import reactor
  3.  
  4. class ExecutionEngine(object):
  5. def __init__(self):
  6. self.stop_deferred = None
  7. self.running_list = []
  8.  
  9. def onedone(self,response,url):
  10. print(response)
  11. self.running_list.remove(url) # 把正在运行的任务列表去除本callback对应的已完成的url
  12.  
  13. def check_empty(self,response):
  14. if not self.running_list: # 监控正在运行的任务列表,如果为空,在把卡死任务手动执行callback
  15. self.stop_deferred.callback(None)
  16.  
  17. @defer.inlineCallbacks
  18. def open_spider(self,url): # 爬虫任务
  19. deferred2 = getPage(bytes(url, encoding='utf8'))
  20. deferred2.addCallback(self.onedone, url) # 添加callback
  21. deferred2.addCallback(self.check_empty) # 添加callback2,监控正在运行的任务列表
  22. yield deferred2
  23.  
  24. @defer.inlineCallbacks
  25. def stop(self,url):
  26. self.stop_deferred = defer.Deferred() # 卡死任务
  27. yield self.stop_deferred
  28.  
  29. @defer.inlineCallbacks
  30. def task(url):
  31. engine = ExecutionEngine()
  32. engine.running_list.append(url) # 正在执行任务的列表添加新url
  33.  
  34. yield engine.open_spider(url)
  35. yield engine.stop(url)
  36.  
  37. def all_done(arg):
  38. reactor.stop()
  39.  
  40. if __name__ == '__main__':
  41.  
  42. ret = task("http://www.baidu.com")
  43. ret.addBoth(all_done)
  44.  
  45. reactor.run()

6、把第5步进一步封装为类

开发TinyScrapy

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. from twisted.web.client import getPage, defer
  4. from twisted.internet import reactor
  5. import queue
  6.  
  7. class Request(object):
  8. def __init__(self, url, callback):
  9. self.url = url
  10. self.callback = callback
  11.  
  12. class Scheduler(object):
  13. def __init__(self, engine):
  14. self.q = queue.Queue()
  15. self.engine = engine
  16.  
  17. def enqueue_request(self, request):
  18. """
  19.  
  20. :param request:
  21. :return:
  22. """
  23. self.q.put(request)
  24.  
  25. def next_request(self):
  26. try:
  27. req = self.q.get(block=False)
  28. except Exception as e:
  29. req = None
  30.  
  31. return req
  32.  
  33. def size(self):
  34. return self.q.qsize()
  35.  
  36. class ExecutionEngine(object):
  37. def __init__(self):
  38. self._closewait = None
  39. self.running = True
  40. self.start_requests = None
  41. self.scheduler = Scheduler(self)
  42.  
  43. self.inprogress = set()
  44.  
  45. def check_empty(self, response):
  46. if not self.running:
  47. self._closewait.callback('......')
  48.  
  49. def _next_request(self):
  50. while self.start_requests:
  51. try:
  52. request = next(self.start_requests)
  53. except StopIteration:
  54. self.start_requests = None
  55. else:
  56. self.scheduler.enqueue_request(request)
  57.  
  58. print(len(self.inprogress), self.scheduler.size())
  59. while len(self.inprogress) < 5 and self.scheduler.size() > 0: # 最大并发数为5
  60.  
  61. request = self.scheduler.next_request()
  62. if not request:
  63. break
  64.  
  65. self.inprogress.add(request)
  66. d = getPage(bytes(request.url, encoding='utf-8'))
  67. d.addBoth(self._handle_downloader_output, request)
  68. d.addBoth(lambda x, req: self.inprogress.remove(req), request)
  69. d.addBoth(lambda x: self._next_request())
  70.  
  71. if len(self.inprogress) == 0 and self.scheduler.size() == 0:
  72. self._closewait.callback(None)
  73.  
  74. def _handle_downloader_output(self, response, request):
  75. """
  76. 获取内容,执行回调函数,并且把回调函数中的返回值获取,并添加到队列中
  77. :param response:
  78. :param request:
  79. :return:
  80. """
  81. import types
  82.  
  83. gen = request.callback(response)
  84. if isinstance(gen, types.GeneratorType):
  85. for req in gen:
  86. self.scheduler.enqueue_request(req)
  87.  
  88. @defer.inlineCallbacks
  89. def start(self):
  90. self._closewait = defer.Deferred()
  91. yield self._closewait
  92.  
  93. @defer.inlineCallbacks
  94. def open_spider(self, start_requests):
  95. self.start_requests = start_requests
  96. yield None
  97. reactor.callLater(0, self._next_request)
  98.  
  99. @defer.inlineCallbacks
  100. def crawl(start_requests):
  101. engine = ExecutionEngine()
  102.  
  103. start_requests = iter(start_requests)
  104. yield engine.open_spider(start_requests)
  105. yield engine.start()
  106.  
  107. def _stop_reactor(_=None):
  108. reactor.stop()
  109.  
  110. def parse(response):
  111. for i in range(10):
  112. yield Request("http://dig.chouti.com/all/hot/recent/%s" % i, callback)
  113.  
  114. if __name__ == '__main__':
  115. start_requests = [Request("http://www.baidu.com", parse), Request("http://www.baidu1.com", parse), ]
  116.  
  117. ret = crawl(start_requests)
  118.  
  119. ret.addBoth(_stop_reactor)
  120.  
  121. reactor.run()

精简版,方便理解流程

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. from twisted.web.client import getPage, defer
  4. from twisted.internet import reactor
  5. import queue
  6.  
  7. class Response(object):
  8. def __init__(self, body, request):
  9. self.body = body
  10. self.request = request
  11. self.url = request.url
  12.  
  13. @property
  14. def text(self):
  15. return self.body.decode('utf-8')
  16.  
  17. class Request(object):
  18. def __init__(self, url, callback=None):
  19. self.url = url
  20. self.callback = callback
  21.  
  22. class Scheduler(object):
  23. def __init__(self, engine):
  24. self.q = queue.Queue()
  25. self.engine = engine
  26.  
  27. def enqueue_request(self, request):
  28. self.q.put(request)
  29.  
  30. def next_request(self):
  31. try:
  32. req = self.q.get(block=False)
  33. except Exception as e:
  34. req = None
  35.  
  36. return req
  37.  
  38. def size(self):
  39. return self.q.qsize()
  40.  
  41. class ExecutionEngine(object):
  42. def __init__(self):
  43. self._closewait = None
  44. self.running = True
  45. self.start_requests = None
  46. self.scheduler = Scheduler(self)
  47.  
  48. self.inprogress = set()
  49.  
  50. def check_empty(self, response):
  51. if not self.running:
  52. self._closewait.callback('......')
  53.  
  54. def _next_request(self):
  55. while self.start_requests:
  56. try:
  57. request = next(self.start_requests)
  58. except StopIteration:
  59. self.start_requests = None
  60. else:
  61. self.scheduler.enqueue_request(request)
  62.  
  63. while len(self.inprogress) < 5 and self.scheduler.size() > 0: # 最大并发数为5
  64.  
  65. request = self.scheduler.next_request()
  66. if not request:
  67. break
  68.  
  69. self.inprogress.add(request)
  70. d = getPage(bytes(request.url, encoding='utf-8'))
  71. d.addBoth(self._handle_downloader_output, request)
  72. d.addBoth(lambda x, req: self.inprogress.remove(req), request)
  73. d.addBoth(lambda x: self._next_request())
  74.  
  75. if len(self.inprogress) == 0 and self.scheduler.size() == 0:
  76. self._closewait.callback(None)
  77.  
  78. def _handle_downloader_output(self, body, request):
  79. """
  80. 获取内容,执行回调函数,并且把回调函数中的返回值获取,并添加到队列中
  81. :param response:
  82. :param request:
  83. :return:
  84. """
  85. import types
  86.  
  87. response = Response(body, request)
  88. func = request.callback or self.spider.parse
  89. gen = func(response)
  90. if isinstance(gen, types.GeneratorType):
  91. for req in gen:
  92. self.scheduler.enqueue_request(req)
  93.  
  94. @defer.inlineCallbacks
  95. def start(self):
  96. self._closewait = defer.Deferred()
  97. yield self._closewait
  98.  
  99. @defer.inlineCallbacks
  100. def open_spider(self, spider, start_requests):
  101. self.start_requests = start_requests
  102. self.spider = spider
  103. yield None
  104. reactor.callLater(0, self._next_request)
  105.  
  106. class Crawler(object):
  107. def __init__(self, spidercls):
  108. self.spidercls = spidercls
  109.  
  110. self.spider = None
  111. self.engine = None
  112.  
  113. @defer.inlineCallbacks
  114. def crawl(self):
  115. self.engine = ExecutionEngine()
  116. self.spider = self.spidercls()
  117. start_requests = iter(self.spider.start_requests())
  118. yield self.engine.open_spider(self.spider, start_requests)
  119. yield self.engine.start()
  120.  
  121. class CrawlerProcess(object):
  122. def __init__(self):
  123. self._active = set()
  124. self.crawlers = set()
  125.  
  126. def crawl(self, spidercls, *args, **kwargs):
  127. crawler = Crawler(spidercls)
  128. self.crawlers.add(crawler)
  129.  
  130. d = crawler.crawl(*args, **kwargs)
  131. self._active.add(d)
  132. return d
  133.  
  134. def start(self):
  135. dl = defer.DeferredList(self._active)
  136. dl.addBoth(self._stop_reactor)
  137. reactor.run()
  138.  
  139. def _stop_reactor(self, _=None):
  140. reactor.stop()
  141.  
  142. class Spider(object):
  143. def start_requests(self):
  144. for url in self.start_urls:
  145. yield Request(url)
  146.  
  147. class ChoutiSpider(Spider):
  148. name = "chouti"
  149. start_urls = [
  150. 'http://dig.chouti.com/',
  151. ]
  152.  
  153. def parse(self, response):
  154. print(response.text)
  155.  
  156. class CnblogsSpider(Spider):
  157. name = "cnblogs"
  158. start_urls = [
  159. 'http://www.cnblogs.com/',
  160. ]
  161.  
  162. def parse(self, response):
  163. print(response.text)
  164.  
  165. if __name__ == '__main__':
  166.  
  167. spider_cls_list = [ChoutiSpider, CnblogsSpider]
  168.  
  169. crawler_process = CrawlerProcess()
  170. for spider_cls in spider_cls_list:
  171. crawler_process.crawl(spider_cls)
  172.  
  173. crawler_process.start()

TinyScrapy

示例

  1. # -*- coding: utf-8 -*-
  2. import scrapy
  3. from scrapy.http import Request
  4. from scrapy.selector import Selector
  5.  
  6. class ChoutiSpider(scrapy.Spider):
  7. name = 'chouti'
  8. allowed_domains = ['chouti.com']
  9. start_urls = ['http://chouti.com/']
  10.  
  11. cookie_dict = {}
  12.  
  13. def start_requests(self):
  14. for url in self.start_urls:
  15. yield Request(url, dont_filter=True,callback=self.parse)
  16.  
  17. def parse(self,response):
  18. from scrapy.http.cookies import CookieJar
  19. cookie_jar = CookieJar() # 对象,中封装了cookies
  20. cookie_jar.extract_cookies(response,response.request) # 去响应中获取cookies
  21. for k, v in cookie_jar._cookies.items():
  22. for i, j in v.items():
  23. for m, n in j.items():
  24. self.cookie_dict[m] = n.value
  25.  
  26. from urllib.parse import urlencode
  27. post_dict = {
  28. "phone":"8618xxxxxxxxxx",
  29. "password":"xxxx",
  30. "oneMonth":1,
  31. }
  32.  
  33. yield Request(
  34. url = "http://dig.chouti.com/login",
  35. method="POST",
  36. cookies=self.cookie_dict,
  37. body=urlencode(post_dict),
  38. headers={
  39. "Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",
  40. },
  41. callback=self.parse2,
  42. )
  43.  
  44. def parse2(self,response):
  45. # print(response.text)
  46. yield Request(
  47. url="http://dig.chouti.com",
  48. cookies=self.cookie_dict,
  49. callback=self.parse3,
  50. )
  51.  
  52. def parse3(self,response):
  53. # 找div,class=part2的标签,获取share-linkid属性
  54. hxs = Selector(response)
  55. linkid_list = hxs.xpath("//div[@class='part2']/@share-linkid").extract()
  56. # print(linkid_list)
  57.  
  58. for linkid in linkid_list:
  59. base_url = "https://dig.chouti.com/link/vote?linksId={linkid}".format(linkid=linkid)
  60. yield Request(
  61. method="POST",
  62. url=base_url,
  63. cookies=self.cookie_dict,
  64. callback=self.parse4,
  65. )
  66.  
  67. def parse4(self,response):
  68. print(response.text)
  69.  
  70. 抽屉网第一页点赞

抽屉网第一页点赞

  1. # -*- coding: utf-8 -*-
  2. import scrapy
  3. from scrapy.http import Request
  4. from scrapy.selector import Selector
  5.  
  6. class ChoutiSpider(scrapy.Spider):
  7. name = 'chouti'
  8. allowed_domains = ['chouti.com']
  9. start_urls = ['http://chouti.com/']
  10.  
  11. cookie_dict = {}
  12.  
  13. def start_requests(self):
  14. for url in self.start_urls:
  15. yield Request(url, dont_filter=True,callback=self.parse)
  16.  
  17. def parse(self,response):
  18. from scrapy.http.cookies import CookieJar
  19. cookie_jar = CookieJar() # 对象,中封装了cookies
  20. cookie_jar.extract_cookies(response,response.request) # 去响应中获取cookies
  21. for k, v in cookie_jar._cookies.items():
  22. for i, j in v.items():
  23. for m, n in j.items():
  24. self.cookie_dict[m] = n.value
  25.  
  26. from urllib.parse import urlencode
  27. post_dict = {
  28. "phone":"8618xxxxxxxxxx",
  29. "password":"xxxx",
  30. "oneMonth":1,
  31. }
  32.  
  33. yield Request(
  34. url = "http://dig.chouti.com/login",
  35. method="POST",
  36. cookies=self.cookie_dict,
  37. body=urlencode(post_dict),
  38. headers={
  39. "Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",
  40. },
  41. callback=self.parse2,
  42. )
  43.  
  44. def parse2(self,response):
  45. # print(response.text)
  46. yield Request(
  47. url="http://dig.chouti.com",
  48. cookies=self.cookie_dict,
  49. callback=self.parse3,
  50. )
  51.  
  52. def parse3(self,response):
  53. # 找div,class=part2的标签,获取share-linkid属性
  54. hxs = Selector(response)
  55. linkid_list = hxs.xpath("//div[@class='part2']/@share-linkid").extract()
  56. # print(linkid_list)
  57.  
  58. for linkid in linkid_list:
  59. # 获取每一个ID去点赞
  60. base_url = "https://dig.chouti.com/link/vote?linksId={linkid}".format(linkid=linkid)
  61. yield Request(
  62. method="POST",
  63. url=base_url,
  64. cookies=self.cookie_dict,
  65. callback=self.parse4,
  66. )
  67.  
  68. page_list = hxs.xpath("//div[@id='dig_lcpage']//a/@href").extract()
  69. for page in page_list:
  70. # /all/hot/recent/2
  71. page_url = "http://dig.chouti.com{}".format(page)
  72. yield Request(url=page_url,method="GET",callback=self.parse3)
  73.  
  74. def parse4(self,response):
  75. print(response.text)

抽屉网所有文章点赞(遍历所有页,无节制的)

补充

发送post请求

  1. from scrapy.http import Request,FormRequest
  2.  
  3. class mySpider(scrapy.Spider):
  4. # start_urls = ["http://www.example.com/"]
  5.  
  6. def start_requests(self):
  7. url = 'http://www.renren.com/PLogin.do'
  8.  
  9. # FormRequest 是Scrapy发送POST请求的方法
  10. yield scrapy.FormRequest(
  11. url = url,
  12. formdata = {"email" : "xxx", "password" : "xxxxx"},
  13. callback = self.parse_page
  14. )
  15. def parse_page(self, response):
  16. # do something

发送post请求

数据采集器

https://www.cnblogs.com/sufei-duoduo/p/5881385.html

  1. Scrapy 提供了方便的收集数据的机制。数据以 key/value 方式存储,值大多是计数值。该机制叫做数据收集器(Stats Collector),可以通过 Crawler API 的属性 stats来使用。
  2.  
  3. 无论数据收集(stats collection)开启或者关闭,数据收集器永远都是可用的。因此可以 import 进自己的模块并使用其 API(增加值或者设置新的状态键(stats keys))。该做法是为了简化数据收集的方法:不应该使用超过一行代码来收集你的 spiderScrapy 扩展或者任何你使用数据收集器代码里头的状态。
  4.  
  5. 数据收集器的另一个特性是(在启用状态下)很高效,(在关闭情况下)非常高效(几乎察觉不到)。
  6.  
  7. 数据收集器对每个 spider 保持一个状态。当 spider 启动时,该表自动打开,当 spider 关闭时,自动关闭。

可用的数据采集器种类

  1. 可用的数据收集器
  2. 除了基本的 StatsCollector Scrapy 也提供了基于 StatsCollector 的数据收集器。 您可以通过 STATS_CLASS 设置来选择。默认使用的是 MemoryStatsCollector
  3.  
  4. MemoryStatsCollector
  5.  
  6. class scrapy.statscol.MemoryStatsCollector
  7.  
  8. 一个简单的数据收集器。其在 spider 运行完毕后将其数据保存在内存中。数据可以通过 spider_stats 属性访问。该属性是一个以 spider 名字为键(key)的字典。
  9.  
  10. 这是 Scrapy 的默认选择。
  11.  
  12. spider_stats
  13.  
  14. 保存了每个 spider 最近一次爬取的状态的字典(dict)。该字典以 spider 名字为键,值也是字典。
  15.  
  16. DummyStatsCollector
  17.  
  18. class scrapy.statscol.DummyStatsCollector
  19.  
  20. 该数据收集器并不做任何事情但非常高效。您可以通过设置 STATS_CLASS 启用这个收集器,来关闭数据收集,提高效率。 不过,数据收集的性能负担相较于 Scrapy 其他的处理(例如分析页面)来说是非常小的。

数据采集器种类

使用方法

  1. #设置数据:
  2. stats.set_value('hostname', socket.gethostname())
  3.  
  4. #增加数据值:
  5. stats.inc_value('pages_crawled')
  6.  
  7. #当新的值比原来的值大时设置数据:
  8. stats.max_value('max_items_scraped', value)
  9.  
  10. #当新的值比原来的值小时设置数据:
  11. stats.min_value('min_free_memory_percent', value)
  12.  
  13. #获取数据:
  14. >>> stats.get_value('pages_crawled')
  15.  
  16. #获取所有数据:
  17. >>> stats.get_stats()
  18. {'pages_crawled': 1238, 'start_time': datetime.datetime(2009, 7, 14, 21, 47, 28, 977139)}

常用方法

  1. class ExtensionThatAccessStats(object):
  2.  
  3. def __init__(self, stats):
  4. self.stats = stats
  5.  
  6. @classmethod
  7. def from_crawler(cls, crawler):
  8. return cls(crawler.stats)

在extension中的使用

  1. """
  2. Extension for collecting core stats like items scraped and start/finish times
  3. """
  4. import datetime
  5.  
  6. from scrapy import signals
  7.  
  8. class CoreStats(object):
  9.  
  10. def __init__(self, stats):
  11. self.stats = stats
  12.  
  13. @classmethod
  14. def from_crawler(cls, crawler):
  15. o = cls(crawler.stats)
  16. crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
  17. crawler.signals.connect(o.spider_closed, signal=signals.spider_closed)
  18. crawler.signals.connect(o.item_scraped, signal=signals.item_scraped)
  19. crawler.signals.connect(o.item_dropped, signal=signals.item_dropped)
  20. crawler.signals.connect(o.response_received, signal=signals.response_received)
  21. return o
  22.  
  23. def spider_opened(self, spider):
  24. print("haha")
  25. self.stats.set_value('start_time', datetime.datetime.utcnow(), spider=spider)
  26.  
  27. def spider_closed(self, spider, reason):
  28. self.stats.set_value('finish_time', datetime.datetime.utcnow(), spider=spider)
  29. self.stats.set_value('finish_reason', reason, spider=spider)
  30. print("spider_closed","spider start time",self.stats.get_value("start_time"))
  31. print("spider_closed","item_scraped",self.stats.get_value("item_scraped_count"))
  32. print("spider_closed","response_received",self.stats.get_value("response_received_count"))
  33. print("spider_closed","spider finish time",self.stats.get_value("finish_time"))
  34. print("spider_closed","spider finish reason",self.stats.get_value("finish_reason"))
  35.  
  36. def item_scraped(self, item, spider):
  37. self.stats.inc_value('item_scraped_count', spider=spider)
  38.  
  39. def response_received(self, spider):
  40. self.stats.inc_value('response_received_count', spider=spider)
  41.  
  42. def item_dropped(self, item, spider, exception):
  43. reason = exception.__class__.__name__
  44. self.stats.inc_value('item_dropped_count', spider=spider)
  45. self.stats.inc_value('item_dropped_reasons_count/%s' % reason, spider=spider)
  46.  
  47. """
  48. haha
  49. spider_closed spider start time 2018-11-03 16:44:18.618151
  50. spider_closed item_scraped None
  51. spider_closed response_received 4
  52. spider_closed spider finish time 2018-11-03 16:44:21.951843
  53. spider_closed spider finish reason finished
  54. """

实例

log

https://scrapy-chs.readthedocs.io/zh_CN/latest/topics/logging.html#log-levels

  1. Logging
  2.  
  3. Scrapy提供了log功能。您可以通过 scrapy.log 模块使用。当前底层实现使用了 Twisted logging ,不过可能在之后会有所变化。
  4.  
  5. log服务必须通过显示调用 scrapy.log.start() 来开启,以捕捉顶层的Scrapy日志消息。 在此之上,每个crawler都拥有独立的log观察者(observer)(创建时自动连接(attach)),接收其spider的日志消息。
  6.  
  7. Log levels
  8.  
  9. Scrapy提供5logging级别:
  10.  
  11. CRITICAL - 严重错误(critical)
  12. ERROR - 一般错误(regular errors)
  13. WARNING - 警告信息(warning messages)
  14. INFO - 一般信息(informational messages)
  15. DEBUG - 调试信息(debugging messages)
  16. 如何设置log级别
  17.  
  18. 您可以通过终端选项(command line option) loglevel/-L LOG_LEVEL 来设置log级别。
  19.  
  20. 如何记录信息(log messages)
  21.  
  22. 下面给出如何使用 WARNING 级别来记录信息的例子:
  23.  
  24. from scrapy import log
  25. log.msg("This is a warning", level=log.WARNING)
  26. Spider中添加log(Logging from Spiders)
  27.  
  28. spider中添加log的推荐方式是使用Spider log() 方法。该方法会自动在调用 scrapy.log.msg() 时赋值 spider 参数。其他的参数则直接传递给 msg() 方法。
  29.  
  30. scrapy.log模块
  31.  
  32. scrapy.log.start(logfile=None, loglevel=None, logstdout=None)
  33. 启动Scrapy顶层logger。该方法必须在记录任何顶层消息前被调用 (使用模块的 msg() 而不是 Spider.log 的消息)。否则,之前的消息将会丢失。
  34.  
  35. 参数:
  36. logfile (str) 用于保存log输出的文件路径。如果被忽略, LOG_FILE 设置会被使用。 如果两个参数都是 None log将会被输出到标准错误流(standard error)。
  37. loglevel 记录的最低的log级别. 可用的值有: CRITICAL, ERROR, WARNING, INFO and DEBUG.
  38. logstdout (boolean) 如果为 True 所有您的应用的标准输出(包括错误)将会被记录(logged instead)。 例如,如果您调用 print hello’” ,则’hello 会在Scrapylog中被显示。 如果被忽略,则 LOG_STDOUT 设置会被使用。
  39. scrapy.log.msg(message, level=INFO, spider=None)
  40. 记录信息(Log a message)
  41.  
  42. 参数:
  43. message (str) log的信息
  44. level 该信息的log级别. 参考 Log levels.
  45. spider (Spider 对象) 记录该信息的spider. 当记录的信息和特定的spider有关联时,该参数必须被使用。
  46. scrapy.log.CRITICAL
  47. 严重错误的Log级别
  48.  
  49. scrapy.log.ERROR
  50. 错误的Log级别 Log level for errors
  51.  
  52. scrapy.log.WARNING
  53. 警告的Log级别 Log level for warnings
  54.  
  55. scrapy.log.INFO
  56. 记录信息的Log级别(生产部署时推荐的Log级别)
  57.  
  58. scrapy.log.DEBUG
  59. 调试信息的Log级别(开发时推荐的Log级别)
  60.  
  61. Logging设置
  62.  
  63. 以下设置可以被用来配置logging:
  64.  
  65. LOG_ENABLED
  66. LOG_ENCODING
  67. LOG_FILE
  68. LOG_LEVEL
  69. LOG_STDOUT

logging

实例

  1. LOG_FILE = "my_log.log"
  2. LOG_LEVEL = 'DEBUG'

settings.py

  1. class JiandanSpider(scrapy.Spider):
  2. """略"""
  3.  
  4. def parse(self, response):
  5. import logging
  6. self.log("test scrapy log",level=logging.ERROR)
  7. self.log("test scrapy log")

spiders/xx.py

参考or转发

http://www.cnblogs.com/wupeiqi/articles/6229292.html

http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html

scrapy笔记集合的更多相关文章

  1. Learning Scrapy笔记(六)- Scrapy处理JSON API和AJAX页面

    摘要:介绍了使用Scrapy处理JSON API和AJAX页面的方法 有时候,你会发现你要爬取的页面并不存在HTML源码,譬如,在浏览器打开http://localhost:9312/static/, ...

  2. Learning Scrapy笔记(零) - 前言

    我已经使用了scrapy有半年之多,但是却一直都感觉没有入门,网上关于scrapy的文章简直少得可怜,而官网上的文档(http://doc.scrapy.org/en/1.0/index.html)对 ...

  3. JavaScript基础笔记集合(转)

    JavaScript基础笔记集合   JavaScript基础笔记集合   js简介 js是脚本语言.浏览器是逐行的读取代码,而传统编程会在执行前进行编译   js存放的位置 html脚本必须放在&l ...

  4. 转 Scrapy笔记(5)- Item详解

    Item是保存结构数据的地方,Scrapy可以将解析结果以字典形式返回,但是Python中字典缺少结构,在大型爬虫系统中很不方便. Item提供了类字典的API,并且可以很方便的声明字段,很多Scra ...

  5. Scrapy笔记(1)- 入门篇

    Scrapy笔记01- 入门篇 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架.可以应用在包括数据挖掘, 信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取(更确切来说, ...

  6. Scrapy笔记02- 完整示例

    Scrapy笔记02- 完整示例 这篇文章我们通过一个比较完整的例子来教你使用Scrapy,我选择爬取虎嗅网首页的新闻列表. 这里我们将完成如下几个步骤: 创建一个新的Scrapy工程 定义你所需要要 ...

  7. Scrapy笔记03- Spider详解

    Scrapy笔记03- Spider详解 Spider是爬虫框架的核心,爬取流程如下: 先初始化请求URL列表,并指定下载后处理response的回调函数.初次请求URL通过start_urls指定, ...

  8. Scrapy笔记04- Selector详解

    Scrapy笔记04- Selector详解 在你爬取网页的时候,最普遍的事情就是在页面源码中提取需要的数据,我们有几个库可以帮你完成这个任务: BeautifulSoup是python中一个非常流行 ...

  9. Scrapy笔记05- Item详解

    Scrapy笔记05- Item详解 Item是保存结构数据的地方,Scrapy可以将解析结果以字典形式返回,但是Python中字典缺少结构,在大型爬虫系统中很不方便. Item提供了类字典的API, ...

随机推荐

  1. python 定时器schedule执行任务

    import schedule import time """英文版书籍:<essential sqlalchemy>,这本书讲了很多在每天某个指定的时间点上 ...

  2. Redis(五)主从复制

    本文转载自编程迷思,原文链接 深入学习Redis(3):主从复制 前言 在前面的两篇文章中,分别介绍了Redis的内存模型和Redis的持久化. 在Redis的持久化中曾提到,Redis高可用的方案包 ...

  3. $2018/8/19 = Day5$学习笔记 + 杂题整理

    \(\mathcal{Morning}\) \(Task \ \ 1\) 容斥原理 大概这玩意儿就是来用交集大小求并集大小或者用并集大小求交集大小的\(2333\)? 那窝萌思考已知\(A_1,A_2 ...

  4. 404 Note Found 队-Beta2

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  5. oracle什么时候须要commit

    今天在oracle的SQL plus 中运行了删除和查询操作,然后在PL/SQL中也运行查询操作,语句一样,结果却不一样,让我大感郁闷,后来才突然想到可能是两边数据不一致造成的,可是为什么不一致呢,就 ...

  6. Linux学习笔记(第十章)

    vim程序编辑器 vim特点: vim三种模式: 一般模式:打开文档就直接进入编辑模式 -可进行删除,复制等,无法直接编辑文档 编辑模式:按下[i,I,o,O,A,R,r]等字母才会进入编辑模式,按E ...

  7. Visual Studio 2015 正式版镜像下载(含专业版/企业版KEY)

    Visual Studio Community 2015简体中文版(社区版,针对个人免费): 在线安装exe:http://download.microsoft.com/download/B/4/8/ ...

  8. 一维码UPC A简介及其解码实现(zxing-cpp)

    UPC(Universal Product Code)码是最早大规模应用的条码,其特性是一种长度固定.连续性的条  码,目前主要在美国和加拿大使用,由于其应用范围广泛,故又被称万用条码. UPC码仅可 ...

  9. 41-mysql作业

    1 2 3 4

  10. 2460: [BeiJing2011]元素

    2460: [BeiJing2011]元素 链接 分析: 贪心的想:首先按权值排序,然后从大到小依次放,能放则放.然后用线性基维护是否合法. 代码: #include<cstdio> #i ...