瀏覽器本質,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 智能玩具
來自轉載,有較大改動。