浏览器本质,socket客户端遵循Http协议 HTTP协议本质:经过\r\n分割的规范,请求响应以后断开连接 ==> 短链接、无状态 具体: Http协议是创建在tcp/ip之上的,是一种规范,它规范定了发送的数据的数据格式, 然而这个数据格式是经过\r\n进行分割的,请求头与请求体也是经过2个\r\n分割的,响应的时候, 响应头与响应体也是经过\r\n分割,而且还规定已请求已响应就会断开连接,即-->短链接、无状态
websocket是给浏览器新建的一套(相似于http)协议,协议规定:浏览器和服务器链接以后不断开,以达到服务端向客户端主动推送消息。
本质: 建立一个链接后不断开的socket 当链接成功以后: 客户端(浏览器)会自动向服务端发送消息,包含:Sec-WebSocket-Key: iyRe1KMHi4S4QXzcoboMmw== 服务端接收以后,会对于该数据进行加密:base64(sha1(swk + magic_string)) 构造响应头: HTTP/1.1 101 Switching Protocols\r\n Upgrade:websocket\r\n Connection: Upgrade\r\n Sec-WebSocket-Accept: 加密后的值\r\n WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n 发给客户端(浏览器) 创建:双工通道,接下来就能够进行收发数据 发送数据是加密,解密,根据payload_len的值进行处理 payload_len <= 125 payload_len == 126 payload_len == 127 获取内容: mask_key 数据 根据mask_key和数据进行位运算,就能够把值解析出来。
客户端向服务端发送消息时,会有一个'sec-websocket-key'和'magic string'的随机字符串(魔法字符串), 服务端接收到消息后会把他们链接成一个新的key串,进行编码、加密,确保信息的安全性。
响应式布局是经过@media实现的 @media (min-width:768px){ .pg-header{ background-color:green; } } @media (min-width:992px){ .pg-header{ background-color:pink; } } 代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <style> body{ margin: 0; } .pg-header{ background-color: red; height: 48px; } @media (min-width: 768px) { .pg-header{ background-color: aqua; } } @media (min-width: 992px) { .pg-header{ background-color: blueviolet; } } </style> </head> <body> <div class="pg-header"></div> </body> </html>
- jQuery - Bootstrap - Vue.js(与vue齐名的前端框架React和Angular)
http://www.javashuo.com/article/p-efmrdpct-u.htmljavascript
轮询:经过定时器让程序每隔n秒执行一次操做。css
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <h1>请选出最帅的男人</h1> <ul> {% for k,v in gg.items() %} <li>ID:{{ k }}, 姓名:{{ v.name }} ,票数:{{ v.count }}</li> {% endfor %} </ul> <script> setInterval(function () { location.reload(); },2000) </script> </body> </html>
客户端向服务器发送请求,服务器接到请求后hang住链接,等待30秒,直到有新消息,才返回响应信息并关闭链接,客户端处理完响应信息后再向服务器发送新的请求。html
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <h1>请选出最帅的男人</h1> <ul> {% for k,v in gg.items() %} <li style="cursor: pointer" id="user_{{ k }}" ondblclick="vote({{ k }});">ID:{{ k }}, 姓名:{{ v.name }} ,票数:<span>{{ v.count }}</span></li> {% endfor %} </ul> <script src="/static/jquery-3.3.1.min.js"></script> <script> $(function () { get_new_count(); }); function get_new_count() { $.ajax({ url: '/get_new_count', type:'GET', dataType:'JSON', success:function (arg) { if (arg.status){ // 更新票数 var gid = "#user_" + arg.data.gid; $(gid).find('span').text(arg.data.count); }else{ // 10s内没有人投票 } get_new_count(); } }) } function vote(gid) { $.ajax({ url: '/vote', type:'POST', data:{gid:gid}, dataType:"JSON", success:function (arg) { } }) } </script> </body> </html>
多组件之间共享:vuex 补充luffyvue 1:router-link / router-view 2:双向绑定,用户绑定v-model 3:循环展现课程:v-for 4:路由系统,添加动态参数 5:cookie操做:vue-cookies 6:多组件之间共享:vuex 7:发送ajax请求:axios (js模块)
vue-resource的interceptors拦截器的做用正是解决此需求的妙方。
在每次http的请求响应以后,若是设置了拦截器以下,会优先执行拦截器函数,获取响应体,而后才会决定是否把response返回给then进行接收
发送ajax请求:axios (js模块)
一、v-show指令:条件渲染指令,不管返回的布尔值是true仍是false,元素都会存在在html中,只是false的元素会隐藏在html中,并不会删除.
二、v-if指令:判断指令,根据表达式值得真假来插入或删除相应的值。
三、v-else指令:配合v-if或v-else使用。
四、v-for指令:循环指令,至关于遍历。
五、v-bind:给DOM绑定元素属性。
六、v-on指令:监听DOM事件。前端
JSONP 是json用来跨域的一个东西。原理是经过script标签的跨域特性来绕过同源策略。 JSONP的简单实现:建立一个回调函数,而后在远程服务上调用这个函数而且将JSON数据做为参数传递,完成回调。
CORS:跨域资源共享(CORS,Cross-Origin Resource Sharing),随着技术的发展,如今的浏览器能够支持主动设置从而容许跨域请求,其本质是设置响应头,使得浏览器容许跨域请求。
浏览器将CORS请求分红两类:简单请求和复杂请求 简单请求(同时知足如下两大条件) (1)请求方法是如下三种方法之一: HEAD GET POST (2)HTTP的头信息不超出如下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 凡是不一样时知足上面两个条件,就属于非简单请求
GET、POST、PUT、DELETE
PATCH(修改数据) HEAD(相似于get请求,只不过返回的响应中没有具体的内容,用于获取报头)
分类: 1** 信息,服务器收到请求,须要请求者继续执行操做 2** 成功,操做被成功接收并处理 3** 重定向,须要进一步的操做以完成请求 4** 客户端错误,请求包含语法错误或没法完成请求 5** 服务器错误,服务器在处理请求的过程当中发生了错误 常见的状态码 200 - 请求成功
202 - 已接受请求,还没有处理
204 - 请求成功,且不需返回内容 301 - 资源(网页等)被永久转移到其余url
400 - 请求的语义或是参数有错
403 - 服务器拒绝请求 404 - 请求资源(网页)不存在 500 - 内部服务器错误
502 - 网关错误,通常是服务器压力过大致使链接超时
503 - 因为超载或系统维护,服务器暂时的没法处理客户端的请求
- user-agent (代理) - host - referer - cookie - content-type
李杰vue
武沛奇java
老男孩python
undefinedmysql
武沛奇react
Alexjquery
- django,大而全的框架,它的内部组件比较多,内部提供:ORM、Admin、中间件、Form、ModelForm、Session、缓存、信号、CSRF;功能也至关完善。 - flask,微型框架,内部组件就比较少了,可是有不少第三方组件来扩展它,定制化程度高。
好比说有那个wtform(与django的modelform相似,表单验证)、flask-sqlalchemy(操做数据库的)、flask-session、flask-migrate、flask-script、blinker可扩展强,第三方组件丰富。因此对他自己来讲有那种短小精悍的感受
- tornado,异步非阻塞。 django和flask的共同点就是,他们2个框架都没有写socket,因此他们都是利用第三方模块wsgi。 可是内部使用的wsgi也是有些不一样的:django自己运行起来使用wsgiref,而flask使用werkzeug wsgi 还有一个区别就是他们的请求管理不太同样:django是经过将请求封装成request对象,再经过参数传递,而flask是经过上下文管理机制。
Tornado:
是一个轻量级的Web框架,异步非阻塞+内置WebSocket功能。
目标:经过一个线程处理N个并发请求(处理IO)。
内部组件
#内部本身实现socket
#路由系统
#视图
#模板
#cookie
#csrf
是web服务网关接口,是一套协议。 是经过如下模块实现了wsgi协议: - wsgiref - werkzurg - uwsgi 关于部署 以上模块本质:编写socket服务端,用于监听请求,当有请求到来,则将请求数据进行封装,而后交给web框架处理。
用户请求 --> wsgi --> jango的中间件(方法process_request) --> url路由匹配 --> 视图 --> orm数据库操做 --> 模板渲染
--> 中间件(方法process_response) --> wsgi -->用户
表单form:
- 对用户请求的数据进行校验 - 生成HTML标签 PS: - form对象是一个可迭代对象。 - 问题:如何实现choice的数据实时更新?(动态数据,而不是写死) - 解决:给该字段定义成ModelChoiceField的时候利用好"queryset"参数
class UserForm(Form): ut_id = ModelChoiceField(queryset=models.UserType.objects.all()) # 从另外一张依赖表中提取数据 依赖表: class UserType(models.Model): title = models.CharField(max_length=32)
信号signal:
django的信号其实就是django内部为开发者预留的一些自定制功能的钩子。 只要在某个信号中注册了函数,那么django内部执行的过程当中就会自动触发注册在信号中的函数。 如:
场景: 在数据库某些表中添加数据时,能够进行日志记录。
中间件middleware:
对全部的【请求】进行【批量】处理,说得直白一点中间件是帮助咱们在视图函数执行以前和执行以后均可以作一些额外的操做,它本质上就是一个自定义类。其影响的是全局,需谨慎使用。
应用:用户登陆校验
问题:为甚么不使用装饰器?
若是不使用中间件,就须要给每一个视图函数添加装饰器,太繁琐。
权限:
用户登陆后,将权限放到session中,而后再每次请求进来在中间件里,根据当前的url去session中匹配,
判断当前用户是否有权限访问当前url,有权限就继续访问,没有就返回, 检查的东西就能够放到中间件中进行统一处理,在process_request方法里面作的, 咱们的中间件是放在session后面,由于中间件须要到session里面取数据。
会话session:
cookie与session区别 (a)cookie是保存在浏览器端的键值对,而session是保存的服务器端的键值对,可是依赖cookie。(也能够不依赖cookie,能够放在url,或请求头可是cookie比较方便) (b)以登陆为例,cookie为经过登陆成功后,设置明文的键值对,并将键值对发送客户端存,明文信息可能存在泄漏,不安全;
session则是生成随机字符串,发给用户,并写到浏览器的cookie中,同时服务器本身也会保存一份。 (c)在登陆验证时,cookie:根据浏览器发送请求时附带的cookie的键值对进行判断,若是存在,则验证经过;
session:在请求用户的cookie中获取随机字符串,根据随机字符串在session中获取其对应的值进行验证
跨域请求cors(场景:先后端分离时,本地测试开发时使用):
若是网站之间存在跨域,域名不一样,端口不一样会致使出现跨域,但凡出现跨域,浏览器就会出现同源策略的限制。 解决:在咱们的服务端给咱们响应数据,加上响应头 --> 在中间件加的。
缓存cache:
经常使用的数据放在缓存里面,就不用走视图函数,请求进来经过全部的process_request,会到缓存里面查数据,有就直接拿,没有就走视图函数。
关键点:1:执行完全部的process_request才去缓存取数据
2:执行完全部的process_response才将数据放到缓存
关于缓存问题 1:为何放在最后一个process_request才去缓存? 由于须要验证完用户的请求,才能返回数据 2:何时将数据放到缓存中? 第一次走中间件,缓存没有数据,会走视图函数,取数据库里面取数据, 当走完process_response,才将数据放到缓存里,由于,走process_response的时候可能给咱们的响应加处理。
3:为何使用缓存?
将经常使用且不太频繁修改的数据放入缓存。
之后用户再来访问,先去缓存查看是否存在,若是有就返回
不然,去数据库中获取并返回给用户(再加入到缓存,以便下次访问)
CSRF-TOKEN:
目标:防止用户直接向服务端发起POST请求。
对全部的post请求作验证,将jango生成的一串字符串发送给后台,一种是从请求体发过来,一种是放在隐藏的标签里面。
方案:先发送GET请求时,将token保存到:cookie、Form表单中(隐藏的input标签),
之后再发送请求时只要携带过来便可。
# 这些方法中的参数都是与视图函数参数对应的
process_request(self, request) 主要方法。请求刚进来时,执行视图函数以前调用。(无return) process_view(self, request, callback, callback_args, callback_kwargs) URL路由匹配成功后,执行视图函数以前调用,拿到视图函数对象,及其全部参数。(无return) process_exception(self, request, exception) 执行视图函数中遇到异常时调用。(无return) process_template_response(self, request, response) 不多用。执行了render()渲染方法后调用。(有return) process_response(self, request, response) 主要方法。执行视图函数结束以后有响应时调用。(有return)
执行流程
FBV 函数视图 # FBV 写法 # urls.py url(r'^login/$',views.login, name="login"), # views.py def login(request): if request.method == "POST": print(request.POST) return render(request,"login.html") CBV 类视图 # urls.py url(r'^login/$',views.Login.as_view(), name="login"), # views.py from django.views import View class Login(View): # 类首字母大写 def get(self,request): return render(request, "login.html") def post(self,request): print(request.POST) return HttpResponse("OK")
- 没什么区别,由于他们的本质都是函数。CBV的.as_view()返回的view函数,view函数中调用类的dispatch方法, 在dispatch方法中经过反射执行get/post/delete/put等方法。 - 非要说区别的话: CBV比较简洁,GET/POST等业务功能分别放在不一样get/post函数中。FBV本身作判断进行区分。
当请求一个页面时, Django会创建一个包含请求元数据的HttpRequest对象。
当Django加载对应的视图时, HttpRequest对象将做为视图函数的第一个参数,另外每一个视图会返回一个HttpResponse对象。
利用方法装饰器"method_decorator":
def auth(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner class UserView(View): @method_decorator(auth) def get(self,request,*args,**kwargs): return HttpResponse('...')
1.返回QuerySet对象的方法: all() filter() exclude() order_by() reverse() distinct()
select_related()
prefetch_related()
only()
defer()
using() 特殊的QuerySet: values() (返回一个字典序列) values_list() (返回一个元组序列)
2.不返回QuerySet,而返回具体对象的方法: get() first() last()
earliest()
latest()
update()
delete() 返回布尔值的方法有: exists() 返回数字的方法有: count()
only()查询指定的字段,defer()查询排除指定的字段。
# 相同点:它俩都用于连表查询,缓存查询结果,减小SQL查询次数。
# 不一样点: select_related 主要针对一对一和一对多关系进行优化。经过多表join关联查询,一次性得到全部数据,缓存在内存中,但若是关联的表太多,会严重影响数据库性能。 prefetch_related 主要针对多对多关系进行优化。经过分表,先获取各个表的数据,缓存在内存中,而后经过Python处理他们之间的关联。
filter(self, *args, **kwargs) # 条件查询(符合条件) # 查出符合条件 # 条件能够是:参数,字典,Q exclude(self, *args, **kwargs) # 条件查询(排除条件) # 排除不想要的 # 条件能够是:参数,字典,Q
靠近原生SQL --> extra()、raw() - extra def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(10,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(10,), order_by=['-nid']) - raw def raw(self, raw_query, params=None, translations=None, using=None): # 执行原生SQL models.UserInfo.objects.raw('select * from userinfo') # 若是SQL是其余表时,必须将名字设置为当前UserInfo对象的主键列名 models.UserInfo.objects.raw('select id as nid,name as title from 表') # 为原生SQL设置参数 models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) # 将获取的到列名转换为指定列名 name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) # 指定数据库 models.UserInfo.objects.raw('select * from userinfo', using="default")
步骤一:写配置文件 class Router1: # 指定到某个数据库读数据 def db_for_read(self, model, **hints): if model._meta.model_name == 'usertype': return 'db1' else: return 'default'
# 指定到某个数据库写数据 def db_for_write(self, model, **hints): return 'default'
再写到配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'db1': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } DATABASE_ROUTERS = ['db_router.Router1',]
步骤二:手动使用queryset的using方法 def index(request): models.UserType.objects.using('db1').create(title='普通用户') # 手动指定去某个数据库取数据 result = models.UserType.objects.all().using('db1') return HttpResponse('...')
F:主要用来对字段的值进行四则计算。
Goods.objects.update(price=F("price")+10) # 对于goods表中每件商品的价格都在原价格的基础上增长10元
Q:用来进行复杂查询,实现"与"、"或"、"非"查询。
Q(条件1) | Q(条件2) # 或
Q(条件1) & Q(条件2) # 且
~ Q(条件) # 非
def values(self, *fields): # 返回每行数据为字典格式 def values_list(self, *fields, **kwargs): # 返回每行数据为元组格式
# bulk_create(objs, batch_size=None):批量插入 # batch_size表示一次插入的个数 objs = [ models.DDD(name='r11'), models.DDD(name='r22') ] models.DDD.objects.bulk_create(objs, 10)
- 做用: - 对用户请求数据格式进行校验 - 自动生成HTML标签 - 区别: - Form,字段须要本身手写。 class Form(Form): xx = fields.IntegerField(.) xx = fields.CharField(.) xx = fields.EmailField(.) xx = fields.ImageField(.)
- ModelForm,能够经过Meta进行定义 class MForm(ModelForm): class Meta:
model = UserInfo fields = "__all__" - 应用:只要是客户端向服务端发送表单数据时,均可以进行使用,如:用户登陆注册
方式一:重写初始化方法,在构造方法中从新去数据库获取值 class UserForm(Form): ut_id = fields.ChoiceField(choices=())
def __init__(self, *args, **kwargs): super(UserForm, self).__init__(*args, **kwargs) self.fields['ut_id'].choices = models.UserType.objects.all().values_list('id', 'title')
方式二: ModelChoiceField字段 class UserForm(Form): ut_id = ModelChoiceField(queryset=models.UserType.objects.all()) # 从另外一张依赖表中提取数据 依赖表: class UserType(models.Model): title = models.CharField(max_length=32)
在django2.0后,定义外键和一对一关系的时候须要加on_delete选项,此参数为了不两个表里的数据不一致问题,否则会报错: user=models.OneToOneField(User,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值 owner=models.ForeignKey(UserProfile,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值
参数说明: on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值 CASCADE:级联删除。 PROTECT:报完整性错误。 SET_NULL:把外键设置为null,前提是容许为null。 SET_DEFAULT:把设置为外键的默认值。 SET():调用外面的值,能够是一个函数。 通常状况下使用CASCADE就能够了。
目的:防止用户直接向服务端发起POST请求
- 用户先发送GET获取令牌csrf token: Form表单中一个隐藏的标签 + token; - 发起POST请求时,须要携带这个令牌csrf token; - 在中间件的process_view方法中进行令牌校验。 在html中添加{%csrf_token%}标签。
django中能够经过channel实现websocket。
http://www.javashuo.com/article/p-ropuwivn-p.html
# 使用Django的信号机制,能够在添加、删除数据先后设置日志记录: pre_init # Django中的model对象执行其构造方法前,自动触发 post_init # Django中的model对象执行其构造方法后,自动触发 pre_save # Django中的model对象保存前,自动触发 post_save # Django中的model对象保存后,自动触发 pre_delete # Django中的model对象删除前,自动触发 post_delete # Django中的model对象删除后,自动触发
# 使用
@receiver(post_save, sender=Myclass) # 信号接收装饰器。因为内置信号,因此直接接收
def signal_handler(sender, **kwargs): # 接收到信号后,在此处理
logger = logging.getLogger()
logger.success('保存成功')
Django中提供了6种缓存方式: 开发调试(默认缓存) 内存 文件 数据库 Memcache缓存 第三方库支持redis:django-redis
设置缓存: # 全站缓存(中间件) MIDDLEWARE = [ ‘django.middleware.cache.UpdateCacheMiddleware’, #第一个位置 'django.middleware.common.CommonMiddleware', ‘django.middleware.cache.FetchFromCacheMiddleware’, #最后位置 ]
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', # 取决于您选择的Memcached绑定 'LOCATION': ['127.0.0.1:11211', ], # 缓存后端服务器位置,支持分布式,可多个 'TIMEOUT': 5 * 60, # 缓存超时,默认300s } }
# 视图缓存 @cache_page(15) #超时时间为15秒 def index(request): t=time.time() #获取当前时间 return render(request,"index.html",locals())
# 模板缓存 {% load cache %}
{% cache 2 'name' %} # 存的key <h3>缓存:{{ t }}</h3> {% endcache %}
# 在setting添加配置文件
# 配置中间件同上。 CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", # 缓存类型 "LOCATION": "127.0.0.1:6379", # 缓存服务器IP和端口 "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100} # 链接池最大链接数 # "PASSWORD": "123456", } } } # 使用 def index(request): conn = get_redis_connection("default") # 根据名字去链接池中获取链接 conn.hset('n1','k1','v1') # 存数据 return HttpResponse('...')
反向解析路由字符串.
url(r'^home', views.home, name='home')
在模板中使用:{ % url 'home' %}
在视图中使用:reverse('home')
filter: 过滤器,只能接受两个参数,第一个参数是|前的数据。 用于操做变量。 simple_tag: 标签(简单标签)。 用于操做模板模块。
1、查看访问的速度、数据库的行为、cache命中等信息。 2、尤为在Mysql访问等的分析上大有用处(sql查询速度).
单元测试是class类(继承TestCase),每个测试方法必须以"test"开头。你能够重写setUp()(测试开始以前的操做)和tearDown()(测试结束以后的操做)方法。
经常使用的断言方法:assertEqual()。 会单独新建一个测试数据库来进行数据库的操做方面的测试,垃圾数据默认在测试完成后销毁。 Django单元测试时为了模拟生产环境,会修改settings中的变量,例如, 把DEBUG变量修改成True, 把ALLOWED_HOSTS修改成[*]。
db first: 先建立库,再更新表 code first:先建立表,再更新库
一、修改seting文件,在setting里面设置要链接的数据库类型和名称、地址
二、运行下面代码能够自动生成models模型文件
- python manage.py inspectdb > app/models.py # inspectdb 监测数据库的意思
SQL: # 优势: 执行速度快 # 缺点: 编写复杂,开发效率不高 ---------------------------------------------------- ORM: # 优势: 让用户再也不写SQL语句,提升开发效率 能够很方便地引入数据缓存之类的附加功能 # 缺点: 在处理多表联查、where条件复杂查询时,ORM的语法会变得复杂。 没有原生SQL速度快
MVC:model、view(显示)、controller(视图) MTV:model、tempalte、view
两者本质没有区别。
contenttype是Django的一个组件(app),它能够将django下全部app下的表记录下来。 可使用他再加上表中的两个字段,实现一张表和N张表动态建立FK关系。 - 字段:表名称 - 字段:数据行ID
Restful其实就是一套编写接口的'风格规范',规定如何编写以及如何设置返回值、状态码等信息。 最显著的特色: # 用Restful: 给用户一个url,再根据不一样的method在后端作不一样的处理 好比:post建立数据、get获取数据、put和patch修改数据、delete删除数据。 # 不用Restful: 给调用者不少url,每一个url表明一个功能,好比:add_user/delte_user/edit_user/ # 固然,还有其余的,好比: '版本' 来控制让程序有多个版本共存的状况,版本能够放在 url、请求头(accept/自定义)、GET参数 '状态码' 200/300/400/500 'url中尽可能使用名词' restful也能够称为“面向资源编程” 'api标示' api.luffycity.com www.luffycity.com/api/
第一次访问一个接口后,再对该接口进行N次相同的访问时,对资源不造影响,就认为接口具备幂等性。 GET, #第一次获取结果、第二次也是获取结果对资源都不会形成影响,幂等。 POST, #第一次新增数据,第二次也会再次新增,非幂等。 PUT, #第一次更新数据,第二次不会再次更新,幂等。 PATCH,#第一次更新数据,第二次不会再次更新,非幂等。 DELTE,#第一次删除数据,第二次不在再删除,幂等。
'远程过程调用协议'。 是一种经过网络从远程计算机程序上请求服务,而不须要了解底层网络技术的协议。 进化的顺序: 现有的RPC,而后有的RESTful规范
#Http: 80端口 #https: 443端口 #http信息是明文传输,https则是具备安全性的ssl加密传输协议。 #- 自定义证书 - 服务端:建立一对证书 - 客户端:必须携带证书 #- 购买证书 - 服务端: 建立一对证书,。。。。 - 客户端: 去机构获取证书,数据加密后发给我们的服务单 - 证书机构:公钥给改机构
# 在编写接口时能够不使用django rest framework框。 # 不使用:也能够作,能够用django的CBV来实现,开发者编写的代码会更多一些。 # 使用:内部帮助咱们提供了不少方便的组件,咱们经过配置就能够完成相应操做,如: '序列化'能够作用户请求数据校验+queryset对象的序列化称为json '解析器'获取用户请求数据request.data,会自动根据content-type请求头的不能对数据进行解析 '分页'将从数据库获取到的数据在页面进行分页显示 #还有其余组件:'认证'、'权限'、'访问频率控制'
#- 路由,自动帮助开发者快速为一个视图建立4个url www.oldboyedu.com/api/v1/student/$ www.oldboyedu.com/api/v1/student(?P<format>\w+)$ www.oldboyedu.com/api/v1/student/(?P<pk>\d+)/$ www.oldboyedu.com/api/v1/student/(?P<pk>\d+)(?P<format>\w+)$ #- 版本处理 - 问题:版本均可以放在那里? - url - GET - 请求头 #- 认证 - 问题:认证流程? #- 权限 - 权限是否能够放在中间件中?以及为何? #- 访问频率的控制 匿名用户能够真正的防止?没法作到真正的访问频率控制,只能把小白拒之门外。 若是要封IP,使用防火墙来作。 登陆用户能够经过用户名做为惟一标示进行控制,若是有人注册不少帐号,则没法防止。 #- 视图 #- 解析器 ,根据Content-Type请求头对请求体中的数据格式进行处理。request.data #- 分页 #- 序列化 - 序列化 - source - 定义方法 - 请求数据格式校验 #- 渲染器
a. 继承APIView(最原始)但定制性比较强 这个类属于rest framework中的顶层类,内部帮助咱们实现了只是基本功能:认证、权限、频率控制, 但凡是数据库、分页等操做都须要手动去完成,比较原始。 class GenericAPIView(APIView) def post(...): pass b.继承GenericViewSet(ViewSetMixin,generics.GenericAPIView) 首先他的路由就发生变化 若是继承它以后,路由中的as_view须要填写对应关系 在内部也帮助咱们提供了一些方便的方法: get_queryset get_object get_serializer get_serializer_class get_serializer_context filter_queryset 注意:要设置queryset字段,不然会抛出断言的异常。 代码 只提供增长功能 只继承GenericViewSet class TestView(GenericViewSet): serialazer_class = xxx def creat(self,*args,**kwargs): pass # 获取数据并对数据 c. 继承 modelviewset --> 快速快发 -ModelViewSet(增删改查全有+数据库操做) -mixins.CreateModelMixin(只有增),GenericViewSet -mixins.CreateModelMixin,DestroyModelMixin,GenericViewSet 对数据库和分页等操做不用咱们在编写,只须要继承相关类便可。 示例:只提供增长功能 class TestView(mixins.CreateModelMixin,GenericViewSet): serializer_class = XXXXXXX *** modelviewset --> 快速开发,复杂点的genericview、apiview
- 如何编写?写类并实现authenticators 请求进来认证须要编写一个类,类里面有一个authenticators方法,咱们能够自定义这个方法,能够定制3类返回值。 成功返回元组,返回none为匿名用户,抛出异常为认证失败。 源码流程:请求进来先走dispatch方法,而后封装的request对象会执行user方法,由user触发authenticators认证流程 - 方法中能够定义三种返回值: - (user,auth),认证成功 - None , 匿名用户 - 异常 ,认证失败 - 流程: - dispatch - 再去request中进行认证处理
# 对匿名用户,根据用户IP或代理IP做为标识进行记录,为每一个用户在redis中建一个列表 { throttle_10.1.1.1:[1526868876.497521, 152686885.497521, ...], throttle_10.1.1.2:[1526868876.497521, 152686885.497521, ...], throttle_10.1.1.3:[1526868876.497521, 152686885.497521, ...], } 每一个用户再来访问时,先去记录中剔除过时记录,再根据列表的长度判断是否能够继续访问。 '如何封IP':在防火墙中进行设置 -------------------------------------------------------------------------- # 对注册用户,根据用户名或邮箱进行判断。 { throttle_xxxx1:[1526868876.497521, 152686885.497521, ...], throttle_xxxx2:[1526868876.497521, 152686885.497521, ...], throttle_xxxx3:[1526868876.497521, 152686885.497521, ...], } 每一个用户再来访问时,先去记录中剔除过时记录,再根据列表的长度判断是否能够继续访问。 如1分钟:40次,列表长度限制在40,超过40则不可访问
Flask自由、灵活,可扩展性强,透明可控,第三方库的选择面广。
# 依赖jinja2模板引擎 # 依赖werkzurg协议
# blueprint把实现不一样功能的module分开.也就是把一个大的App分割成各自实现不一样功能的module. # 在一个blueprint中能够调用另外一个blueprint的视图函数, 但要加相应的blueprint名.
# Flask组件 flask-session session放在redis flask-SQLAlchemy 如django里的ORM操做 flask-migrate 数据库迁移 flask-script 自定义命令 blinker 信号-触发信号 # 第三方组件 Wtforms 快速建立前端标签、文本校验 dbutile 建立数据库链接池 gevnet-websocket 实现websocket # 自定义Flask组件 自定义auth认证 参考flask-login组件
# a、简单来讲,falsk上下文管理能够分为三个阶段: 一、'请求进来时':将请求相关的数据放入上下问管理中 二、'在视图函数中':要去上下文管理中取值 三、'请求响应':要将上下文管理中的数据清除 # b、详细点来讲: 一、'请求刚进来': 将request,session封装在RequestContext类中 app,g封装在AppContext类中 并经过LocalStack将requestcontext和appcontext放入Local类中 二、'视图函数中': 经过localproxy--->偏函数--->localstack--->local取值 三、'请求响应时': 先执行save.session()再各自执行pop(),将local中的数据清除
# g是贯穿于一次请求的全局变量,当请求进来将g和current_app封装为一个APPContext类; # 再经过LocalStack将Appcontext放入Local中,取值时经过偏函数在LocalStack、local中取值; # 响应时将local中的g数据删除;
RequestContext #封装进来的请求(赋值给ctx) AppContext #封装app_ctx LocalStack #将local对象中的数据维护成一个栈(先进后出) Local #保存请求上下文对象和app上下文对象
# 由于经过维护成列表,能够实现一个栈的数据结构,进栈出栈时只取一个数据,巧妙的简化了问题。 # 还有,在多app应用时,能够实现数据隔离;列表里不会加数据,而是会生成一个新的列表 # local是一个字典,字典里key(stack)是惟一标识,value是一个列表
请求进来时,能够根据URL的不一样,交给不一样的APP处理。蓝图也能够实现。 #app1 = Flask('app01') #app2 = Flask('app02') #@app1.route('/index') #@app2.route('/index2') 源码中在DispatcherMiddleware类里调用app2.__call__, 原理其实就是URL分割,而后将请求分发给指定的app。 以后app也按单app的流程走。就是从app.__call__走。
gevent-websocket
#快速建立前端标签、文本校验;如django的ModelForm
# 前提: 不熟的话:记不太清了,应该是……分两个阶段吧 # 建立: 当请求刚进来的时候,会将request和session封装成一个RequestContext()对象, 接下来把这个对象经过LocalStack()放入内部的一个Local()对象中; 由于刚开始 Local 的ctx中session是空的; 因此,接着执行open_session,将cookie 里面的值拿过来,从新赋值到ctx中 (Local实现对数据隔离,相似threading.local) # 销毁: 最后返回时执行 save_session() 将ctx 中的session读出来进行序列化,写到cookie 而后给用户,接着把 ctx pop掉
# a.threading.local 做用:为每一个线程开辟一块空间进行数据存储(数据隔离)。 问题:本身经过字典建立一个相似于threading.local的东西。 storage = { 4740: {val: 0}, 4732: {val: 1}, 4731: {val: 3}, } # b.自定义Local对象 做用:为每一个线程(协程)开辟一块空间进行数据存储(数据隔离)。 class Local(object): def __init__(self): object.__setattr__(self, 'storage', {}) def __setattr__(self, k, v): ident = get_ident() if ident in self.storage: self.storage[ident][k] = v else: self.storage[ident] = {k: v} def __getattr__(self, k): ident = get_ident() return self.storage[ident][k] obj = Local() def task(arg): obj.val = arg obj.xxx = arg print(obj.val) for i in range(10): t = Thread(target=task, args=(i,)) t.start()
# flask中的信号blinker 信号主要是让开发者但是在flask请求过程当中定制一些行为。 或者说flask在列表里面预留了几个空列表,在里面存东西。 简言之,信号容许某个'发送者'通知'接收者'有事情发生了
@before_request有返回值,blinker没有返回值
# 10个信号
request_started = _signals.signal('request-started') #请求到来前执行
request_finished = _signals.signal('request-finished') #请求结束后执行
before_render_template = _signals.signal('before-render-template') #模板渲染前执行
template_rendered = _signals.signal('template-rendered') #模板渲染后执行
got_request_exception = _signals.signal('got-request-exception') #请求执行出现异常时执行
request_tearing_down = _signals.signal('request-tearing-down') #请求执行完毕后自动执行(不管成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down') #请求上下文执行完毕后自动执行(不管成功与否)
appcontext_pushed = _signals.signal('appcontext-pushed') #请求app上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') #请求上下文pop时执行
message_flashed = _signals.signal('message-flashed')#调用flask在其中添加数据时,自动触发
# Session: 因为没法提供线程共享功能,开发时要给每一个线程都建立本身的session 打印sesion可知他是sqlalchemy.orm.session.Session的对象 # scoped_session: 为每一个线程都建立一个session,实现支持线程安全 在整个程序运行的过程中,只存在惟一的一个session对象。 建立方式: 经过本地线程Threading.Local() # session=scoped_session(Session) 建立惟一标识的方法(参考flask请求源码)
# 使用execute方法直接操做SQL语句(导入create_engin、sessionmaker) engine = create_engine('mysql://root:*****@127.0.0.1/database?charset=utf8') DB_Session = sessionmaker(bind=engine) session = DB_Session() session.execute('alter table mytablename drop column mycolumn ;')
# ORM的实现基于一下三点 映射类:描述数据库表结构, 映射文件:指定数据库表和映射类之间的关系 数据库配置文件:指定与数据库链接时须要的链接信息(数据库、登陆用户名、密码or链接字符串)
# 数据库链接池 使用模式: 一、为每一个线程建立一个链接,链接不可控,须要控制线程数 二、建立指定数量的链接在链接池,当线程访问的时候去取,不够了线程排队,直到有人释放(推荐) --------------------------------------------------------------------------- 两种写法: 一、用静态方法装饰器,经过直接执行类的方法来链接使用数据库 二、经过实例化对象,经过对象来调用方法执行语句 https://www.cnblogs.com/ArmoredTitan/p/Flask.html
如下SQLAlchemy的字段是否正确?若是不正确请更正:
from datetime import datetime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, DateTime Base = declarative_base() class UserInfo(Base): __tablename__ = 'userinfo' id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(64), unique=True) ctime = Column(DateTime, default=datetime.now()) ----------------------------------------------------------------------- 更正: ctime 字段中参数应为’default=datetime.now’,now后面不该该加括号,加了的话,字段不会实时更新。
1. 设置引擎编码方式为utf8。 engine = create_engine("mysql+pymysql://user:password@127.0.0.1:3306/db01?charset=utf8") 2. 设置数据库表编码方式为utf8 class UserType(Base): __tablename__ = 'usertype' id = Column(Integer, primary_key=True) caption = Column(String(50), default='管理员') # 添加配置 __table_args__ = { 'mysql_charset': 'utf8' } 这样生成的SQL语句就自动设置数据表编码为utf8了,__table_args__还可设置存储引擎、外键约束等等信息。
经过'UniqueConstraint'字段来设置联合惟一索引 __table_args__ = {
UniqueConstraint('hid', 'username', name='hid_username_i')
} #hid和username组成联合惟一约束。
异步非阻塞+websocket
# 实现异步非阻塞 视图函数yield一个future对象,future对象默认: self._done = False ,请求未完成 self._result = None ,请求完成后返回值,用于传递给回调函数使用。 tornado就会一直去检测future对象的_done是否已经变成True。 若是IO请求执行完毕,自动会调用future的set_result方法: self._result = result self._done = True 参考:http://www.cnblogs.com/wupeiqi/p/6536518.html(自定义异步非阻塞web框架)
Tornado在websocket模块中提供了一个WebSocketHandler类。 这个类提供了和已链接的客户端通讯的WebSocket事件和方法的钩子。 当一个新的WebSocket链接打开时,open方法被调用, 而on_message和on_close方法,分别在链接、接收到新的消息和客户端关闭时被调用。 此外,WebSocketHandler类还提供了write_message方法用于向客户端发送消息,close方法用于关闭链接。
# settings.py settings = { "static_path": os.path.join(os.path.dirname(__file__), "static"), # 指定了静态文件的位置在当前目录中的"static"目录下 "cookie_secret": "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=", "login_url": "/login", "xsrf_cookies": True, } 经上面配置后 static_url()自动去配置的路径下找'commons.css'文件
torndb torndb是基于mysqldb的再封装,因此使用时要先安装myqldb
tornado-redis
web聊天室,在线投票
'git stash':将当前工做区全部修改过的内容存储到“某个地方”,将工做区还原到当前版本未修改过的状态 'git stash list':查看“某个地方”存储的全部记录 'git stash clear':清空“某个地方” 'git stash pop':将第一个记录从“某个地方”从新拿到工做区(可能有冲突) 'git stash apply':编号, 将指定编号记录从“某个地方”从新拿到工做区(可能有冲突) 'git stash drop':编号,删除指定编号的记录
merge: 会将不一样分支的提交合并成一个新的节点,以前的提交分开显示, 注重历史信息、能够看出每一个分支信息,基于时间点,遇到冲突,手动解决,再次提交 rebase: 将两个分支的提交结果融合成线性,不会产生新的节点; 注重开发过程,遇到冲突,手动解决,继续操做
一、大家公司的代码review分支怎么作?谁来作? 答:组长建立review分支,咱们小功能开发完以后,合并到review分支交给老大(小组长)来看。 1.一、你组长不开发代码吗? 他开发代码,可是它只开发核心的东西,任务比较少 或者抽出时间,咱们一块儿作这个事情。 二、大家公司协同开发是怎么协同开发的? 每一个人都有本身的分支,阶段性代码完成以后,合并到review,而后交给老大看。 -------------------------------------------------------------------------- # 大体工做流程: 在公司: 下载代码 git clone https://gitee.com/wupeiqi/xianglong.git 或建立目录 cd 目录 git init git remote add origin https://gitee.com/wupeiqi/xianglong.git git pull origin maste 建立dev分支
git checkout -b dev git checkout dev git pull origin dev 继续写代码 git add . git commit -m '提交记录' git push origin dev 回到家中: 拉代码: git pull origin dev 继续写: 继续写代码 git add . git commit -m '提交记录' git push origin dev
https://blog.csdn.net/june_y/article/details/50817993
在命令行中,使用git tag –a tagname –m 'comment'能够快速建立一个标签。 须要注意,命令行建立的标签只存在本地Git库中,还须要使用Git push –tags指令发布到服务器的Git库中。
gitlab是公司本身搭建的项目代码托管平台。
一、gitHub是一个面向开源及私有软件项目的托管平台(建立私有的话,须要购买,最低级的付费为每个月7刀,支持5个私有项目) 二、gitlab是公司本身搭建的项目托管平台
一、fork须要协做项目 二、克隆/关联fork的项目到本地 三、新建分支(branch)并检出(checkout)新分支 四、在新分支上完成代码开发 五、开发完成后将你的代码合并到master分支 六、添加原做者的仓库地址做为一个新的仓库地址 七、合并原做者的master分支到你本身的master分支,用于和做者仓库代码同步 八、push你的本地仓库到GitHub 九、在Github上提交 pull requests 十、等待管理员(你须要贡献的开源项目管理员)处理
通常来讲每一个Git项目中都须要一个“.gitignore”文件, 这个文件的做用就是告诉Git哪些文件不须要添加到版本管理中。 实际项目中,不少文件都是不须要版本管理的,好比Python的.pyc文件和一些包含密码的配置文件等等。
'敏捷开发':是一种以人为核心、迭代、按部就班的开发方式。 它并非一门技术,而是一种开发方式,也就是一种软件开发的流程。 它会指导咱们用规定的环节去一步一步完成项目的开发。 由于它采用的是迭代式开发,因此这种开发方式的主要驱动核心是人
'Jenkins'是一个可扩展的持续集成引擎。 主要用于: 持续、自动地构建/测试软件项目。 监控一些定时执行的任务。
https://blog.csdn.net/zhailihua/article/details/7899006
为了预防消息丢失,rabbitmq提供了ack 即工做进程在收到消息并处理后,发送ack给rabbitmq,告知rabbitmq这时候能够把该消息从队列中删除了。 若是工做进程挂掉 了,rabbitmq没有收到ack,那么会把该消息 从新分发给其余工做进程。 不须要设置timeout,即便该任务须要很长时间也能够处理。 ack默认是开启的,工做进程显示指定了no_ack=True
一、建立队列和发送消息时将设置durable=Ture,若是在接收到消息尚未存储时,消息也有可能丢失,就必须配置publisher confirm channel.queue_declare(queue='task_queue', durable=True) 二、返回一个ack,进程收到消息并处理完任务后,发给rabbitmq一个ack表示任务已经完成,能够删除该任务 三、镜像队列:将queue镜像到cluster中其余的节点之上。 在该实现下,若是集群中的一个节点失效了,queue能自动地切换到镜像中的另外一个节点以保证服务的可用性
默认消息队列里的数据是按照顺序被消费者拿走, 例如:消费者1 去队列中获取奇数序列的任务,消费者2去队列中获取偶数序列的任务。 channel.basic_qos(prefetch_count=1) 表示谁来谁取,再也不按照奇偶数排列(同时也保证了公平的消费分发)
amqp协议中的核心思想就是生产者和消费者隔离,生产者从不直接将消息发送给队列。 生产者一般不知道是否一个消息会被发送到队列中,只是将消息发送到一个交换机。 先由Exchange来接收,而后Exchange按照特定的策略转发到Queue进行存储。 同理,消费者也是如此。Exchange 就相似于一个交换机,转发各个消息分发到相应的队列中。 -------------------------------------------------- type=fanout 相似发布者订阅者模式,会为每个订阅者建立一个队列,而发布者发布消息时,会将消息放置在全部相关队列中 type=direct 队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 断定应该将数据发送至指定队列。 type=topic 队列绑定几个模糊的关键字,以后发送者将数据发送到exchange,exchange将传入”路由值“和 ”关键字“进行匹配,匹配成功,
则将数据发送到指定队列。 --------------------------------------------------- 发送者路由值 队列中 old.boy.python old.* -- 不匹配 *表示匹配一个 old.boy.python old.# -- 匹配 #表示匹配0个或多个
# Celery是由Python开发的一个简单、灵活、可靠的处理大量任务的分发系统, # 它不只支持实时处理也支持任务调度。 # http://www.cnblogs.com/wupeiqi/articles/8796552.html
# celery实现定时任务 启用Celery的定时任务须要设置CELERYBEAT_SCHEDULE 。 CELERYBEAT_SCHEDULE='djcelery.schedulers.DatabaseScheduler'#定时任务 '建立定时任务' # 经过配置CELERYBEAT_SCHEDULE: #每30秒调用task.add from datetime import timedelta CELERYBEAT_SCHEDULE = { 'add-every-30-seconds': { 'task': 'tasks.add', 'schedule': timedelta(seconds=30), 'args': (16, 16) }, }
pro_cel ├── celery_tasks # celery相关文件夹 │ ├── celery.py # celery链接和配置相关文件 │ └── tasks.py # 全部任务函数 ├── check_result.py # 检查结果 └── send_task.py # 触发任务
# 通常状况使用的是从celeryapp中引入的app做为的装饰器:@app.task # django那种在app中定义的task则须要使用@shared_task
# 做用: 使用requests能够模拟浏览器的请求 # 经常使用参数: url、headers、cookies、data json、params、proxy # 经常使用返回值: content iter_content text encoding="utf-8" cookie.get_dict()
#BeautifulSoup 用于从HTML或XML文件中提取、过滤想要的数据形式 #经常使用方法 解析:html.parser 或者 lxml(须要下载安装) find、find_all、text、attrs、get
http://www.javashuo.com/article/p-rbbucvxs-c.html
Selenium是一个用于Web应用程序测试的工具, 他的测试直接运行在浏览器上,模拟真实用户,按照代码作出点击、输入、打开等操做 爬虫中使用他是为了解决requests没法解决javascript动态问题
Scrapy 使用了 Twisted 异步非阻塞网络库来处理网络通信,总体架构大体以下(绿线是数据流向):
Scrapy主要包括了如下组件:
Scrapy运行流程大概以下:
1.引擎:Hi!Spider, 你要处理哪个网站? 2.Spider:老大要我处理xxxx.com(初始URL)。 3.引擎:你把第一个须要处理的URL给我吧。 4.Spider:给你,第一个URL是xxxxxxx.com。 5.引擎:Hi!调度器,我这有request请求你帮我排序入队一下。 6.调度器:好的,正在处理你等一下。 7.引擎:Hi!调度器,把你处理好的request请求给我。 8.调度器:给你,这是我处理好的request 9.引擎:Hi!下载器,你按照老大的下载中间件的设置帮我下载一下这个request请求。 10.下载器:好的!给你,这是下载好的东西。(若是失败:sorry,这个request下载失败了。而后引擎告诉调度器,这个request下载失败了,你记录一下,咱们待会儿再下载) 11.引擎:Hi!Spider,这是下载好的东西,而且已经按照老大的下载中间件处理过了,你本身处理一下(注意!这儿responses默认是交给def parse()这个函数处理的) 12.Spider:(处理完毕数据以后对于须要跟进的URL),Hi!引擎,我这里有两个结果,这个是我须要跟进的URL,还有这个是我获取到的Item数据。 13.引擎:Hi !管道 我这儿有个item你帮我处理一下!调度器!这是须要跟进URL你帮我处理下。而后从第四步开始循环,直到获取完老大须要所有信息。 14.管道、调度器:好的,如今就作!
http://www.javashuo.com/article/p-fklqtvnh-cy.html
方式一:内置添加代理功能 # -*- coding: utf-8 -*- import os import scrapy from scrapy.http import Request class ChoutiSpider(scrapy.Spider): name = 'chouti' allowed_domains = ['chouti.com'] start_urls = ['https://dig.chouti.com/'] def start_requests(self): os.environ['HTTP_PROXY'] = "http://192.168.11.11" for url in self.start_urls: yield Request(url=url,callback=self.parse) def parse(self, response): print(response) 方式二:自定义下载中间件 import random import base64 import six def to_bytes(text, encoding=None, errors='strict'): """Return the binary representation of `text`. If `text` is already a bytes object, return it as-is.""" if isinstance(text, bytes): return text if not isinstance(text, six.string_types): raise TypeError('to_bytes must receive a unicode, str or bytes ' 'object, got %s' % type(text).__name__) if encoding is None: encoding = 'utf-8' return text.encode(encoding, errors) class MyProxyDownloaderMiddleware(object): def process_request(self, request, spider): proxy_list = [ {'ip_port': '111.11.228.75:80', 'user_pass': 'xxx:123'}, {'ip_port': '120.198.243.22:80', 'user_pass': ''}, {'ip_port': '111.8.60.9:8123', 'user_pass': ''}, {'ip_port': '101.71.27.120:80', 'user_pass': ''}, {'ip_port': '122.96.59.104:80', 'user_pass': ''}, {'ip_port': '122.224.249.122:8088', 'user_pass': ''}, ] proxy = random.choice(proxy_list) if proxy['user_pass'] is not None: request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port']) encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass'])) request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass) else: request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port']) 配置: DOWNLOADER_MIDDLEWARES = { # 'xiaohan.middlewares.MyProxyDownloaderMiddleware': 543, }
from twisted.web.client import Agent, getPage, ResponseDone, PotentialDataLoss from twisted.internet import defer, reactor, protocol from twisted.web._newclient import Response from io import BytesIO class _ResponseReader(protocol.Protocol): def __init__(self, finished, txresponse, file_name): self._finished = finished self._txresponse = txresponse self._bytes_received = 0 self.f = open(file_name, mode='wb') def dataReceived(self, bodyBytes): self._bytes_received += len(bodyBytes) # 一点一点的下载 self.f.write(bodyBytes) self.f.flush() def connectionLost(self, reason): if self._finished.called: return if reason.check(ResponseDone): # 下载完成 self._finished.callback((self._txresponse, 'success')) elif reason.check(PotentialDataLoss): # 下载部分 self._finished.callback((self._txresponse, 'partial')) else: # 下载异常 self._finished.errback(reason) self.f.close()
http://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/autothrottle.html
# 有些状况下,例如爬取大的站点,咱们但愿能暂停爬取,以后再恢复运行。 # Scrapy经过以下工具支持这个功能: 一个把调度请求保存在磁盘的调度器 一个把访问请求保存在磁盘的副本过滤器[duplicates filter] 一个能持续保持爬虫状态(键/值对)的扩展 Job 路径 要启用持久化支持,你只须要经过 JOBDIR 设置 job directory 选项。 这个路径将会存储全部的请求数据来保持一个单独任务的状态(例如:一次spider爬取(a spider run))。 必需要注意的是,这个目录不容许被不一样的spider共享,甚至是同一个spider的不一样jobs/runs也不行。 也就是说,这个目录就是存储一个单独 job的状态信息。
在spiders同级建立任意目录,如:commands 在其中建立'crawlall.py'文件(此处文件名就是自定义的命令) from scrapy.commands import ScrapyCommand from scrapy.utils.project import get_project_settings class Command(ScrapyCommand): requires_project = True def syntax(self): return '[options]' def short_desc(self): return 'Runs all of the spiders' def run(self, args, opts): spider_list = self.crawler_process.spiders.list() for name in spider_list: self.crawler_process.crawl(name, **opts.__dict__) self.crawler_process.start() 在'settings.py'中添加配置'COMMANDS_MODULE = '项目名称.目录名称'' 在项目目录执行命令:'scrapy crawlall'
'DepthMiddleware'是一个用于追踪每一个Request在被爬取的网站的深度的中间件。 其能够用来限制爬取深度的最大深度或相似的事情。 'DepthMiddleware'能够经过下列设置进行配置(更多内容请参考设置文档): 'DEPTH_LIMIT':爬取所容许的最大深度,若是为0,则没有限制。 'DEPTH_STATS':是否收集爬取状态。 'DEPTH_PRIORITY':是否根据其深度对requet安排优先
Scrapy 提供了 pipeline 模块来执行保存数据的操做。 在建立的 Scrapy 项目中自动建立了一个 pipeline.py 文件,同时建立了一个默认的 Pipeline 类。 咱们能够根据须要自定义 Pipeline 类,而后在 settings.py 文件中进行配置便可
经过raise DropItem()方法
http://www.cnblogs.com/wupeiqi/articles/6229292.html
实现了分布式爬虫,url去重、调度器、数据持久化 'scheduler'调度器 'dupefilter'URL去重规则(被调度器使用) 'pipeline'数据持久化
a. 内部进行配置,链接Redis
b.去重规则经过redis的集合完成,集合的Key为:
key = defaults.DUPEFILTER_KEY % {'timestamp': int(time.time())}
默认配置:
DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'
c.去重规则中将url转换成惟一标示,而后在redis中检查是否已经在集合中存在
from scrapy.utils import request
from scrapy.http import Request
req = Request(url='http://www.cnblogs.com/wupeiqi.html')
result = request.request_fingerprint(req)
print(result) # 8ea4fd67887449313ccc12e5b6b92510cc53675c
scrapy和scrapy-redis的去重规则(源码) 1. scrapy中去重规则是如何实现? class RFPDupeFilter(BaseDupeFilter): """Request Fingerprint duplicates filter""" def __init__(self, path=None, debug=False): self.fingerprints = set() @classmethod def from_settings(cls, settings): debug = settings.getbool('DUPEFILTER_DEBUG') return cls(job_dir(settings), debug) def request_seen(self, request): # 将request对象转换成惟一标识。 fp = self.request_fingerprint(request) # 判断在集合中是否存在,若是存在则返回True,表示已经访问过。 if fp in self.fingerprints: return True # 以前未访问过,将url添加到访问记录中。 self.fingerprints.add(fp) def request_fingerprint(self, request): return request_fingerprint(request) 2. scrapy-redis中去重规则是如何实现? class RFPDupeFilter(BaseDupeFilter): """Redis-based request duplicates filter. This class can also be used with default Scrapy's scheduler. """ logger = logger def __init__(self, server, key, debug=False): # self.server = redis链接 self.server = server # self.key = dupefilter:123912873234 self.key = key @classmethod def from_settings(cls, settings): # 读取配置,链接redis server = get_redis_from_settings(settings) # key = dupefilter:123912873234 key = defaults.DUPEFILTER_KEY % {'timestamp': int(time.time())} debug = settings.getbool('DUPEFILTER_DEBUG') return cls(server, key=key, debug=debug) @classmethod def from_crawler(cls, crawler): return cls.from_settings(crawler.settings) def request_seen(self, request): fp = self.request_fingerprint(request) # This returns the number of values added, zero if already exists. # self.server=redis链接 # 添加到redis集合中:1,添加工程;0,已经存在 added = self.server.sadd(self.key, fp) return added == 0 def request_fingerprint(self, request): return request_fingerprint(request) def close(self, reason=''): self.clear() def clear(self): """Clears fingerprints data.""" self.server.delete(self.key)
'vitualenv'是一个独立的python虚拟环境。 如: 当前项目依赖的是一个版本,可是另外一个项目依赖的是另外一个版本,这样就会形成依赖冲突, 而virtualenv就是解决这种状况的,virtualenv经过建立一个虚拟化的python运行环境, 将咱们所需的依赖安装进去的,不一样项目之间相互不干扰。
能够经过对项目目录扫描,自动发现使用了那些类库,而且自动生成依赖清单。 pipreqs ./ 生成requirements.txt
1)PyFlakes:静态检查Python代码逻辑错误的工具。 2)Pep8: 静态检查PEP8编码风格的工具。 3)NedBatchelder’s McCabe script:静态分析Python代码复杂度的工具。 Python代码分析工具:PyChecker、Pylint
1.B树中同一键值不会出现屡次,而且有可能出如今叶结点,也有可能出如今非叶结点中。 而B+树的键必定会出如今叶结点中,并有可能在非叶结点中重复出现,以维持B+树的平衡。 2.由于B树键位置不定,且在整个树结构中只出现一次,
工厂模式/单例模式等。
leetcode是个题库,里面有多很编程题目,能够在线编译运行。 https://leetcode-cn.com/problemset/all/
1建立目录 mkdir /data cd / mkdir data 2查看目录 ls ls -l 显示详细信息
Linux/Centos
PV访问量(Page View),即页面访问量,每打开一次页面PV计数+1,刷新页面也是。 UV访客量(Unique Visitor)指独立访客访问数,一台电脑终端为一个访客。
'QPS(Query Per Second)' 每秒查询率,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准
wsgi是一种通用的接口标准或者接口协议,实现了python web程序与服务器之间交互的通用性。 uwsgi:同WSGI同样是一种通讯协议 uwsgi协议是一个'uWSGI服务器'自有的协议,它用于定义传输信息的类型, 'uWSGI'是实现了uwsgi和WSGI两种协议的Web服务器,负责响应python的web请求。
# Supervisor: 是一款基于Python的进程管理工具,能够很方便的管理服务器上部署的应用程序。 是C/S模型的程序,其服务端是supervisord服务,客户端是supervisorctl命令 # 主要功能: 1 启动、重启、关闭包括但不限于python进程。 2 查看进程的运行状态。 3 批量维护多个进程。
正向代理代理客户端(客户端找一个代理去访问服务器,服务器不知道你的真实IP) 反向代理代理服务器(服务器找一个代理给你响应,你不知道服务器的真实IP)
SSH 为 'Secure Shell' 的缩写,是创建在应用层基础上的安全协议。 SSH 是目前较可靠,为远程登陆会话和其余网络服务提供的安全性协议。 利用 SSH 协议能够有效防止远程管理过程当中的信息泄露问题。
起初是百度,发现搜到的答案不精准,净广告 转战谷歌,但墙了;捣鼓怎么FQ 还会去知乎、stackoverfloow、必应、思否(segmentfault)
python之禅(主要专一Python相关知识,做者:刘志军) 码农翻身(主要是Java的,但不光是java,涵盖面很广,做者:刘欣) 实验楼(在线练项目) and so on
Numpy pandas(金融量化分析、聚宽) 百度AI 图灵API 智能玩具
来自转载,有较大改动。