Skip to content

Commit

Permalink
Merge pull request #7 from NPC-GO/multiple-boards
Browse files Browse the repository at this point in the history
add features -> Multiple boards, the prev status can be storage to browser by cookies.
  • Loading branch information
Xanonymous-GitHub committed Mar 28, 2020
2 parents 44a7143 + 5ba0455 commit 604e73b
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 31 deletions.
14 changes: 11 additions & 3 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,26 @@ def give_html():

@app.route('/api/articles/<article_id>/info', methods=['GET'])
def article_info(article_id):
return cra.get_article(article_id, is_include_content=False)
board = request.args.get('board')
return cra.get_article(article_id, board, is_include_content=False)


@app.route('/api/articles/<article_id>', methods=['GET'])
def article_content(article_id):
return cra.get_article(article_id, is_include_content=True)
board = request.args.get('board')
return cra.get_article(article_id, board, is_include_content=True)


@app.route('/api/articles', methods=['GET'])
def article_ids():
page = request.args.get('page')
return cra.get_article_ids(page)
board = request.args.get('board')
return cra.get_article_ids(board, page)


@app.route('/api/boards', methods=['GET'])
def get_boards():
return cra.get_popular_boards()


if __name__ == '__main__':
Expand Down
28 changes: 22 additions & 6 deletions crawler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@


# 取出頁數或是 id
def get_last_session_of_url(url):
def get_last_session_of_url(url) -> str:
url_split = url.split('/')
page_or_id = url_split[3]
page_or_id = page_or_id.replace('index', '')
page_or_id = page_or_id.replace('.html', '')
return page_or_id


def get_article(id_, is_include_content=False):
url = 'https://www.ptt.cc/bbs/Gossiping/' + id_ + '.html'
def get_article(id_, board="Gossiping", is_include_content=False) -> dict:
base_url = "https://www.ptt.cc/bbs/"
if board is None:
board = "Gossiping"
url = base_url + board + "/" + id_ + '.html'

# 以 GET 傳請求給目標伺服器,伺服器回傳 response 物件
# response 接收回傳值
Expand Down Expand Up @@ -51,13 +54,16 @@ def get_article(id_, is_include_content=False):
return articles


def get_article_ids(page=""):

def get_article_ids(board="Gossiping", page="") -> dict:
base_url = "https://www.ptt.cc/bbs/"
if page is None:
page = ""

if board is None:
board = "Gossiping"

# format 中的內容會替換 {} 所在的位置
url = 'https://www.ptt.cc/bbs/Gossiping/index{}.html'.format(page)
url = base_url + board + '/index{}.html'.format(page)
cookies = dict(over18="1")

response = requests.get(url, cookies=cookies)
Expand Down Expand Up @@ -96,3 +102,13 @@ def get_article_ids(page=""):
error = soup.find('title')
error_message = dict(error=error.get_text())
return error_message


def get_popular_boards() -> dict:
url = "https://www.ptt.cc/bbs/index.html"
response = requests.get(url)
if not response or response.status_code != 200:
return dict(boards=["Gossiping"])
soup = BeautifulSoup(response.text, 'html.parser')
board_names = [div.get_text() for div in soup.find_all("div", class_="board-name")]
return dict(boards=board_names)
13 changes: 8 additions & 5 deletions website-new/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ html, body {
#main-container .main-text {
font-size: xx-large;
font-weight: bolder;
padding: 20px 0 10px;
padding: 20px 0 0;
}

.colorful-line {
Expand Down Expand Up @@ -110,9 +110,11 @@ html, body {

#main-title {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
justify-content: start;
flex-wrap: nowrap;
align-items: end;
-webkit-align-items: flex-end;
white-space: nowrap;
}

#page-title {
Expand Down Expand Up @@ -190,12 +192,13 @@ html, body {
}

#article-container .title-text {
font-size: xx-large;
font-size: x-large;
font-weight: bold;
margin: 0 10px 10px;
padding: 10px 0 0;
white-space: nowrap;
overflow: scroll;
overflow: hidden;
text-overflow: ellipsis;
}


Expand Down
6 changes: 5 additions & 1 deletion website-new/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
<div id="page-title">
<div id="main-title">
<span class="main-text">
✾ Hey! PTT <span id="board-name">Gossiping</span>
✾ Hey! PTT
</span>
<span id="board-name">
<label for="boards"></label>
<select id="boards"></select>
</span>
</div>
<div class="colorful-line"></div>
Expand Down
84 changes: 68 additions & 16 deletions website-new/js/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
let prevPage;
let currentBoard = getCookie("board") || "Gossiping";
let prevPage, boards;
let loadMoreButton = document.getElementById("load-more-button");
let loadingIcon = document.getElementById("loading-icon");
let loadingIconChildren = loadingIcon.children;
Expand All @@ -22,11 +23,15 @@ let loadingIconChildren = loadingIcon.children;
}
};

