6.9 File Download
만약 사용자가 search query 없이 search 페이지로 이동하려 한다면
아래 화면 처럼 에러가 발생한다.
이런 에러 화면을 사용자에게 보여주지 않기 위해 코드를 수정해야 한다.
코드를 약간 변경해서 아무것도 작성하지 않을 때의 keyword 값을 알아본다.
< main.py > - print(keyword) 추가
from flask import Flask, render_template, request
from extractors.indeed import extract_indeed_jobs
from extractors.wwr import extract_wwr_jobs
app = Flask("JobScrapper")
db = {}
@app.route("/")
def home():
return render_template("home.html", name="james", age=20)
@app.route("/search")
def search():
keyword = request.args.get("keyword")
print(keyword)
if keyword in db:
jobs = db[keyword]
else:
indeed = extract_indeed_jobs(keyword)
wwr = extract_wwr_jobs(keyword)
jobs = indeed + wwr
db[keyword] = jobs
return render_template("search.html", keyword=keyword, jobs=jobs )
app.run("127.0.0.1")
크롬창 열고 127.0.0.1:5000/search 입력 시 ( 아무것도 입력하지 않았으므로 keyword 가 없는 구인정보가 검색됨 )
콘솔 창에는 None 이 print 된다.
사용자가 아무것도 작성하지 않을 떄의 keyword 값은 'None' 이다.
None 이란 무언가가 없다는 뜻 이다.
여기서 체크해야 할 것은, keyword 가 None 이랑 같은지를 확인해주면 된다.
만약 Keyword 가 None 이면 사용자를 검색하는 페이지 ( home.html )로 돌려보낸다.
이걸 위해서 Flask 에서 redirect funtion 이라는 것을 가져온다. → import redirect
( redirect : 다른 페이지로 연결해준다. )
< main.py >
from flask import Flask, render_template, request, redirect
from extractors.indeed import extract_indeed_jobs
from extractors.wwr import extract_wwr_jobs
app = Flask("JobScrapper")
db = {}
@app.route("/")
def home():
return render_template("home.html", name="james", age=20)
@app.route("/search")
def search():
keyword = request.args.get("keyword")
if keyword == None:
return redirect("/")
if keyword == "":
return redirect("/")
if keyword in db:
jobs = db[keyword]
else:
indeed = extract_indeed_jobs(keyword)
wwr = extract_wwr_jobs(keyword)
jobs = indeed + wwr
db[keyword] = jobs
return render_template("search.html", keyword=keyword, jobs=jobs )
app.run("127.0.0.1")
if keyword == None: ( search 라고 url 에 입력 시 )
→ keyword 가 없는 구인정보가 검색되지 않고 home.html ( 검색창 ) 으로 넘어간다 .
if keyword == "": ( 검색창에서 아무것도 입력 안하고 검색 시 - 공백으로 검색 시 )
→ 아까처럼 internal error 가 발생하지 않고 home.html ( 검색창 ) 으로 넘어간다.
빈칸으로 검색하거나, url 에 127.0.0.1:5000/search 라고 검색해도 아래와 같이 검색 화면 ( home.html ) 로 넘어간다.
keyword 관련 해서 헷갈리는 경우 아래 질문과 답을 참고하면 좋을거같다.
< 질문 >
강의 따라서 keyword에 대한 if문을 그대로 작성했는데 검색어 없이 search 버튼을 누르면 URL주소가 ../search?keyword= 로 바뀌면서 500 에러가 뜨고 직접 ../search로 URL을 수정해야만 홈으로 돌아가는 데 이유가 뭘까요?
< 답 >
강의에서 보여주는 "이상행동"은 URL 주소창에 keyword없이 "/search" 라고 예제입니다. 질문하신 홈페이지에서 keyword란에 아무것도 입력하지 않고, search buttom을 누르는 경우는 URL 주소창에 "/search?keyword=" 과 동일한 효과로 keword = "" 과 같은 빈문자열이 저장됩니다. 이경우 예외처리를 하려면 ' if keyword == "" ' 로 해야 합니다.
강의내용과 같이 적용하려면 ' if keyword == None ' 과 ' if keyword == "" ' 를 다같이 사용해야 합니다.
이제 구인정보를 extract 한 웹 페이지를 보는것 뿐만 아니라 csv 형태로 파일을 다운로드 할 수 있는 기능을 만든다.
검색창에서 nextjs 검색.
빨간 박스 안에 export 라는 링크를 만들어서 파일을 다운로드 할 수 있게 한다.
사용자가 export 를 누르면 내보내고 싶은 keyword 와 함께 export 페이지로 이동하게 한다.
여기서 일어나는 일은, 데이터 베이스에 접근해 jobs 를 파일 안에 넣은 다음 그 파일을 사용자에게 전달한다.
( indeed.py 와 wwr.py 에서 extract 한 구인정보들을 jobs 변수에 저장한 후, jobs 를 csv 파일에 넣고 사용자가 다운로드 )
'데이터 베이스에 접근한다고' 한 이유는 사용자가 이 페이지에 접근 한 후에는
이미 데이터 베이스에 데이터가 있기 때문이다. → 이미 데이터 베이스에 nextjs 라는 jobs(구인정보) 를 저장했다.
그래서 아래 url 처럼 같은 keyword 로 export 로 가면 데이터 베이스에서 nextjs jobs 를 찾을 수 있다.
http://127.0.0.1:5000/search?keyword=nextjs ( nextjs 검색 후 db 에 저장함. )
http://127.0.0.1:5000/export?keyword=nextjs ( 이미 db 에 저장된 nextjs 정보를 가져와서 export 하게 함. )
그리고 사용자가 search 페이지를 거치지 않고 오는 경우도 생각해야한다.
사용자는 반드시 search 페이지를 먼저 가야한다, 왜나하면 jobs (구인정보) 를 search 페이지에서 가져오기 때문에.
만약 그렇지 않다면 위에 했던거 처럼 redirect 해주면 된다.
( search 페이지에서 jobs 를 DB 에 저장하기 때문에 사용자는 먼저 search 에 가야한다.
search 로 먼저 가지 않으면 db 에는 사용자가 export 하는 keyword 가 없기 때문이다. )
home.html ( 검색창 ) → search.html ( keyword 검색 후 db 저장 ) → export ( db에 저장 된 keyword 파일로 다운로드 )
< main.py >
from flask import Flask, render_template, request, redirect
from extractors.indeed import extract_indeed_jobs
from extractors.wwr import extract_wwr_jobs
app = Flask("JobScrapper")
db = {}
@app.route("/")
def home():
return render_template("home.html", name="james", age=20)
@app.route("/search")
def search():
keyword = request.args.get("keyword")
if keyword == None:
return redirect("/")
if keyword == "":
return redirect("/")
if keyword in db:
jobs = db[keyword]
else:
indeed = extract_indeed_jobs(keyword)
wwr = extract_wwr_jobs(keyword)
jobs = indeed + wwr
db[keyword] = jobs
return render_template("search.html", keyword=keyword, jobs=jobs )
@app.route("/export")
def export():
keyword = request.args.get("keyword")
if keyword == None:
return redirect("/")
if keyword == "":
return redirect("/")
if keyword not in db:
return redirect(f"/search?keyword={keyword}")
app.run("127.0.0.1")
main.py 코드 작성 후 저장, 크롬 창 열고 127.0.0.1:5000 접속 후 ( 서버 재시작 )
127.0.0.1:5000/export 입력 시 바로 홈 ( 검색 창 ) 으로 redirect 된다.
아래 코드에 의해서.
127.0.0.1:5000/export?keyword=react 접속 시
127.0.0.1:5000/search?keyword=react 로 redirect 된다.
( react 관련 jobs(구인정보) 가 extract 되고 db 에 저장된다. )
아래 코드에 의해서.
이제 file_name 이랑 jobs 로 파일을 생성하는 function 을 import 해야한다. ( main.py 에 )
( 이전에 file.py 파일을 만들었었다. )
< file.py >
def save_to_file(file_name, jobs):
file = open(f'{file_name}.csv','w',encoding='utf-8-sig')
file.write('Postition,Company,Location,URL\n')
for job in jobs:
file.write(f"{job['position']},{job['company']},{job['location']},{job['link']}\n")
file.close()
save_to_file 함수의 기능을 그대로 가져온다.
save_to_file(file_name, jobs)
file_name 은 사용자가 찾고 있는 keyword 가 되고, jobs 는 db[keyword] 가 된다.
( jobs → indeed.py 와 wwr.py 에서 extract 한 구인정보 모음,
db[keyword] → db 는 search 에서 검색한 keyword 를 저장 )
파일을 보내기 위해서 send_file 이라는 function 도 import 한다.
save_to_file 로 서버의 파일 시스템에 파일을 저장한 뒤,
send_file() 을 return 한다.
send_file 은 첫 번째 argument 로 파일 이름이 있어야 한다.
파일 이름은 keyword 랑 csv 를 합친것이다. ( 아래 코드에 의해서. )
save_to_file 함수가 저장하는 이름을 그대로 쓴다.
그리고 다운로드가 실행되도록 as_attachment=True 코드도 추가한다.
( as_attachment=True 를 추가하면 "" 안에 이름 적은대로 저장된다. )
< 수정된 main.py 의 export 부분의 코드 일부분 >
@app.route("/export")
def export():
keyword = request.args.get("keyword")
if keyword == None:
return redirect("/")
if keyword == "":
return redirect("/")
if keyword not in db:
return redirect(f"/search?keyword={keyword}")
save_to_file(keyword, db[keyword])
return send_file(f"{keyword}.csv", as_attachment=True)
< 전체 main.py 코드 >
from flask import Flask, render_template, request, redirect, send_file
from extractors.indeed import extract_indeed_jobs
from extractors.wwr import extract_wwr_jobs
from file import save_to_file
app = Flask("JobScrapper")
db = {}
@app.route("/")
def home():
return render_template("home.html", name="james", age=20)
@app.route("/search")
def search():
keyword = request.args.get("keyword")
if keyword == None:
return redirect("/")
if keyword == "":
return redirect("/")
if keyword in db:
jobs = db[keyword]
else:
indeed = extract_indeed_jobs(keyword)
wwr = extract_wwr_jobs(keyword)
jobs = indeed + wwr
db[keyword] = jobs
return render_template("search.html", keyword=keyword, jobs=jobs )
@app.route("/export")
def export():
keyword = request.args.get("keyword")
if keyword == None:
return redirect("/")
if keyword == "":
return redirect("/")
if keyword not in db:
return redirect(f"/search?keyword={keyword}")
save_to_file(keyword, db[keyword])
return send_file(f"{keyword}.csv", as_attachment=True)
app.run("127.0.0.1")
이제 search.html 파일로 가서 export 페이지로 이동하는 링크를 추가해준다.
<hgroup>
<a target="_blank" href="export?keyword={{keyword}}">Export to file</a>
</hgroup>
target="_blank" → 새 탭에서 열기
< export 페이지로 이동하는 링크가 추가된 search.html 코드 >
<!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.0">
<title>Job Scrapper</title>
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
</head>
<body>
<main class="container">
<h1>Search Results for "{{keyword}}":</h1>
<hgroup>
<a target="_blank" href="export?keyword={{keyword}}">Export to file</a>
</hgroup>
<figure><table role="grid">
<thead>
<tr>
<th>Position</th>
<th>Company</th>
<th>Location</th>
<th>Link</th>
</tr>
</thead>
<tbody>
{% for job in jobs %}
<tr>
<td>{{job.position}}</td>
<td>{{job.company}}</td>
<td>{{job.location}}</td>
<td><a href="{{job.link}}" target="_blank">Apply now →</a></td>
</tr>
{% endfor %}
</tbody>
</table></figure>
</main>
</body>
</html>
main.py 의 search 함수는 사용자가 검색 한 keyword 를 잡아서, 그 keyword 를 template 으로 보내고 있다.
그래서 search template 은 사용자가 검색한 keyword 가 무엇인지 알 수 있다.
그래서 search.html 에서 export url를 추가할 때 href="export?keyword={{keyword}} 이런 식으로 만들 수 있었다.
search.html 과 main.py 파일을 저장한 후 main.py 실행
크롬창을 열고 127.0.0.1:5000 에 접속 후 python 을 검색해본다.
search.html 에 만들었던 Export to file 이 보인다.
Export to file 클릭 하면 python.csv 파일이 다운로드 된다.
python.csv 파일을 엑셀로 열면 아래와 같이 엑셀 파일로 볼 수 있다.
python 참고 강의
'Programming > Python 웹 스크래퍼 만들기' 카테고리의 다른 글
Python 웹 스크래퍼 & Flask 웹 사이트 구축 정리 (0) | 2023.01.11 |
---|---|
Python Flask 가짜 DB 만들어서 웹사이트 속도 향상 시키기 (0) | 2023.01.09 |
Flask 웹 사이트 Pico CSS 사용해서 꾸미기 (0) | 2023.01.06 |
Python Flask 로 HTML 에서 For 문 사용 (0) | 2023.01.05 |
Python Flask Arguments (0) | 2023.01.02 |