概念

介绍

由一系列定义了一个网址或一组网址类如何被爬取的类组成

具体包括如何执行爬取任务并且如何从页面中提取结构化的数据。

简单来说就是帮助你爬取数据的地方

内部行为流程

初始请求以及默认回调

生成初始的 Requests 来爬取第一个URLS,并且标识一个回调函数, 

第一个请求定义在start_requests()方法内默认从start_urls列表中获得url地址来生成Request请求

默认的回调函数是parse方法, 回调函数在下载完成返回response时自动触发

回调函数内解析方式

在回调函数中解析页面内容

通常使用Scrapy自带的Selectors

也可以使用Beutifulsoup,lxml或其他

回调函数处理解析并返回

在回调函数中,解析response并且返回值

返回值可以4种:

  • 包含解析数据的字典
  • Item对象
  • 新的Request对象(新的Requests也需要指定一个回调函数)
  • 或者是可迭代对象(包含Items或Request)

持久化处理

针对返回的Items对象将会被持久化到数据库或者其他文件

  • 通过Item Pipeline组件存到数据库
    • https://docs.scrapy.org/en/latest/topics/item-pipeline.html#topics-item-pipeline)
  • 或者导出到不同的文件
    • 通过Feed exports:https://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports

 内部类

#1、scrapy.spiders.Spider 
  #scrapy.Spider等同于scrapy.spiders.Spider #2、scrapy.spiders.CrawlSpider #3、scrapy.spiders.XMLFeedSpider #4、scrapy.spiders.CSVFeedSpider #5、scrapy.spiders.SitemapSpider

class scrapy.spiders.Spider

这是最简单的spider类,任何其他的spider类都需要继承它(包含你自己定义的)。

该类不提供任何特殊的功能,它仅提供了一个默认的start_requests方法默认从start_urls中读取url地址发送requests请求,并且默认parse作为回调函数

scrapy.spiders.CrawlSpider

对  scrapy.spiders.Spider  更进一步封装了的类

创建爬虫

指定模板创建爬虫

scrapy genspider -t crawl lagou www.lagou.com

查看支持的模板

默认不指名模板的时候使用第一个  basic 模板

如果使用 basic 模板则使用的是   scrapy.spiders.Spider   类来使用爬虫

 crawl   ------>   scrapy.spiders.CrawlSpider 

scrapy genspider --list

Scrapy 框架 - 爬虫 / itemloader

使用 - scrapy.spiders.Spider

基础使用框架

import scrapy
class AmazonSpider(scrapy.Spider):

    name = 'amazon'  # 爬虫名, 必须唯一 
    allowed_domains = ['www.amazon.cn']  # 允许爬取的域名
    start_urls = ['http://www.amazon.cn/']  # 起始爬取地址
def parse(self,response): # 默认的回调函数, 用于对响应内容进行解析 pass

属性方法

#1、name = 'amazon' 
定义爬虫名,scrapy会根据该值定位爬虫程序
所以它必须要有且必须唯一(In Python 2 this must be ASCII only.)

#2、allowed_domains = ['www.amazon.cn'] 
定义允许爬取的域名,如果OffsiteMiddleware启动(默认就启动),
那么不属于该列表的域名及其子域名都不允许爬取
如果爬取的网址为:https://www.example.com/1.html,那就添加'example.com'到列表.

#3、start_urls = ['http://www.amazon.cn/']
如果没有指定url,就从该列表中读取url来生成第一个请求

#4、custom_settings
值为一个字典,定义一些配置信息,在运行爬虫程序时,这些配置会覆盖项目级别的配置
所以custom_settings必须被定义成一个类属性,由于settings会在类实例化前被加载

#5、settings
通过self.settings['配置项的名字']可以访问settings.py中的配置,如果自己定义了custom_settings还是以自己的为准

#6、logger
日志名默认为spider的名字
self.logger.debug('=============>%s' %self.settings['BOT_NAME'])

#5、crawler:了解
该属性必须被定义到类方法from_crawler中

#6、from_crawler(crawler, *args, **kwargs):了解
You probably won’t need to override this directly because the default implementation acts as a proxy to the __init__() method, calling it with the given arguments args and named arguments kwargs.

