1. 程式人生 > 其它 >2.1 內建模組和開發規範

2.1 內建模組和開發規範

內建模組和開發規範

目標:掌握常見的內建模組的使用及瞭解軟體開發的規範。

今日概要:

  • 內建模組
    • json
    • time
    • datetime
    • re
  • 開發規範
    • 主檔案
    • 配置檔案
    • 資料
    • 附件
    • 業務程式碼

1. 內建模組

1.1 json

json模組,是python內部的一個模組,可以將python的資料格式 轉換為json格式的資料,也可以將json格式的資料轉換為python的資料格式。

json格式,是一個數據格式(本質上就是個字串,常用語網路資料傳輸)

# Python中的資料型別的格式
data = [
    {"id": 1, "name": "武沛齊", "age": 18},
    {"id": 2, "name": "alex", "age": 18},
    ('wupeiqi',123),
]

# JSON格式
value = '[{"id": 1, "name": "武沛齊", "age": 18}, {"id": 2, "name": "alex", "age": 18},["wupeiqi",123]]'

1.1.1 核心功能

json格式的作用?

跨語言資料傳輸,例如:
	A系統用Python開發,有列表型別和字典型別等。
	B系統用Java開發,有陣列、map等的型別。

	語言不同,基礎資料型別格式都不同。
	
	為了方便資料傳輸,大家約定一個格式:json格式,每種語言都是將自己資料型別轉換為json格式,也可以將json格式的資料轉換為自己的資料型別。

Python資料型別與json格式的相互轉換:

  • 資料型別 -> json ,一般稱為:序列化

    import json
    
    data = [
        {"id": 1, "name": "武沛齊", "age": 18},
        {"id": 2, "name": "alex", "age": 18},
    ]
    
    res = json.dumps(data)
    print(res) # '[{"id": 1, "name": "\u6b66\u6c9b\u9f50", "age": 18}, {"id": 2, "name": "alex", "age": 18}]'
    
    res = json.dumps(data, ensure_ascii=False)
    print(res) # '[{"id": 1, "name": "武沛齊", "age": 18}, {"id": 2, "name": "alex", "age": 18}]'
    
  • json格式 -> 資料型別,一般稱為:反序列化

    import json
    
    data_string = '[{"id": 1, "name": "武沛齊", "age": 18}, {"id": 2, "name": "alex", "age": 18}]'
    
    data_list = json.loads(data_string)
    
    print(data_list)
    

練習題

  1. 寫網站,給使用者返回json格式資料

    • 安裝flask模組,協助我們快速寫網站(之前已安裝過)

      pip3 install flask
      
    • 使用flask寫網站

      import json
      from flask import Flask
      
      app = Flask(__name__)
      
      
      def index():
          return "首頁"
      
      
      def users():
          data = [
              {"id": 1, "name": "武沛齊", "age": 18},
              {"id": 2, "name": "alex", "age": 18},
          ]
          return json.dumps(data)
      
      
      app.add_url_rule('/index/', view_func=index, endpoint='index')
      app.add_url_rule('/users/', view_func=users, endpoint='users')
      
      if __name__ == '__main__':
          app.run()
      
  2. 傳送網路請求,獲取json格式資料並處理。

    import json
    import requests
    
    url = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=5&page_start=20"
    
    res = requests.get(
        url=url,
        headers={
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
        }
    )
    
    # json格式
    print(res.text)
    
    # json格式轉換為python資料型別
    data_dict = json.loads(res.text)
    print(data_dict)
    

1.1.2 型別要求

python的資料型別轉換為 json 格式,對資料型別是有要求的,預設只支援:

    +-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |
    +-------------------+---------------+
    | list, tuple       | array         |
    +-------------------+---------------+
    | str               | string        |
    +-------------------+---------------+
    | int, float        | number        |
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+
data = [
    {"id": 1, "name": "武沛齊", "age": 18},
    {"id": 2, "name": "alex", "age": 18},
]

其他型別如果想要支援,需要自定義JSONEncoder 才能實現【目前只需要瞭解大概意思即可,以後專案開發中用到了還會講解。】,例如:

