概念
介绍
由一系列定义了一个网址或一组网址类如何被爬取的类组成
具体包括如何执行爬取任务并且如何从页面中提取结构化的数据。
简单来说就是帮助你爬取数据的地方
内部行为流程
初始请求以及默认回调
生成初始的 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.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
属性
url 指定请求地址
callback 指定回调函数
method 指定请求方式, 默认为 GET , 在使用 POST 表单提交的时候推荐使用
meta 在 request 和 response 中添加传递信息时使用 scrapy.FormRequest
headers 指定请求头, 字典形式可传递多个键值
body 指定请求体
cookies 指定 cookies , 可以自己设置, 列表或者字典形式
但是 scrapy 会自动处理好( 默认自带一个中间件处理 ), 所以不需要操心这个
priority 优先级, 越高越优先, 默认为 0
dont_filter 默认为 False , 表示不能被过滤, 设置为 True 时, 表示会被过滤
errback 返回 500 或者 404 的时候的回调函数, 发送错误的回调
方法
copy() 返回一个当前请求的复制
replace() 返回一个当前请求某些属性的替换后的复制
子类
FormRequest 用于处理 表单POST 方式提交数据请求
XmlRpcRequest 没用过不清楚是干嘛的
常用类 Response
属性
url 当前响应网页的 URL
status 当前响应的 状态码, 默认是 200
headers 服务器返回的响应头
body 当前响应网页的全部内容
request 当前响应之前的请求, 通过此属性可以拿到此响应的发送请求
方法
copy() 当前响应的复制
replace() 当前响应替换某些属性后的复制
urljoin() 进行 URL 的拼接
text 当前响应的内容
css() / xpath() CSS / Xpath 标签选择器方法
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 )