#7、start_requests()
该方法用来发起第一个Requests请求,且必须返回一个可迭代的对象。它在爬虫程序打开时就被Scrapy调用,Scrapy只调用它一次。
默认从start_urls里取出每个url来生成Request(url, dont_filter=True)

#针对参数dont_filter,请看自定义去重规则

如果你想要改变起始爬取的Requests,你就需要覆盖这个方法,例如你想要起始发送一个POST请求,如下
class MySpider(scrapy.Spider):
name = 'myspider'

def start_requests(self):
return [scrapy.FormRequest("http://www.example.com/login",
formdata={'user': 'john', 'pass': 'secret'},
callback=self.logged_in)]

def logged_in(self, response):
# here you would extract links to follow and return Requests for
# each of them, with another callback
pass

#8、parse(response)
这是默认的回调函数,所有的回调函数必须返回an iterable of Request and/or dicts or Item objects.

#9、log(message[, level, component]):了解
Wrapper that sends a log message through the Spider’s logger, kept for backwards compatibility. For more information see Logging from Spiders.

#10、closed(reason)
爬虫程序结束时自动触发

简单实例 - 爬取猫眼电影

循环方式  - 1 递归解析

# -*- coding: utf-8 -*-
import scrapy
from Maoyan.items import MaoyanItem

class MaoyanSpider(scrapy.Spider):
    # 爬虫名
    name = 'maoyan'
    # 允许爬取的域名
    allowed_domains = ['maoyan.com']
    offset = 0
    # 起始的URL地址
    start_urls = ['https://maoyan.com/board/4?offset=0']

    def parse(self, response):
        # 基准xpath,匹配每个电影信息节点对象列表
        dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
        # dd_list : [<element dd at xxx>,<...>]

        for dd in dd_list:
            # 创建item对象
            item = MaoyanItem()
# [<selector xpath='' data='霸王别姬'>] # dd.xpath('')结果为[选择器1,选择器2] # .extract() 把[选择器1,选择器2]所有选择器序列化为 unicode 字符串 # .extract_first() : 取第一个字符串
item['name'] = dd.xpath('./a/@title').extract_first().strip() item['star'] = dd.xpath('.//p[@class="star"]/text()').extract()[0].strip() item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').extract()[0] yield item # 此方法不推荐,效率低 self.offset += 10 if self.offset <= 90: url = 'https://maoyan.com/' \ 'board/4?offset={}'.format(str(self.offset)) yield scrapy.Request( url=url, callback=self.parse )

循环方式 2 - 指定回调

# -*- coding: utf-8 -*-
import scrapy
from Maoyan.items import MaoyanItem


class MaoyanSpider(scrapy.Spider):
    # 爬虫名
    name = 'maoyan2'
    # 允许爬取的域名
    allowed_domains = ['maoyan.com']
    # 起始的URL地址
    start_urls = ['https://maoyan.com/board/4?offset=0']

    def parse(self, response):
        for offset in range(0, 91, 10):
            url = 'https://maoyan.com' \
                  '/board/4?offset={}'.format(str(offset))
            # 把地址交给调度器入队列
            yield scrapy.Request(
                url=url,
                callback=self.parse_html
            )

    def parse_html(self, response):
        # 基准xpath,匹配每个电影信息节点对象列表
        dd_list = response.xpath(
            '//dl[@class="board-wrapper"]/dd')
        # dd_list : [<element dd at xxx>,<...>]

        for dd in dd_list:
            # 创建item对象
            item = MaoyanItem()
            item['name'] = dd.xpath('./a/@title').extract_first().strip()
            item['star'] = dd.xpath('.//p[@class="star"]/text()').extract()[0].strip()
            item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').extract()[0]

            yield item

详细框架模板