import json
from decimal import Decimal
from datetime import datetime

data = [
    {"id": 1, "name": "武沛齊", "age": 18, 'size': Decimal("18.99"), 'ctime': datetime.now()},
    {"id": 2, "name": "alex", "age": 18, 'size': Decimal("9.99"), 'ctime': datetime.now()},
]


class MyJSONEncoder(json.JSONEncoder):
    def default(self, o):
        if type(o) == Decimal:
            return str(o)
        elif type(o) == datetime:
            return o.strftime("%Y-%M-%d")
        return super().default(o)


res = json.dumps(data, cls=MyJSONEncoder)
print(res)

1.1.3 其他功能

json模組中常用的是:

  • json.dumps,序列化生成一個字串。

  • json.loads,發序列化生成python資料型別。

  • json.dump,將資料序列化並寫入檔案(不常用)

    import json
    
    data = [
        {"id": 1, "name": "武沛齊", "age": 18},
        {"id": 2, "name": "alex", "age": 18},
    ]
    
    file_object = open('xxx.json', mode='w', encoding='utf-8')
    
    json.dump(data, file_object)
    
    file_object.close()
    
  • json.load,讀取檔案中的資料並反序列化為python的資料型別(不常用)

    import json
    
    file_object = open('xxx.json', mode='r', encoding='utf-8')
    
    data = json.load(file_object)
    print(data)
    
    file_object.close()
    

1.2 時間處理

  • UTC/GMT:世界時間

  • 本地時間:本地時區的時間。

Python中關於時間處理的模組有兩個,分別是time和datetime。

1.2.1 time

import time

# 獲取當前時間戳(自1970-1-1 00:00)
v1 = time.time()
print(v1)

# 時區
v2 = time.timezone

# 停止n秒,再執行後續的程式碼。
time.sleep(5)

1.2.2 datetime

在平時開發過程中的時間一般是以為如下三種格式存在:

  • datetime

    from datetime import datetime, timezone, timedelta
    
    v1 = datetime.now()  # 當前本地時間
    print(v1)
    
    tz = timezone(timedelta(hours=7))  # 當前東7區時間
    v2 = datetime.now(tz)
    print(v2)
    
    v3 = datetime.utcnow()  # 當前UTC時間
    print(v3)
    
    from datetime import datetime, timedelta
    
    v1 = datetime.now()
    print(v1)
    
    # 時間的加減
    v2 = v1 + timedelta(days=140, minutes=5)
    print(v2)
    
    # datetime型別 + timedelta型別
    
    from datetime import datetime, timezone, timedelta
    
    v1 = datetime.now()
    print(v1)
    
    v2 = datetime.utcnow()  # 當前UTC時間
    print(v2)
    
    # datetime之間相減,計算間隔時間(不能相加)
    data = v1 - v2
    print(data.days, data.seconds / 60 / 60, data.microseconds)
    
    # datetime型別 - datetime型別
    # datetime型別 比較 datetime型別
    
  • 字串

    # 字串格式的時間  ---> 轉換為datetime格式時間
    text = "2021-11-11"
    v1 = datetime.strptime(text,'%Y-%m-%d') # %Y 年,%m,月份,%d,天。
    print(v1)
    
    # datetime格式 ----> 轉換為字串格式
    v1 = datetime.now()
    val = v1.strftime("%Y-%m-%d %H:%M:%S")
    print(val)
    
  • 時間戳

    # 時間戳格式 --> 轉換為datetime格式
    ctime = time.time() # 11213245345.123
    v1 = datetime.fromtimestamp(ctime)
    print(v1)
    
    # datetime格式 ---> 轉換為時間戳格式
    v1 = datetime.now()
    val = v1.timestamp()
    print(val)
    