let boardsSelectBox = document.getElementById("boards");
boardsSelectBox.addEventListener("change", changeBoard);

let colorBars = document.querySelectorAll(".colorful-line");
const randomColor = () => Math.floor(Math.random() * 16777215).toString(16);
colorBars.forEach((coloBar) => {
coloBar.style.background = "linear-gradient(to top right," + "#" + randomColor() + "," + "#" + randomColor() + ")";
});
await showBoardsSelectorOptions();
await showList();
}());

Expand Down Expand Up @@ -54,6 +59,17 @@ function request(url, method, parameters, ...header) {
});
}

function getCookie(name) {
let cookieArr = document.cookie.split(";");
for (let i = 0; i < cookieArr.length; i++) {
let cookiePair = cookieArr[i].split("=");
if (name === cookiePair[0].trim()) {
return decodeURIComponent(cookiePair[1]);
}
}
return null;
}

function errorHandler(e) {
loadingIconChildren[0].style.display = "inline";
loadingIconChildren[0].textContent = e.slice(0, 3) + " ERROR";
Expand All @@ -66,43 +82,79 @@ function leaveError() {
loadingIconChildren[1].style.display = "inline";
}

async function getArticleWithContent(articleId) {
return await request("/api/articles/" + articleId, "GET")
.catch((e) => errorHandler(e));
async function getArticleWithContent(articleId, board) {
return await request("/api/articles/" + articleId, "GET", `board=${board || currentBoard}`)
.catch((e) => errorHandler(e)) || "";
}

async function getArticleTitle(articleId, board) {
return await request("/api/articles/" + articleId + "/info", "GET", `board=${board || currentBoard}`)
.catch((e) => errorHandler(e)) || "";
}

async function getArticleTitle(articleId) {
return await request("/api/articles/" + articleId + "/info", "GET")
async function getList(page, board) {
let list = await request("/api/articles", "GET", `page=${page || ""}&board=${board || currentBoard}`)
.catch((e) => errorHandler(e));
return [list["articles"] || [], list["prev"] || []];
}

async function getList(page) {
let list = await request("/api/articles", "GET", `page=${page || ""}`)
async function getBoards() {
let boards = await request("/api/boards", "GET")
.catch((e) => errorHandler(e));
return [list["articles"], list["prev"]];
return boards["boards"] || {boards: "Gossiping"};
}

async function showBoardsSelectorOptions() {
leaveError();
loadingIcon.style.display = "flex";
boards = await getBoards();
let boardsSelectBox = document.getElementById("boards");
boards.forEach(board => {
let option = document.createElement("option");
option.text = board;
boardsSelectBox.add(option);
});
let inferredBoardIndex = getCookie("board-index") || 0;
if (boardsSelectBox[inferredBoardIndex] !== currentBoard) {
boardsSelectBox.selectedIndex = boards.findIndex(board => board === currentBoard);
} else {
boardsSelectBox.selectedIndex = inferredBoardIndex;
}
loadingIcon.style.display = "none";
}

async function changeBoard() {
let listContainer = document.getElementById("list");
listContainer.innerHTML = "";
currentBoard = boards[this.selectedIndex];
await showList("", currentBoard);
document.cookie = `board=${currentBoard}`;
document.cookie = `board-index=${this.selectedIndex}`;
}

async function showList(page) {
async function showList(page, board) {
leaveError();
loadingIcon.style.display = "flex";
loadMoreButton.classList.add("disabled");
loadMoreButton.disabled = true;
loadMoreButton.textContent = "載入中...";
let list;
[list, prevPage] = await getList(page);
[list, prevPage] = await getList(page, board);
let listContainer = document.getElementById("list");
let progressBar = document.getElementsByClassName("progress-bar")[0];
let partOfProgress = 100 / list.length;
let progressBarStatus = 0;
progressBar.parentNode.style.display = "flex";
progressBar.style.width = "0";
(await Promise.all(list.map(async (articleId, index) => {
let cardData = await getArticleTitle(articleId).catch(e => ({
(await Promise.all(list.map(async (articleId) => {
let cardData = await getArticleTitle(articleId, board).catch(e => ({
title: "無法載入文章",
time: e,
author: "",
disabled: true
}));
progressBar.style.width = (index + 2) * partOfProgress + "%";
progressBarStatus += partOfProgress;
progressBar.style.width = progressBarStatus + "%";
await new Promise(resolve => setTimeout(() => resolve(), 800));
return {
title: cardData["title"],
Expand Down Expand Up @@ -148,10 +200,10 @@ async function showList(page) {
loadingIcon.style.display = "none";
}

async function showArticle(articleId) {
async function showArticle(articleId, board) {
leaveError();
loadingIcon.style.display = "flex";
let article = await getArticleWithContent(articleId);
let article = await getArticleWithContent(articleId, board);
if (article === undefined) {
errorHandler("404");
return;
Expand Down

0 comments on commit 604e73b

Please sign in to comment.