Scrapy:
Scrapy使用了Twisted作为框架,Twisted有些特殊的地方是它是事件驱动的,并且比较适合异步的代码。对于会阻塞线程的操作包含访问文件、数据库或者Web、产生新的进程并需要处理新进程的输出(如运行shell命令)、执行系统层次操作的代码(如等待系统队列),Twisted提供了允许执行上面的操作但不会阻塞代码执行的方法。
items.py 负责数据模型的建立,类似于实体类。
middlewares.py 自己定义的中间件。
pipelines.py 负责对spider返回数据的处理。
settings.py 负责对整个爬虫的配置。
spiders目录 负责存放继承自scrapy的爬虫类。
scrapy.cfg scrapy基础配置
今天学习了一下scrapy的抓取,做了第一个学习测试:
Spiders
import scrapy from quotetoturials.items import QuoteItem class QuoteSpider(scrapy.Spider): name = 'quote' allowed_domains = ['quotes.toscrape.com'] start_urls = ["http://quotes.toscrape.com/"] def parse(self, response): quotes = response.css('.quote') for quote in quotes: item = QuoteItem() text = quote.css('.text::text').extract_first() author = quote.css('.author::text').extract_first() tags = quote.css(".tags .tag::text").extract() item['text'] = text item['author'] = author item['tags'] = tags yield item
next = response.css('.pager .next a::attr(href)').extract_first() #css提取下一页 url = response.urljoin(next) #urljoin 做URL yield scrapy.Request(url=url,callback=self.parse) #callback 回调函数
这里面有几个知识点:
1.1 scrapy 里面的spiders 的QuoteSpider必须继承父类:scrapy.Spider。
1.2 css 提取器规则,要学会
1.3 xpath 方法提取也是可以的
1.4 urljoin方法:这个方法是用来构建绝对URL的方法。
python3对urllib和urllib2进行了重构,拆分成了urllib.request, urllib.response, urllib.parse, urllib.error等几个子模块,这样的架构从逻辑和结构上说更加合理。
urljoin现在对应的函数是urllib.parse.urljoin
所以引入urljoin 是:
from urllib.parse import urljoin
from urllib.parse import urljoin url1 = urljoin("http://www.baidu.com/1/aaa.html","bbbb.html") #直接替换url的最后一个'/' print(url1) url2 = urljoin("http://www.baidu.com/1/aaa.html","2/bbbb.html") print(url2) #直接替换url的最后一个'/' url3 = urljoin("http://www.baidu.com/1/aaa.html","/2/bbbb.html") print(url3) #直接替换url的最后二个'/' url4 = urljoin("http://www.baidu.com/1/aaa.html","http://www.baidu.com/3/ccc.html") print(url4) #第二个是完整URL就直接用第二个 url5 = urljoin("http://www.baidu.com/1/aaa.html","http://www.baidu.com/ccc.html") print(url5) #第二个是完整URL就直接用第二个 url6 = urljoin("http://www.baidu.com/1/aaa.html","javascript:void(0)") print(url6) #javascript直接用 执行结果: http://www.baidu.com/1/bbbb.html http://www.baidu.com/1/2/bbbb.html http://www.baidu.com/2/bbbb.html http://www.baidu.com/3/ccc.html http://www.baidu.com/ccc.html javascript:void(0)
1.5 parse方法:
parse(self,response):当请求url返回网页没有指定回调函数,默认的Request对象的回调函数,用来处理网页返回的response,和生成的Item或者Request对象,以下分析一下parse()方法的工作机制:
1.因为使用的yield,而不是return,parse函数将会当做一个生成器使用,scrapy会注意调用parse方法中生成的结果,并且判断该结果是一个什么样的类型
2.如果是request则会加入爬取队列中,如果是item类型则会使用pipeline处理,其他类型则会返回错误信息
3.scrapy取到第一部分的request不会立马就去发送request,只是将这个request放到队列中,然后接着从生成器中获取
4.取完了第一部分的request,然后再获取第二部分的item,取到item了,就会放到对应的pipeline中处理
5.parse方法作为回调函数(callback),赋值给Request,指定parse()方法处理这些请求scrapy.Request(url,callback=self.parse)
6.Request对象经过调度,执行生成scrapy.http.response()响应对象,并送回parse()方法,直到调度器中没有Requset(递归的思路)
7.取尽之后,parse()工作结束,引擎再根据对列和pipeline中的内容去执行相应的操作
8.程序在取得各个页面的items前,会先处理完之前所有的request对列的请求,然后再提取items
1.6 callback传参:可以用lambda,可以用Request.meta传参
items.py
import scrapy class QuoteItem(scrapy.Item): # define the fields for your item here like: text = scrapy.Field() author = scrapy.Field() tags = scrapy.Field()
这里面要了解 Field()这个对象:
①Field对象指明了每个字段的元数据(任何元数据),Field对象接受的值没有任何限制
②设置Field对象的主要目就是在一个地方定义好所有的元数据
③注意,声明item的Field对象,并没有被赋值成class属性。(可通过item.fields进行访问)
④Field类仅是内置字典类(dict)的一个别名,并没有提供额外的方法和属性。被用来基于类属性的方法来支持item生命语法。
class dict(object): """ dict() -> 创建空字典 dict(mapping) -> 从映射对象的键值对中初始化新字典{"key":value} dict(iterable) -> 从迭代器初始化字典 d[key] = value dict(**kwargs) -> 使用字典初始化新字典 """ # 1.清除字典内部所有内容 -> 空字典 def clear(self): pass # 2.字典dict的复制 def copy(self): """ dict_copy = D.copy()""" pass # 3.静态方法。返回一个新的字典,其中包含来自迭代器的键,以及对应的值 @staticmethod # known case def fromkeys(*args, **kwargs): # real signature unknown pass # 4.返回指定键k的值,如果值不在字典中返回d的值 def get(self, k, d=None): # real signature unknown; restored from __doc__ pass # 5.以字典形式返回可遍历的(键,值)对 def items(self): # real signature unknown; restored from __doc__ pass # 6.以列表形式返回一个字典所有的键 def keys(self): # real signature unknown; restored from __doc__ pass # 7.删除字典给定键k所对应的值,返回值为被删除的值。k值必须给出。否则,返回d值。 def pop(self, k, d=None): # real signature unknown; restored from __doc__ pass # 8.随机返回并删除字典中的一对键和值 def popitem(self): # real signature unknown; restored from __doc__ pass # 9.和get()类似, 但如果键k不存在于字典中,将会添加键并将值设为d def setdefault(self, k, d=None): # real signature unknown; restored from __doc__ pass # 10.将字典2中的键值对更新入调用字典中 def update(self, E=None, **F): # known special case of dict.update """ If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k] """ pass # 11.以字典的形式返回字典中所有的值 def values(self): # real signature unknown; restored from __doc__ """ D.values() -> an object providing a view on D's values """ pass # 12.内置方法。判断键是否在字典中,在返回True,不在返回False def __contains__(self, *args, **kwargs): # real signature unknown pass # 13.删除字典中对应k键的值 def __delitem__(self, *args, **kwargs): # real signature unknown """ Delete self[key]. """ pass # 14.返回存在Value值得字典 def __eq__(self, *args, **kwargs): # real signature unknown """ Return self==value. """ pass def __getattribute__(self, *args, **kwargs): # real signature unknown """ Return getattr(self, name). """ pass def __getitem__(self, y): # real signature unknown; restored from __doc__ """ x.__getitem__(y) <==> x[y] """ pass def __ge__(self, *args, **kwargs): # real signature unknown """ Return self>=value. """ pass def __gt__(self, *args, **kwargs): # real signature unknown """ Return self>value. """ pass def __init__(self, seq=None, **kwargs): # known special case of dict.__init__ pass def __iter__(self, *args, **kwargs): # real signature unknown """ Implement iter(self). """ pass def __len__(self, *args, **kwargs): # real signature unknown """ Return len(self). """ pass def __le__(self, *args, **kwargs): # real signature unknown """ Return self<=value. """ pass def __lt__(self, *args, **kwargs): # real signature unknown """ Return self<value. """ pass @staticmethod # known case of __new__ def __new__(*args, **kwargs): # real signature unknown """ Create and return a new object. See help(type) for accurate signature. """ pass def __ne__(self, *args, **kwargs): # real signature unknown """ Return self!=value. """ pass def __repr__(self, *args, **kwargs): # real signature unknown """ Return repr(self). """ pass def __setitem__(self, *args, **kwargs): # real signature unknown """ Set self[key] to value. """ pass def __sizeof__(self): # real signature unknown; restored from __doc__ """ D.__sizeof__() -> size of D in memory, in bytes """ pass __hash__ = None
pipelines.py
class TextPipeline(object): def __init__(self): self.limit = 50 def process_item(self, item, spider): if item['text']: if len(item['text']) > self.limit: item['text'] = item['text'][0:self.limit].rstrip() + '...' return item else: return DropItem('Missing Text')