練習題

  1. 日誌記錄,將使用者輸入的資訊寫入到檔案,檔名格式為年-月-日-時-分.txt

    from datetime import datetime
    
    while True:
        text = input("請輸入內容:")
        if text.upper() == "Q":
            break
            
        current_datetime = datetime.now().strftime("%Y-%m-%d-%H-%M")
        file_name = "{}.txt".format(current_datetime)
        
        with open(file_name, mode='a', encoding='utf-8') as file_object:
            file_object.write(text)
            file_object.flush()
    
  2. 使用者註冊,將使用者資訊寫入Excel,其中包含:使用者名稱、密碼、註冊時間 三列。

    import os
    import hashlib
    from datetime import datetime
    
    from openpyxl import load_workbook
    from openpyxl import workbook
    
    
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    FILE_NAME = "db.xlsx"
    
    
    def md5(origin):
        hash_object = hashlib.md5("sdfsdfsdfsd23sd".encode('utf-8'))
        hash_object.update(origin.encode('utf-8'))
        return hash_object.hexdigest()
    
    
    def register(username, password):
        db_file_path = os.path.join(BASE_DIR, FILE_NAME)
        if os.path.exists(db_file_path):
            wb = load_workbook(db_file_path)
            sheet = wb.worksheets[0]
            next_row_position = sheet.max_row + 1
        else:
            wb = workbook.Workbook()
            sheet = wb.worksheets[0]
            next_row_position = 1
    
        user = sheet.cell(next_row_position, 1)
        user.value = username
    
        pwd = sheet.cell(next_row_position, 2)
        pwd.value = md5(password)
    
        ctime = sheet.cell(next_row_position, 3)
        ctime.value = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
        wb.save(db_file_path)
    
    
    def run():
        while True:
            username = input("請輸入使用者名稱:")
            if username.upper() == "Q":
                break
            password = input("請輸入密碼:")
            register(username, password)
    
    
    if __name__ == '__main__':
        run()
    
    

1.3 正則表示式相關

當給你一大堆文字資訊,讓你提取其中的指定資料時,可以使用正則來實現。例如:提取文字中的郵箱和手機號

import re

text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"

phone_list = re.findall("1[3|5|8|9]\d{9}", text)
print(phone_list)

1.3.1 正則表示式

1. 字元相關
  • wupeiqi 匹配文字中的wupeiqi

    import re
    
    text = "你好wupeiqi,阿斯頓發wupeiqasd 阿士大夫能接受的wupeiqiff"
    data_list = re.findall("wupeiqi", text)
    print(data_list) # ['wupeiqi', 'wupeiqi'] 可用於計算字串中某個字元出現的次數
    
  • [abc] 匹配a或b或c 字元。

    import re
    
    text = "你2b好wupeiqi,阿斯頓發awupeiqasd 阿士大夫a能接受的wffbbupqaceiqiff"
    data_list = re.findall("[abc]", text)
    print(data_list) # ['b', 'a', 'a', 'a', 'b', 'b', 'c']
    
    import re
    
    text = "你2b好wupeiqi,阿斯頓發awupeiqasd 阿士大夫a能接受的wffbbupqcceiqiff"
    data_list = re.findall("q[abc]", text)
    print(data_list) # ['qa', 'qc']
    
  • [^abc] 匹配除了abc意外的其他字元。

    import re
    
    text = "你wffbbupceiqiff"
    data_list = re.findall("[^abc]", text)
    print(data_list)  # ['你', 'w', 'f', 'f', 'u', 'p', 'e', 'i', 'q', 'i', 'f', 'f']
    
  • [a-z] 匹配a~z的任意字元( [0-9]也可以 )。

    import re
    
    text = "alexrootrootadmin"
    data_list = re.findall("t[a-z]", text)
    print(data_list)  # ['tr', 'ta']
    
  • . 代指除換行符以外的任意字元。

    import re
    
    text = "alexraotrootadmin"
    data_list = re.findall("r.o", text)
    print(data_list) # ['rao', 'roo']
    
    import re
    
    text = "alexraotrootadmin"
    data_list = re.findall("r.+o", text) # 貪婪匹配
    print(data_list) # ['raotroo']
    
    import re
    
    text = "alexraotrootadmin"
    data_list = re.findall("r.+?o", text) # 非貪婪匹配
    print(data_list) # ['rao']
    
  • \w 代指字母或數字或下劃線(漢字)。

    import re
    
    text = "北京武沛alex齊北  京武沛alex齊"
    data_list = re.findall("武\w+x", text)
    print(data_list) # ['武沛alex', '武沛alex']
    
  • \d 代指數字

    import re
    
    text = "root-ad32min-add3-admd1in"
    data_list = re.findall("d\d", text)
    print(data_list) # ['d3', 'd3', 'd1']
    
    import re
    
    text = "root-ad32min-add3-admd1in"
    data_list = re.findall("d\d+", text)
    print(data_list) # ['d32', 'd3', 'd1']
    
  • \s 代指任意的空白符,包括空格、製表符等。

    import re
    
    text = "root admin add admin"
    data_list = re.findall("a\w+\s\w+", text)
    print(data_list) # ['admin add']
    
