첫 번째 프로젝트로 전국 자동차 등록 현황 및 기업 FAQ 조회 시스템을 만드는 것을 해보았습니다.
2주라는 과정에서 빠르게 진도를 나가면서 뭔가 후다닥 지나간 것 같네요.
1. 전국 자동차 등록 현황
자동차 등록현황 데이터는 아래의 링크에서 API키를 사용해 데이터를 불러왔습니다.
국토교통 통계누리
stat.molit.go.kr
2. 기업 FAQ
네비 회사 3곳으로 카카오맵, 네이버 지도, 아틀란(네이버 대체), KT원네비에서 FAQ를 크롤링해왔습니다.
저의 경우 아틀란 FAQ 데이터 크롤링과 데이터 프레임화, DB와 연결하는 역할을 담당하였습니다.
○ 크롤링 할 때 사용 : requests와 BeautifulSoup
2024.07.22 - [컴퓨터 공학/Networks] - SK networks AI Camp - Crawling
SK networks AI Camp - Crawling
Web Crawling vs Web Scraping○ Wep Scraping : 웹 사이트 상에서 원하는 정보를 추출하는 방법○ Wep Crawling : 웹 크롤러가 정해진 규칙에 따라 복수 개의 웹 페이지를 탐색하는 행위(= 웹 스파이더링)크롤링
joowon582.tistory.com
2024.07.22 - [컴퓨터 공학/Networks] - SK networks AI Camp - Crawling(2)
SK networks AI Camp - Crawling(2)
시작하기 전에 가상환경 설정(python version : 3.12) 과 jupyter, requests, beautifulsoup, selenium을 설치해줄게요.못 하시겠으면 아래의 글을 참고해주세요.2024.07.08 - [컴퓨터 공학/Networks] - SK networks AI Camp - Pyt
joowon582.tistory.com
○ 데이터 프레임 생성 : pandas
○ MySQL에 데이터 프레임 전송 : sqlalchemy, create_engine
https://patrickstar-jjh.tistory.com/46
SQLAchemy를 활용하여 MySQL에 주가데이터 저장하기
- SQLite의 경우에는 pandas DataFrame의 to_sql메서드를 통해서 바로 db에 데이터입력이 가능하다. - 하지만 다른 종류의 database를 활용한다면 SQLAchemy를 활용해서 판다스의 데이터프레임 형태를 db에 저
patrickstar-jjh.tistory.com
1. url을 통하여 아틀란 FAQ 페이지로 이동
나중에 DB연결을 하면서 카카오 네비 크롤링 주소를 확인해보니
(2번째 페이지로 넘어갔다가 1번을 다시 누르면 page=1라고 뜨더라고요.)
그걸 보니 페이지 수만큼 for문을 돌려서 한번에 받아오면 좋겠다는 생각을 했습니다.
시작하기 전에 제대로 연결되었는 지 확인을 위해 status_code를 돌려보았습니다.
그 결과 정상 연결이 되었다는 200이 떠서 다음 단계로 진행했습니다.
import requests
from bs4 import BeautifulSoup as bs
import pandas as pd
# pip install pandas sqlalchemy mysql-connector-python
from sqlalchemy import create_engine
custom_header = {
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
"referer": "https://www.atlan.co.kr/support/faq/list.do"
}
# 웹 페이지 요청
url = 'https://www.atlan.co.kr/support/faq/list.do?page=1' #url이라는 변수에 크롤링 할 사이트 url을 담기.
response = requests.get(url, headers=custom_header)
response.raise_for_status()
response.status_code
2. FAQ 페이지 연결 및 질문, 키워드, 답변 받아오기
웹페이지가 나오는 규칙을 통해 page=1 부터 13까지 연결되는 것을 알수 있었어요.
그래서 for문으로 url을 수정하고 연결하여 각 리스트에 한번에 저장을 하였습니다.
question = []
keywords = []
answer = []
# 웹 페이지 변경
for i in range(13):
url = 'https://www.atlan.co.kr/support/faq/list.do?page='
url += str(i+1)
response = requests.get(url)
soup = bs(response.text, "html.parser")
question.append(soup.find_all('span', class_='faq_subject'))
keywords.append(soup.find_all('span', class_='sub_category'))
answer.append(soup.find_all('div', class_= "faq_answer"))
3. 질문 리스트 전처리
크롤링 결과 중복이 되어 받아지는 경우가 있었습니다. 어떤 문제로 중복이 되었는지는 모르겠지만 확인을 위하여
set을 통하여 중복을 제거해주었습니다.
question에는 html 코드가 들어가있어서 text만 추출하는 코드를 작성해주었습니다.
또한 앞뒤 공백이 있는 경우를 생각하여 strip으로 공백제거를 해주었습니다.
question_texts = []
seen_texts = set() # 중복 확인을 위한 집합
# 각 question 리스트의 요소에서 텍스트만 추출
for q_list in question:
for span in q_list:
text = span.text.strip() # .strip()으로 앞뒤 공백 제거
# 중복제거
if text not in seen_texts:
seen_texts.add(text) # 텍스트를 집합에 추가
question_texts.append(text) # 리스트에 텍스트 추가
4. 키워드 리스트 전처리
처음에 키워드만 따로 빼서 생각하지 않아서 안 만들었다가 위의 질문 리스트 전처리 코드를 재활용하여 사용했습니다.
keywords_in_FAQ = []
keywords_texts = []
seen_keywords = set() # 중복 확인을 위한 집합
# 각 keywords 리스트의 요소에서 텍스트만 추출
for k_lists in keywords:
for span in k_lists:
text = span.text.strip() # .strip()으로 앞뒤 공백 제거
keywords_in_FAQ.append(text)
# 텍스트가 이미 본 적이 없는 경우에만 추가
if text not in seen_keywords:
seen_keywords.add(text) # 텍스트를 집합에 추가
keywords_texts.append(text) # 리스트에 텍스트 추가
5. 답변 리스트 전처리
이것도 키워드, 질문과 같이 똑같은 형태로 전처리를 해줬습니다.
<에러>
여기서 question, kewords는 잘 받아왔는데 answer가 계속 데이터 처리가 잘못되었습니다.
<해결법>
그냥 다시 question, keywords와 똑같은 로직으로 돌려보니 해결되었습니다. 받아왔을 때 데이터에 사진과 다른 것들이 있어서 신경을 썼더니 오류가 발생했었는데 아래의 로직으로도 해결되었습니다.
<느낀점>
다른 상태(예외 등;)을 신경쓴다고 길게 작성했더니 오류가 발생하고 간단하지만 필요한 것만 적었더니 해결이 되었습니다.
확실히 데이터의 흐름이나 구조를 확인하고 작성해야겠습니다.
제대로 작성한 코드
answer_texts = []
seen_answer = set() # 중복 확인을 위한 집합
# 각 keywords 리스트의 요소에서 텍스트만 추출
for a_lists in answer:
for span in a_lists:
text = span.text.strip() # .strip()으로 앞뒤 공백 제거
# 텍스트가 이미 본 적이 없는 경우에만 추가
if text not in seen_answer:
seen_answer.add(text) # 텍스트를 집합에 추가
answer_texts.append(text) # 리스트에 텍스트 추가
128개 중 28개만 받아오는 코드로 잘못작성한 코드
from bs4 import BeautifulSoup as bs
answer_texts = []
seen_texts = set() # 중복 확인을 위한 집합
# 각 answer 리스트의 요소에서 텍스트만 추출
for a_lists in answer:
combined_text = ""
for div in a_lists:
# BeautifulSoup 객체 생성
soup = bs(str(div), 'html.parser')
# 모든 <img> 태그를 찾아 제거
for img in soup.find_all('img'):
img.decompose() # 태그 제거
# 수정된 HTML에서 텍스트 추출
text = soup.get_text(separator=' ', strip=True) # separator를 사용해 텍스트를 공백으로 구분하고 앞뒤 공백 제거
combined_text += text + "\n\n" # 각 텍스트 블록을 연속적으로 추가, 단락 구분을 위해 두 개의 줄바꿈 추가
combined_text = combined_text.strip() # 결합된 텍스트에서 앞뒤 공백 제거
# 중복 확인 후 텍스트를 추가
if combined_text not in seen_texts and combined_text:
seen_texts.add(combined_text) # 텍스트를 집합에 추가
answer_texts.append(combined_text) # 리스트에 텍스트 추가
# 인덱스 10부터 끝까지 처리
def split_texts_by_double_newline(texts):
split_texts = []
for text in texts:
# 줄바꿈 두 개를 기준으로 텍스트를 나누기
parts = text.split('\n\n')
split_texts.extend(part.strip() for part in parts if part.strip())
return split_texts
# 기존 answer_texts의 10번째 인덱스부터 끝까지 처리
if len(answer_texts) > 10:
# 인덱스 10부터 끝까지 분리
split_parts = split_texts_by_double_newline(answer_texts[10:])
# 기존 인덱스 10부터 끝까지의 데이터를 분리한 리스트로 대체
answer_texts = answer_texts[:10] + [text for text in split_parts if text not in seen_texts]
# 결과 확인
for index, text in enumerate(answer_texts):
print(f"Index {index}:")
print(text)
print("="*50) # 각 텍스트 블록을 구분하기 위해 구분선 추가
6. 길이 확인
질문, 키워드를 눌러서 작동할 수 있도록 목록(중복제거한 것), 키워드 목록, 답변을 DataFrame에 넣기 전에 갯수를 확인해주었습니다. 키워드 목록 갯수와 전체 글 갯수가 동일한 것을 확인했습니다.
len(question_texts), len(keywords_texts), len(keywords_in_FAQ), len(answer_texts) # 확인
7. DataFrame 생성
pandas를 사용해서 Data프레임을 생성했습니다. 결과 확인을 하였더니 예쁘게 표로 나왔습니다.
import pandas as pd
# 데이터프레임 생성
df = pd.DataFrame({
'Keywords' : keywords_in_FAQ,
'Question': question_texts,
'Answer': answer_texts
})
# 결과 확인
df
8. sqlAlchemy 설치 및 DB 연결
pip install pandas sqlalchemy mysql-connector-python
sqlAlchemy의 create_engine 모듈을 통하여 MySQL과 연결하려고 하였습니다.
임시로 만들어 놓은 데이터 베이스(DB ever에 dbname 이라는 DataBase)에 저장하도록 하였습니다.
그리고 아래의 구문처럼 작성하여 연결을 해주고 DataFrame을 그대로 DB에 저장했습니다.
또한 table이름은 faq_data로 해주고 성공을 하여 -1일 출력이 되었습니다.
from sqlalchemy import create_engine
# MySQL 데이터베이스 연결 정보 설정
user = 'user'
password = 'user1234'
host = 'localhost'
database = 'dbname'
# SQLAlchemy 사용
engine = create_engine(f'mysql+mysqlconnector://{user}:{password}@{host}/{database}')
# 데이터프레임을 MySQL 테이블로 저장
table_name = 'faq_data'
df.to_sql(name=table_name, con=engine, if_exists='replace', index=False)
그 결과 DBever에서 root 계정에 dbname이라는 Database에 faq_data 라는 이름으로 데이터가 저장되었습니다.
이제 프론트와 연동하고 전체 코드가 나오면 다시 포스팅하면서 정리하겠습니다.
'Networks > Project' 카테고리의 다른 글
SK networks AI Camp - Toy Project(에자일_1) (0) | 2024.08.10 |
---|---|
SK networks AI Camp - 야구 데이터 분석하기(2) feat. Lotte giants (0) | 2024.08.05 |
SK networks AI Camp - Toy Project (0) | 2024.08.02 |
SK networks AI Camp - 야구 데이터 분석해보기(1) feat. Lotte giants (0) | 2024.07.31 |
SK networks AI Camp - mini project(2) (0) | 2024.07.26 |