import scrapy
class AmazonSpider(scrapy.Spider):
    def __init__(self,keyword=None,*args,**kwargs):  #在entrypoint文件里面传进来的keyword,在这里接收了
        super(AmazonSpider,self).__init__(*args,**kwargs)
        self.keyword = keyword

    name = 'amazon'  # 必须唯一
    allowed_domains = ['www.amazon.cn']  # 允许域
    start_urls = ['http://www.amazon.cn/']  # 如果你没有指定发送的请求地址,会默认使用第一个

    custom_settings = {  # 自定制配置文件,自己设置了用自己的,没有就找父类的
        "BOT_NAME": 'HAIYAN_AMAZON',
        'REQUSET_HEADERS': {},
    }

    def start_requests(self):
        url = 'https://www.amazon.cn/s/ref=nb_sb_noss_1/461-4093573-7508641?'
        url+=urlencode({"field-keywords":self.keyword})
        print(url)
        yield  scrapy.Request(
            url,
            callback = self.parse_index,  #指定回调函数
            dont_filter = True,  #不去重,这个也可以自己定制
            # dont_filter = False,  #去重,这个也可以自己定制
            # meta={'a':1}  #meta代理的时候会用
        )
        #如果要想测试自定义的dont_filter,可多返回结果重复的即可

常用类 Request

Scrapy 框架 - 爬虫 / itemloader

属性

 url   指定请求地址

 callback   指定回调函数

 method   指定请求方式, 默认为  GET , 在使用  POST  表单提交的时候推荐使用 

 meta   在  request 和  response 中添加传递信息时使用  scrapy.FormRequest 

 headers   指定请求头, 字典形式可传递多个键值

 body   指定请求体

 cookies    指定  cookies , 可以自己设置, 列表或者字典形式

         但是  scrapy 会自动处理好( 默认自带一个中间件处理 ), 所以不需要操心这个

priority   优先级, 越高越优先, 默认为 0

dont_filter   默认为  False , 表示不能被过滤, 设置为  True 时, 表示会被过滤

errback   返回 500 或者 404 的时候的回调函数, 发送错误的回调

方法 

Scrapy 框架 - 爬虫 / itemloader

 copy()   返回一个当前请求的复制

Scrapy 框架 - 爬虫 / itemloader

 replace()   返回一个当前请求某些属性的替换后的复制

子类

 FormRequest   用于处理 表单POST 方式提交数据请求

Scrapy 框架 - 爬虫 / itemloader

  XmlRpcRequest   没用过不清楚是干嘛的

 Scrapy 框架 - 爬虫 / itemloader

常用类 Response

Scrapy 框架 - 爬虫 / itemloader

属性

  url   当前响应网页的 URL

  status   当前响应的 状态码, 默认是 200

  headers   服务器返回的响应头

  body   当前响应网页的全部内容

  request   当前响应之前的请求, 通过此属性可以拿到此响应的发送请求

方法

Scrapy 框架 - 爬虫 / itemloader

  copy()   当前响应的复制

Scrapy 框架 - 爬虫 / itemloader

 replace()   当前响应替换某些属性后的复制

Scrapy 框架 - 爬虫 / itemloader

 urljoin()   进行 URL 的拼接

 Scrapy 框架 - 爬虫 / itemloader

 text   当前响应的内容

Scrapy 框架 - 爬虫 / itemloader

 css() / xpath()   CSS / Xpath 标签选择器方法

Scrapy 框架 - 爬虫 / itemloader

 follow   