2. 數量相關
  • * 重複0次或更多次

    import re
    
    text = "他是大B個,確實是個大2B。"
    data_list = re.findall("大2*B", text)
    print(data_list) # ['大B', '大2B']
    
  • + 重複1次或更多次

    import re
    
    text = "他是大B個,確實是個大2B,大3B,大66666B。"
    data_list = re.findall("大\d+B", text)
    print(data_list) # ['大2B', '大3B', '大66666B']
    
  • ? 重複0次或1次

    import re
    
    text = "他是大B個,確實是個大2B,大3B,大66666B。"
    data_list = re.findall("大\d?B", text)
    print(data_list) # ['大B', '大2B', '大3B']
    
  • {n} 重複n次

    import re
    
    text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    data_list = re.findall("151312\d{5}", text)
    print(data_list) # ['15131255789']
    
  • {n,} 重複n次或更多次

    import re
    
    text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    data_list = re.findall("\d{9,}", text)
    print(data_list) # ['442662578', '15131255789']
    
    
  • {n,m} 重複n到m次

    import re
    
    text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    data_list = re.findall("\d{10,15}", text)
    print(data_list) # ['15131255789']
    
3. 括號(分組)
  • 提取資料區域

    import re
    
    text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    data_list = re.findall("15131(2\d{5})", text)
    print(data_list)  # ['255789']
    
    import re
    
    text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來15131266666呀"
    data_list = re.findall("15(13)1(2\d{5})", text)
    print(data_list)  # [ ('13', '255789')   ]
    
    import re
    
    text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    data_list = re.findall("(15131(2\d{5}))", text)
    print(data_list)  # [('15131255789', '255789')]
    
  • 獲取指定區域 + 或條件

    import re
    
    text = "樓主15131root太牛15131alex逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    data_list = re.findall("15131(2\d{5}|r\w+太)", text)
    print(data_list)  # ['root太', '255789']
    
    import re
    
    text = "樓主15131root太牛15131alex逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    data_list = re.findall("(15131(2\d{5}|r\w+太))", text)
    print(data_list)  # [('15131root太', 'root太'), ('15131255789', '255789')]
    
    練習題
  1. 利用正則匹配QQ號碼

    [1-9]\d{4,}
    
  2. 身份證號碼

    import re
    
    text = "dsf130429191912015219k13042919591219521Xkk"
    data_list = re.findall("\d{17}[\dX]", text) # [abc]
    print(data_list) # ['130429191912015219', '13042919591219521X']
    
    import re
    
    text = "dsf130429191912015219k13042919591219521Xkk"
    data_list = re.findall("\d{17}(\d|X)", text)
    print(data_list) # ['9', 'X']
    
    import re
    
    text = "dsf130429191912015219k13042919591219521Xkk"
    data_list = re.findall("(\d{17}(\d|X))", text)
    print(data_list) # [('130429191912015219', '9'), ('13042919591219521X', 'X')]
    
    import re
    
    text = "dsf130429191912015219k13042919591219521Xkk"
    data_list = re.findall("(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)", text)
    print(data_list) # [('130429', '1919', '12', '01', '521', '9'), ('130429', '1959', '12', '19', '521', 'X')]
    
  3. 手機號

    import re
    
    text = "我的手機哈是15133377892,你的手機號是1171123啊?"
    data_list = re.findall("1[3-9]\d{9}", text)
    print(data_list)  # ['15133377892']
    
  4. 郵箱地址

    import re
    
    text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    email_list = re.findall("\w+@\w+\.\w+",text)
    print(email_list) # ['[email protected]和xxxxx']
    
    import re
    
    text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    email_list = re.findall("[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+", text, re.ASCII)
    print(email_list) # ['[email protected]', '[email protected]']
    
    
    import re
    
    text = "樓主太牛逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    email_list = re.findall("\w+@\w+\.\w+", text, re.ASCII)
    print(email_list) # ['[email protected]', '[email protected]']
    
    import re
    
    text = "樓主太牛[email protected]逼了,線上想要 [email protected][email protected]謝謝樓主,手機號也可15131255789,搞起來呀"
    email_list = re.findall("(\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*)", text, re.ASCII)
    print(email_list) # [('[email protected]', '-2578', '', ''), ('[email protected]', '', '', '')]
    
  5. 補充程式碼,實現獲取頁面上的所有評論(已實現),並提取裡面的郵箱。

    # 先安裝兩個模組
    pip3 install requests
    pip3 install beautifulsoup4
    
    import re
    import requests
    from bs4 import BeautifulSoup
    
    res = requests.get(
        url="https://www.douban.com/group/topic/79870081/",
        headers={
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
        }
    )
    bs_object = BeautifulSoup(res.text, "html.parser")
    comment_object_list = bs_object.find_all("p", attrs={"class": "reply-content"})
    for comment_object in comment_object_list:
        text = comment_object.text
        print(text)
        # 請繼續補充程式碼,提取text中的郵箱地址
    
    
