Day 45~ Day 65
Introduction
課程接下來開始都圍繞在網頁爬蟲及開發的部分,如Requests, BeautifulSoup, Flask 以及SQLAlchemy…等
網址 :https://www.udemy.com/course/100-days-of-code/
程式碼:https://github.com/deathpc/python_100day/
Day 45
搭配前一個月的內容,這週開始介紹如何利用BeautifulSoup 這個套件來做網頁內容的擷取,常見起手式如下:
import requests
from bs4 import BeautifulSoup
response = requests.get("html")
soup = BeautifulSoup(response.text, "html.parser")
- 挑選元素
- find(), find_all()
利用Tag的名稱及屬性去搜尋元素 - select_one(), select()
利用CSS選擇器搜尋元素 - 例如搜尋所有 Class 為 card-title 的 H4 標籤
soup.find_all('h4', 'card-title')
soup.find_all('h4', {'class': 'card-title'})
soup.find_all('h4', class_='card-title')
soup.select('h4.card-title') - 取得元素內容
- getText()
取得標籤的文字 - get("attribute_name")
取得標籤的屬性值
Day 46-47
利用剛學到的bs去擷取某日 billboard 的 Top100 歌曲,再送到Spotify API 產生撥放清單以及在購物網站上擷取商品價格,但課程並沒有介紹 Spotify API 的使用方式,建議Google 一下它的認證機制怎麼做的,才不會卡住。
Day 48
介紹Selenium,一個自動化測試網站的模組,基本上BS能做的事Se都可以做,等於是軟體模擬使用者操作跟網站元素互動,因此一些互動渲染的部分也可以抓取到內容,以下為固定起手式
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait
driver_service = Service("chromedriver-97.exe")
driver_options = webdriver.ChromeOptions()
driver = webdriver.Chrome(service=driver_service, options=driver_options)
driver.get("url")
介紹Selenium,一個自動化測試網站的模組,基本上BS能做的事Se都可以做,等於是軟體模擬使用者操作跟網站元素互動,因此一些互動渲染的部分也可以抓取到內容,以下為固定起手式
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait
driver_service = Service("chromedriver-97.exe")
driver_options = webdriver.ChromeOptions()
driver = webdriver.Chrome(service=driver_service, options=driver_options)
driver.get("url")
Day 49-50
這兩週要利用Selenium爬取LinkedIn跟Tinder,不過對這兩個網站沒什麼興趣,看完介紹沒問題就跳過了。
Day 51-52
這兩週的課題是符合條件時自動發推特跟大量追蹤IG帳號,跟前兩週相比處理等待網頁載入時間這件事變得較為重要,除了單純的time.sleep之外,可透過expected_condition與WebDriverWait 兩個 sub-module 輔助,降低網頁載入受網速影響的問題。from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait
ec.invisibility_of_element_located((By.CSS_SELECTOR, "blabla"))(driver)
WebDriverWait(driver, 10).until(ec.presence_of_element_located((By.CSS_SELECTOR, "blabla")))
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait
ec.invisibility_of_element_located((By.CSS_SELECTOR, "blabla"))(driver)
WebDriverWait(driver, 10).until(ec.presence_of_element_located((By.CSS_SELECTOR, "blabla")))
Day 53
這週的內容是利用BS爬取符合條件的房價,並使用SE自動填入 Google 表單,完成後爬蟲的部分到這週就告一段落了
Day 54-55
這週開始介紹Python的網頁框架 - Flask,以及 Python 獨有的裝飾器語法- 在網址路由中傳遞變數
# For security
from markupsafe import escape
@app.route('/user/<username>')
def show_user_profile(username):
return 'User %s' % escape(username)
@app.route('/post/<int:post_id>')
def show_post(post_id):
return 'Post %d' % post_id
- 可用的變數類型
- string
- int
- float
- path:跟 string 的差異在允許斜線作為輸入
- uuid
- Decorator
decorator 可以讓程式碼更精簡,避免掉重複的程式碼,如以下區塊,上下兩段程式的作用是等價的
def decorated():
print("this function is decorated")
def decorator(function):
print('We will call "{}"'.format(function.__name__))
return function
decorated = decorator(decorated)
decorated()
--------------------------------------------------------------
def decorator(function):
print('We will call "{}"'.format(function.__name__))
return function
@decorator
def decorated():
print("this function is decorated")
this_is_decorated()
- 在網址路由中傳遞變數
# For security
from markupsafe import escape
@app.route('/user/<username>')
def show_user_profile(username):
return 'User %s' % escape(username)
@app.route('/post/<int:post_id>')
def show_post(post_id):
return 'Post %d' % post_id - 可用的變數類型
- string
- int
- float
- path:跟 string 的差異在允許斜線作為輸入
- uuid
- Decorator
decorator 可以讓程式碼更精簡,避免掉重複的程式碼,如以下區塊,上下兩段程式的作用是等價的def decorated():
print("this function is decorated")
def decorator(function):
print('We will call "{}"'.format(function.__name__))
return function
decorated = decorator(decorated)
decorated()
--------------------------------------------------------------
def decorator(function):
print('We will call "{}"'.format(function.__name__))
return function
@decorator
def decorated():
print("this function is decorated")
this_is_decorated()
Decorator 也可以透過 *args 及 **kwargs 傳遞被裝飾函式的 arguments,亦可傳遞參數給裝飾器本身,且裝飾器芝兼具有可堆疊的性質,詳細可參考以下區塊
def decorator(dec_arg):
def wrapper(func):
def internal_wrapper(*args):
print(f"Calling {func.__name__}, I'm decorator with {dec_arg}")
func(*args)
return internal_wrapper
return wrapper
@decorator('dec1')
def func1(*args):
print(f"Call from decorated1 with {args}")
@decorator('dec2')
@decorator('dec3')
def func2(*args):
print(f"Call from decorated2 with {args}")
func1('123', '321')
func2('456', '654')
Day 56
在Flask中載入網頁模板及靜態文件必須遵從框架的指示,將檔案放在 template 及 static 目錄,並輔以相應的函數:

- render_template
temp@app.route('/')
def hello_world():
return render_template("index.html") - load static file
temp<link rel="stylesheet" href="static/styles.css">
- Chrome hard fresh
Chrome 會 cache 靜態文件,當更改文件後需要強制重新載入時,可按Shift+F5。
Day 57
Jinja 是 一種 Python 的模板語言,透過 Jinja 我們可以在 template 檔案中渲染 Python 傳遞過來的變數,也可以利用模板繼承的概念大幅簡化 html5 的撰寫- 傳遞參數或單行物件:
- .py
在 Route 的函式中回傳經過 render_template 渲染的 html,參數需以關鍵字參數傳入
@app.route('/guess/<string:name>')
def hello_world(name):
gender = "male"
age = 13
return render_template("index1.html", name=name, gender=gender, age=age)
- .html
在HTML中使用{{}}包住 Python 語法,告知Jinja如何渲染
<h1>Hey {{name.upper()}}</h1>
<p>I think you are {{gender}},</p><br>
<p>And maybe {{age}} years old</p><br>
- 嵌入 Python 語法
- If statement
{% if True %}
blabla
{% endif %}
- For loop
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %}
- Comment
{% raw %}
<ul>
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endraw %}
- 模板繼承:
- base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
- child.html
{% extends "base.html" %}
{% block title %}
Welocome
{% endblock %}
{% block content %}
<div class="jumbotron">
<div class="container">
<h1>Welcome</h1>
<p>Are you ready to discover my secret?</p>
<a href="#"><button class="btn btn-primary btn-lg">Login</button></a>
</div>
</div>
{% endblock %}
- 網頁跳轉:
- from_html
使用 url_for 函式,輸入導向頁面的 Route Function 名稱與其他參數
<a href={{url_for('post_page', post_id=i.id)}}>Read</a>
- .py
@app.route('/post/<int:post_id>')
def post_page(post_id):
post_item = [i for i in post_list if i.id == post_id][0]
return render_template("post.html", post=post_item)
- .py
在 Route 的函式中回傳經過 render_template 渲染的 html,參數需以關鍵字參數傳入@app.route('/guess/<string:name>')
def hello_world(name):
gender = "male"
age = 13
return render_template("index1.html", name=name, gender=gender, age=age) - .html
在HTML中使用{{}}包住 Python 語法,告知Jinja如何渲染<h1>Hey {{name.upper()}}</h1>
<p>I think you are {{gender}},</p><br>
<p>And maybe {{age}} years old</p><br>
- If statement
{% if True %}
blabla
{% endif %} - For loop
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %} - Comment
{% raw %}
<ul>
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endraw %}
- base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html> - child.html
{% extends "base.html" %}
{% block title %}
Welocome
{% endblock %}
{% block content %}
<div class="jumbotron">
<div class="container">
<h1>Welcome</h1>
<p>Are you ready to discover my secret?</p>
<a href="#"><button class="btn btn-primary btn-lg">Login</button></a>
</div>
</div>
{% endblock %}
- from_html
使用 url_for 函式,輸入導向頁面的 Route Function 名稱與其他參數<a href={{url_for('post_page', post_id=i.id)}}>Read</a>
- .py
@app.route('/post/<int:post_id>')
def post_page(post_id):
post_item = [i for i in post_list if i.id == post_id][0]
return render_template("post.html", post=post_item)
Day 58
Bootstrap
Bootstrap 是一個由 HTML、CSS 和 JavaScript 寫成的前端框架,可以讓你的網站排版自動適應螢幕大小,開法者只需要撰寫基本的 HTML 架構並加上 Bootstrap class 即可快速套用想要的格式設定。
- CSS 與 JS 檔案載入
<link rel="stylesheet" href="bootstrap-5.1.3-dist/bootstrap-5.1.3-dist/css/bootstrap.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" 後略
- Grid 佈局
<div class="row">
<div class=col-lg-6> #每列共劃分為12格,lg-6代表在電腦的尺寸上以6格顯示此標籤的內容
</div>
<div class=col-lg-6>
</div>
</div>
- 常用組件
- 導覽列
- 按鈕
- 輪播展示窗
- 卡片組
- ...
- CSS 與 JS 檔案載入
<link rel="stylesheet" href="bootstrap-5.1.3-dist/bootstrap-5.1.3-dist/css/bootstrap.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" 後略 - Grid 佈局
<div class="row">
<div class=col-lg-6> #每列共劃分為12格,lg-6代表在電腦的尺寸上以6格顯示此標籤的內容
</div>
<div class=col-lg-6>
</div>
</div> - 常用組件
- 導覽列
- 按鈕
- 輪播展示窗
- 卡片組
- ...
Day 59-62
修改現成的 Bootstrap 網頁範本,結合 Flask 建構一個簡易的 Blog 網站,簡而言之就是將之前 Day 56-58 學的東西結合在一起
- Flask WTForm and Bootstrap
利用這兩個套件,在.Py檔案中的路由函式傳入事先設定好的表單類別,在.html檔案中引用渲染表單的 marco ,即可快速在頁面上呈現表單。 - .py
from flask import Flask, render_template, request, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import EmailField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length
from flask_bootstrap import Bootstrap5
class MyForm(FlaskForm):
email = EmailField('User Email', validators=[DataRequired(), Email()])
password = PasswordField('User password', validators=[DataRequired(), Length(min=9)])
ok = SubmitField('submit-button')
app = Flask(__name__)
bootstrap = Bootstrap5(app)
app.secret_key = "hello?"
@app.route("/")
def home():
return render_template('index.html')
@app.route("/login", methods=["GET", "POST"])
def logina():
form = MyForm()
# 可替代驗證 request.method 為 POST 的部份
if form.validate_on_submit():
print(form.email.data)
print(form.password.data)
if form.password.data =='zxczxczxc':
return render_template('success.html')
else:
return render_template('denied.html')
return render_template('login.html', form=form)
- .html
{% from 'bootstrap5/form.html' import render_form %}
{% extends "bs5-base.html" %}
{% block title %}
Login
{% endblock %}
{% block content %}
<div class="container">
<h1>Login</h1>
{{ render_form(form) }}
</div>
{% endblock %}
利用這兩個套件,在.Py檔案中的路由函式傳入事先設定好的表單類別,在.html檔案中引用渲染表單的 marco ,即可快速在頁面上呈現表單。
- .py
from flask import Flask, render_template, request, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import EmailField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length
from flask_bootstrap import Bootstrap5
class MyForm(FlaskForm):
email = EmailField('User Email', validators=[DataRequired(), Email()])
password = PasswordField('User password', validators=[DataRequired(), Length(min=9)])
ok = SubmitField('submit-button')
app = Flask(__name__)
bootstrap = Bootstrap5(app)
app.secret_key = "hello?"
@app.route("/")
def home():
return render_template('index.html')
@app.route("/login", methods=["GET", "POST"])
def logina():
form = MyForm()
# 可替代驗證 request.method 為 POST 的部份
if form.validate_on_submit():
print(form.email.data)
print(form.password.data)
if form.password.data =='zxczxczxc':
return render_template('success.html')
else:
return render_template('denied.html')
return render_template('login.html', form=form) - .html
{% from 'bootstrap5/form.html' import render_form %}
{% extends "bs5-base.html" %}
{% block title %}
Login
{% endblock %}
{% block content %}
<div class="container">
<h1>Login</h1>
{{ render_form(form) }}
</div>
{% endblock %}
Day 63
SQLALchemy 是 Python 中的一款 ORM 框架,讓開發者可使用Python 的語法對資料庫進行操作,不需要直接寫 SQL 語法,可讀性較高,也可以防止 SQL injection,但因為多了一層 python 的包裝會犧牲掉部分的效能
- Initialize DB and table
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SECRET_KEY'] = 'blabla'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///book.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(250), unique=True, nullable=False)
author = db.Column(db.String(250), nullable=False)
rating = db.Column(db.Float, nullable=False
db.create_all()
- Create new record
new_book = Book(id=1, title="Harry Potter", author="J. K. Rowling", rating=9.3)
db.session.add(new_book)
db.session.commit()
- Read record
all_books = session.query(Book).all()
book = Book.query.filter_by(title="Harry Potter").first()
- Update record
book_to_update = Book.query.filter_by(title="Harry Potter").first()
book_to_update.title = "Harry Potter and the Chamber of Secrets"
db.session.commit()
- Delete record
book_id = 1
book_to_delete = Book.query.get(book_id)
db.session.delete(book_to_delete)
db.session.commit()
- Initialize DB and table
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)app.config['SECRET_KEY'] = 'blabla'
db = SQLAlchemy(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///book.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(250), unique=True, nullable=False)
author = db.Column(db.String(250), nullable=False)
rating = db.Column(db.Float, nullable=False
db.create_all() - Create new record
new_book = Book(id=1, title="Harry Potter", author="J. K. Rowling", rating=9.3)
db.session.add(new_book)
db.session.commit() - Read record
all_books = session.query(Book).all()
book = Book.query.filter_by(title="Harry Potter").first() - Update record
book_to_update = Book.query.filter_by(title="Harry Potter").first()
book_to_update.title = "Harry Potter and the Chamber of Secrets"
db.session.commit() - Delete record
book_id = 1
book_to_delete = Book.query.get(book_id)
db.session.delete(book_to_delete)
db.session.commit()
Day 65
- Web Design
- Colour Theory
- 互補(對比)色:可用在Logo標誌或是想凸顯的事物上,但盡量避免大範圍使用或是用在跟文字有關的元素上
- 近似色:常用在導航欄或是某元素及其背景色的搭配,創造出一種和諧、舒適的設計感
- 三等份:在色環上間隔120度取色,以其中一種顏色當作主色調,另兩種顏色作為重點的標註
- Typography
- Serif:傳統、穩定
- San-serif:簡單、直接、可讀性
- 在一種設計中盡量只用兩種字型
- 不要使用以下字型:Comic Sans, Kristen, Papyrus, Viner, curlz
- User interfacer Design
- Hierarchy
用顏色與大小建立層次感 - Layout
文字的長度最好在40~60字元之間,太長的文字不利使用者閱讀,太短則會有很重的中斷感 - Alignment
盡量讓元素間的對齊點減少,呈現一致 - White space
在元素的四周適當地保留一些空間,簡約的設計也能提升質感(如蘋果) - Audience
- User experience Design
- Simplicity
避免同時呈現太多不必要的的元素 - Consistency
不同頁面的功能呈現需一致 - Reading Patterns
參考 F or Z-pattern 的閱讀模式擺放元素 - All Platform Design
設計符合各式尺寸的呈現
- Web Design
- Colour Theory
- 互補(對比)色:可用在Logo標誌或是想凸顯的事物上,但盡量避免大範圍使用或是用在跟文字有關的元素上
- 近似色:常用在導航欄或是某元素及其背景色的搭配,創造出一種和諧、舒適的設計感
- 三等份:在色環上間隔120度取色,以其中一種顏色當作主色調,另兩種顏色作為重點的標註
- Typography
- Serif:傳統、穩定
- San-serif:簡單、直接、可讀性
- 在一種設計中盡量只用兩種字型
- 不要使用以下字型:Comic Sans, Kristen, Papyrus, Viner, curlz
- User interfacer Design
- Hierarchy
用顏色與大小建立層次感 - Layout
文字的長度最好在40~60字元之間,太長的文字不利使用者閱讀,太短則會有很重的中斷感 - Alignment
盡量讓元素間的對齊點減少,呈現一致 - White space
在元素的四周適當地保留一些空間,簡約的設計也能提升質感(如蘋果) - Audience
- User experience Design
- Simplicity
避免同時呈現太多不必要的的元素 - Consistency
不同頁面的功能呈現需一致 - Reading Patterns
參考 F or Z-pattern 的閱讀模式擺放元素 - All Platform Design
設計符合各式尺寸的呈現
沒有留言:
張貼留言