子类

  TextResponse   该子类中进行大量的落实操作,对 Response 类进行了大量方法重写

  1 class TextResponse(Response):
  2 
  3     _DEFAULT_ENCODING = 'ascii'
  4 
  5     def __init__(self, *args, **kwargs):
  6         self._encoding = kwargs.pop('encoding', None)
  7         self._cached_benc = None
  8         self._cached_ubody = None
  9         self._cached_selector = None
 10         super(TextResponse, self).__init__(*args, **kwargs)
 11 
 12     def _set_url(self, url):
 13         if isinstance(url, six.text_type):
 14             if six.PY2 and self.encoding is None:
 15                 raise TypeError("Cannot convert unicode url - %s "
 16                                 "has no encoding" % type(self).__name__)
 17             self._url = to_native_str(url, self.encoding)
 18         else:
 19             super(TextResponse, self)._set_url(url)
 20 
 21     def _set_body(self, body):
 22         self._body = b''  # used by encoding detection
 23         if isinstance(body, six.text_type):
 24             if self._encoding is None:
 25                 raise TypeError('Cannot convert unicode body - %s has no encoding' %
 26                     type(self).__name__)
 27             self._body = body.encode(self._encoding)
 28         else:
 29             super(TextResponse, self)._set_body(body)
 30 
 31     def replace(self, *args, **kwargs):
 32         kwargs.setdefault('encoding', self.encoding)
 33         return Response.replace(self, *args, **kwargs)
 34 
 35     @property
 36     def encoding(self):
 37         return self._declared_encoding() or self._body_inferred_encoding()
 38 
 39     def _declared_encoding(self):
 40         return self._encoding or self._headers_encoding() \
 41             or self._body_declared_encoding()
 42 
 43     def body_as_unicode(self):
 44         """Return body as unicode"""
 45         return self.text
 46 
 47     @property
 48     def text(self):
 49         """ Body as unicode """
 50         # access self.encoding before _cached_ubody to make sure
 51         # _body_inferred_encoding is called
 52         benc = self.encoding
 53         if self._cached_ubody is None:
 54             charset = 'charset=%s' % benc
 55             self._cached_ubody = html_to_unicode(charset, self.body)[1]
 56         return self._cached_ubody
 57 
 58     def urljoin(self, url):
 59         """Join this Response's url with a possible relative url to form an
 60         absolute interpretation of the latter."""
 61         return urljoin(get_base_url(self), url)
 62 
 63     @memoizemethod_noargs
 64     def _headers_encoding(self):
 65         content_type = self.headers.get(b'Content-Type', b'')
 66         return http_content_type_encoding(to_native_str(content_type))
 67 
 68     def _body_inferred_encoding(self):
 69         if self._cached_benc is None:
 70             content_type = to_native_str(self.headers.get(b'Content-Type', b''))
 71             benc, ubody = html_to_unicode(content_type, self.body,
 72                     auto_detect_fun=self._auto_detect_fun,
 73                     default_encoding=self._DEFAULT_ENCODING)
 74             self._cached_benc = benc
 75             self._cached_ubody = ubody
 76         return self._cached_benc
 77 
 78     def _auto_detect_fun(self, text):
 79         for enc in (self._DEFAULT_ENCODING, 'utf-8', 'cp1252'):
 80             try:
 81                 text.decode(enc)
 82             except UnicodeError:
 83                 continue
 84             return resolve_encoding(enc)
 85 
 86     @memoizemethod_noargs
 87     def _body_declared_encoding(self):
 88         return html_body_declared_encoding(self.body)
 89 
 90     @property
 91     def selector(self):
 92         from scrapy.selector import Selector
 93         if self._cached_selector is None:
 94             self._cached_selector = Selector(self)
 95         return self._cached_selector
 96 
 97     def xpath(self, query, **kwargs):
 98         return self.selector.xpath(query, **kwargs)
 99 
100     def css(self, query):
101         return self.selector.css(query)
102 
103     def follow(self, url, callback=None, method='GET', headers=None, body=None,
104                cookies=None, meta=None, encoding=None, priority=0,
105                dont_filter=False, errback=None):
106         # type: (...) -> Request
107         """
108         Return a :class:`~.Request` instance to follow a link ``url``.
109         It accepts the same arguments as ``Request.__init__`` method,
110         but ``url`` can be not only an absolute URL, but also
111         
112         * a relative URL;
113         * a scrapy.link.Link object (e.g. a link extractor result);
114         * an attribute Selector (not SelectorList) - e.g.
115           ``response.css('a::attr(href)')[0]`` or
116           ``response.xpath('//img/@src')[0]``.
117         * a Selector for ``<a>`` or ``<link>`` element, e.g.
118           ``response.css('a.my_link')[0]``.
119           
120         See :ref:`response-follow-example` for usage examples.
121         """
122         if isinstance(url, parsel.Selector):
123             url = _url_from_selector(url)
124         elif isinstance(url, parsel.SelectorList):
125             raise ValueError("SelectorList is not supported")
126         encoding = self.encoding if encoding is None else encoding
127         return super(TextResponse, self).follow(url, callback,
128             method=method,
129             headers=headers,
130             body=body,
131             cookies=cookies,
132             meta=meta,
133             encoding=encoding,
134             priority=priority,
135             dont_filter=dont_filter,
136             errback=errback
137         )
TextResponse 源码

相关文章:

  • 2021-06-11
  • 2021-11-16
  • 2021-12-21
猜你喜欢
  • 2022-01-18
  • 2021-11-20
  • 2021-11-23
  • 2021-04-13
  • 2021-09-23
相关资源
相似解决方案