4. 起始和結束

上述示例中都是去一段文字中提取資料,只要文字中存在即可。

但,如果要求使用者輸入的內容必須是指定的內容開頭和結尾,比就需要用到如下兩個字元。

  • ^ 開始
  • $ 結束
import re

text = "啊[email protected]我靠"
email_list = re.findall("^\w+@\w+.\w+$", text, re.ASCII)
print(email_list) # []
import re

text = "[email protected]"
email_list = re.findall("^\w+@\w+.\w+$", text, re.ASCII)
print(email_list) # ['[email protected]']

這種一般用於對使用者輸入資料格式的校驗比較多,例如:

import re

text = input("請輸入郵箱:")
email = re.findall("^\w+@\w+.\w+$", text, re.ASCII)
if not email:
    print("郵箱格式錯誤")
else:
    print(email)
5. 特殊字元

由於正則表示式中 * . \ { } ( ) 等都具有特殊的含義,所以如果想要在正則中匹配這種指定的字元,需要轉義,例如:

import re

text = "我是你{5}爸爸"
data = re.findall("你{5}爸", text)
print(data) # []
import re

text = "我是你{5}爸爸"
data = re.findall("你\{5\}爸", text)
print(data)

1.3.2 re模組

python中提供了re模組,可以處理正則表示式並對文字進行處理。

  • findall,獲取匹配到的所有資料

    import re
    
    text = "dsf130429191912015219k13042919591219521Xkk"
    data_list = re.findall("(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)", text)
    print(data_list) # [('130429', '1919', '12', '01', '521', '9'), ('130429', '1959', '12', '19', '521', 'X')]
    
  • match,從起始位置開始匹配,匹配成功返回一個物件,未匹配成功返回None

    import re
    
    text = "大小逗2B最逗3B歡樂"
    data = re.match("逗\dB", text)
    print(data) # None
    
    import re
    
    text = "逗2B最逗3B歡樂"
    data = re.match("逗\dB", text)
    if data:
        content = data.group() # "逗2B"
        print(content)
    
  • search,瀏覽整個字串去匹配第一個,未匹配成功返回None

    import re
    
    text = "大小逗2B最逗3B歡樂"
    data = re.search("逗\dB", text)
    if data:
        print(data.group())  # "逗2B"
    
  • sub,替換匹配成功的位置

    import re
    
    text = "逗2B最逗3B歡樂"
    data = re.sub("\dB", "沙雕", text)
    print(data) # 逗沙雕最逗沙雕歡樂
    
    import re
    
    text = "逗2B最逗3B歡樂"
    data = re.sub("\dB", "沙雕", text, 1)
    print(data) # 逗沙雕最逗3B歡樂
    
  • split,根據匹配成功的位置分割

    import re
    
    text = "逗2B最逗3B歡樂"
    data = re.split("\dB", text)
    print(data) # ['逗', '最逗', '歡樂']
    
    import re
    
    text = "逗2B最逗3B歡樂"
    data = re.split("\dB", text, 1)
    print(data) # ['逗', '最逗3B歡樂']
    
  • finditer

    import re
    
    text = "逗2B最逗3B歡樂"
    data = re.finditer("\dB", text)
    for item in data:
        print(item.group())
    
    import re
    
    text = "逗2B最逗3B歡樂"
    data = re.finditer("(?P<xx>\dB)", text)  # 命名分組
    for item in data:
        print(item.groupdict())
    
    text = "dsf130429191912015219k13042919591219521Xkk"
    data_list = re.finditer("\d{6}(?P<year>\d{4})(?P<month>\d{2})(?P<day>\d{2})\d{3}[\d|X]", text)
    for item in data_list:
        info_dict = item.groupdict()
        print(info_dict)
    

小結

到此,關於最常見的內建模組就全部講完了(共11個),現階段只需要掌握這些模組的使用即可,在後續的課程和練習題中也會涉及到一起其他內建模組。

  • os
  • shutil
  • sys
  • random
  • hashlib
  • configparser
  • xml
  • json
  • time
  • datetime
  • re

2. 專案開發規範

現階段,我們在開發一些程式時(終端執行),應該遵循一些結構的規範,讓你的系統更加專業。

2.1 單檔案應用

當基於python開發簡單應用時(一個py檔案就能搞定),需要注意如下幾點。

"""
檔案註釋
"""

import re
import random

import requests
from openpyxl import load_workbook

DB = "XXX"


def do_something():
    """ 函式註釋 """

    # TODO 待完成時,下一期實現xxx功能
    for i in range(10):
        pass


def run():
    """ 函式註釋 """

    # 對功能程式碼進行註釋
    text = input(">>>")
    print(text)


if __name__ == '__main__':
    run()

2.2 單可執行檔案

新建立一個專案,假設名字叫 【crm】,可以建立如下檔案和資料夾來存放程式碼和資料。

crm
├── app.py        檔案,程式的主檔案(儘量精簡)
├── config.py     檔案,配置檔案(放相關配置資訊,程式碼中讀取配置資訊,如果想要修改配置,即可以在此修改,不用再去程式碼中逐一修改了)
├── db            資料夾,存放資料
├── files         資料夾,存放檔案
├── src           包,業務處理的程式碼
└── utils         包,公共功能

示例程式見附件:crm.zip

2.3 多可執行檔案

新建立專案,假設名稱叫【killer】,可以建立如下檔案和資料夾來存放程式碼和資料。

killer
├── bin					資料夾,存放多個主檔案(可執行)
│ ├── app1.py
│ └── app2.py
├── config              包,配置檔案
│ ├── __init__.py
│ └── settings.py
├── db                  資料夾,存放資料
├── files               資料夾,存放檔案
├── src                 包,業務程式碼
│ └── __init__.py
└── utils               包,公共功能
    └── __init__.py

總結

  1. json格式和json模組
  2. json模組處理特殊的資料型別
  3. datetime格式與字串、時間戳以及相關之間的轉換。
  4. datetime格式時間與timedelta的加減。
  5. 兩個datetime相減可以計算時間間隔,得到的是一個timedelta格式的時間。
  6. 瞭解正則表示式的編寫方式和python中re模組的使用。
  7. 專案開發規範。

作業:開發短視訊資訊平臺

  • 有video.csv視訊庫檔案,其中有999條短視訊資料,格式如下:【 video.csv 檔案已為大家提供好,在day15課件目錄下。 】
  • 專案的核心功能有:

    • 分頁看新聞(每頁顯示10條),提示使用者輸入頁碼,根據頁碼顯示指定頁面的資料。

      • 提示使用者輸入頁碼,根據頁碼顯示指定頁面的資料。
      • 當用戶輸入的頁碼不存在時,預設顯示第1頁
    • 搜尋專區

      • 使用者輸入關鍵字,根據關鍵詞篩選出所有匹配成功的短視訊資訊。
      • 支援的搜尋兩種搜尋格式:
        • id=1715025,篩選出id等於1715025的視訊(video.csv的第一列)。
        • key=文字,模糊搜尋,篩選包含關鍵字的所有新聞(video.csv的第二列)。
    • 下載專區

      • 使用者輸入視訊id,根據id找到對應的mp4視訊下載地址,然後下載視訊到專案的files目錄。

        • 視訊的檔名為:視訊id-年-月-日-時-分-秒.mp4

        • 視訊下載程式碼示例

          import requests
          
          res = requests.get(
              url='https://video.pearvideo.com/mp4/adshort/20210105/cont-1715046-15562045_adpkg-ad_hd.mp4'
          )
          
          # 視訊總大小(位元組)
          file_size = int(res.headers['Content-Length'])
          
          download_size = 0
          with open('xxx.mp4', mode='wb') as file_object:
              # 分塊讀取下載的視訊檔案(最多一次讀128位元組),並逐一寫入到檔案中。 len(chunk)表示實際讀取到每塊的視訊檔案大小。
              for chunk in res.iter_content(128):
                  download_size += len(chunk)
                  file_object.write(chunk)
                  file_object.flush()
                  message = "視訊總大小為:{}位元組,已下載{}位元組。".format(file_size, download_size)
                  print(message)
              file_object.close()
          
          res.close()
          
        • 下載的過程中,輸出已下載的百分比,示例程式碼如下:

          import time
          
          print("正在下載中...")
          for i in range(101):
              text = "\r{}%".format(i)
              print(text, end="")
              time.sleep(0.2)
          
          print("\n下載完成")
          

附贈

自動採集梨視訊1000條資訊的爬蟲指令碼。梨視訊平臺系統更新後可能會導致下載失敗,到時候需根據平臺調整再來修改程式碼。

"""
下載梨視訊的:視訊ID,視訊標題,視訊URL地址 並寫入到本次 video.csv 檔案中。

執行此指令碼需要預先安裝:
    pip install request
    pip install beautifulsoup4

"""
import requests
from bs4 import BeautifulSoup


def get_mp4_url(video_id):
    data = requests.get(
        url="https://www.pearvideo.com/videoStatus.jsp?contId={}".format(video_id),
        headers={
            "Referer": "https://www.pearvideo.com/video_{}".format(video_id),
        }
    )
    response = data.json()
    image_url = response['videoInfo']['video_image']
    video_url = response['videoInfo']['videos']['srcUrl']
    middle = image_url.rsplit('/', 1)[-1].rsplit('-', 1)[0]
    before, after = video_url.rsplit('/', 1)
    suffix = after.split('-', 1)[-1]
    url = "{}/{}-{}".format(before, middle, suffix)
    return url


def download_video():
    file_object = open('video.csv', mode='w', encoding='utf-8')
    count = 0
    while count <= 999:
        res = requests.get(
            url="https://www.pearvideo.com/category_loading.jsp?reqType=14&categoryId=&start={}".format(count)
        )
        bs = BeautifulSoup(res.text, 'lxml')
        a_list = bs.find_all("a", attrs={'class': "vervideo-lilink"})
        for tag in a_list:
            title = tag.find('div', attrs={'class': "vervideo-title"}).text.strip()
            video_id = tag.get('href').split('_')[-1]
            mp4_url = get_mp4_url(video_id)
            row = "{},{},{}\n".format(video_id, title, mp4_url)
            file_object.write(row)
            file_object.flush()
            count += 1
            message = "已下載{}個".format(count)
            print(message)
    file_object.close()


if __name__ == '__main__':
    download_video()