본문 바로가기
Portfolio & Toy-Project

임의 페이지 텍스트 크롤링 및 엑셀저장

by Mr.DonyStark 2024. 1. 30.

□ 목적 : 게시판 타이틀과 각 타이틀별 댓글까지 크롤링, 전처리하여 엑셀로 깔끔하게 자동 저장

□ 활용 라이브러리 및 사용이유

import requests  #크롤링 요청을 보낼 라이브러리
import openpyxl #엑셀 저장을 위해 사용
from bs4 import BeautifulSoup #크롤링 기능사용을 위해 호출한 라이브러리
from openpyxl.styles import Alignment, Font, colors, Border, Side  #엑셀 데이터 디자인 효과를 위해 호출

 

□ 세부내용

  ○ 특정 페이지 및 페이지의 게시글별 상세 페이지 데이터 크롤링

  ○ 임의 사이트(개인수강하는 곳에서 지원하는 웹)에서 추출하고자하는 데이터를 +1개씩 증가하며 코딩

  ○ 한개의 데이터 및 기능추가 시 함수를 정의하였음

****붉은색 영역은 게시글별 세부페이지므로 requsts.get 시 기존 'url데이터+게시글별 링크'를 조합 전처리하여 접근함

  ○ 크롤링 : 제목 데이터

    - 위 그림과 같이 푸른실선 영역의 태그값을 select으로 데이터 리스트화

     * select()함수는 타겟 사이트에서 파싱하여 가져온 데이터를 리스트형식으로 저장해줌

    - 아래 코드를 보면 div 태그의 클래스 네임이 list_item symph_row 인 영역을 대상으로 select하였고, 리스트화 된 데이터들 중 span 태그의 클래스 네임이 subject_fixed 인 데이터를 select_one하여 크롤링하였음

    - .get_text() 후 텍스트로 추출 후 앞뒤 공백제거를 위해 .strip()을 사용하였음

    - 게시글별 번호를 부여하기위해 for문 사용시 enumerate()함수를 지정하여 게시글에 따라 번호가 부여되도록 코딩

자료출처 : https://pythonblog.co.kr/coding/11/

# 함수정의 : 인덱스번호 + 제목
def CATCH_INDEX_TITLE():
    #크롤링 대상 사이트에 요청세팅
    my_Req = requests.get('크롤링 URL')
    my_Soup = BeautifulSoup(my_Req.content, 'html.parser')
    all_Data_area = my_Soup.select('div.list_item.symph_row') #div 태그 중 클래스네임이 list_item 인것 / select 은 데이터를 리스트화 함

    # 구분번호, 타이틀
    for index, v in enumerate(all_Data_area):
        title = v.select_one('span.subject_fixed')
        if title != None:  #select_one 메서드는 지정된 선택자에 매칭되는 태그를 찾지 못할 경우 None을 반환하는데 이경우에 바로 .get_text()를 사용하면 오류 발생되므로 예외처리 진행
            edit_Title = title.get_text().strip()
            print(index, edit_Title)

 

코드실행 결과

  ○ 크롤링 : 제목 데이터 + 댓글 수

    - '크롤링 : 제목 데이터' 에 이어서 작성

    - 댓글 수 또한 제목 데이터 크롤링한 방식으로 코딩하였고 출력시 앞/뒤 공백제거를 위해 .strip()를 사용했음

    - 댓글 수 연산을 나중에 처리할 가능성이 있다고 생각하여 int()로 미리 형변환  하였음

def CATCH_INDEX_TITLE_COMMENTCNT():
    #크롤링 대상 사이트에 요청세팅
    my_Req = requests.get('크롤링 url')
    my_Soup = BeautifulSoup(my_Req.content, 'html.parser')
    all_Data_area = my_Soup.select('div.list_item.symph_row') #div 태그 중 클래스네임이 list_item 인것 / select 은 데이터를 리스트화 함

    # 구분번호, 타이틀, 댓글 수
    for  index_numb, v in enumerate(all_Data_area):
        title = v.select_one('span.subject_fixed')
        comment = v.select_one('span.rSymph05')
        if title != None: #select_one 메서드는 지정된 선택자에 매칭되는 태그를 찾지 못할 경우 None을 반환하는데 이경우에 바로 .get_text()를 사용하면 오류 발생되므로 예외처리 진행
            edit_Title = title.get_text().strip()
            edit_Comment = int(comment.get_text().strip())
            print('○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●')
            print(f'인덱스번호\t:\t{index_numb} 번\n게시글 제목\t:\t{edit_Title}\n글수\t:\t{edit_Comment} 개')

코드실행 결과

 

  ○ 크롤링 : 제목 데이터 + 댓글 수 + 링크

    - '크롤링 : 제목 데이터 + 댓글 수' 에 이어서 작성

    -  특정 게시글 태그값의 하위로 있는 링크 태그의 속성값을 추출하기위해 'link['href']'을 사용하였음

# 함수정의 : 구분번호 + 제목 + 댓글수 + 링크
def CATCH_INDEX_TITLE_COMMENTCNT_LINK():
    #크롤링 대상 사이트에 요청세팅
    my_Req = requests.get('크롤링 url')
    my_Soup = BeautifulSoup(my_Req.content, 'html.parser')
    all_Data_area = my_Soup.select('div.list_item.symph_row') #div 태그 중 클래스네임이 list_item 인것 / select는 리스트로 가져옴
    
    # 구분번호, 타이틀, 댓글수, 링크
    for index_numb, v in enumerate(all_Data_area):
        title = v.select_one('span.subject_fixed')
        comment = v.select_one('span.rSymph05')
        link = v.select_one('a.list_subject')
        if title != None:
            edit_Title = title.get_text().strip()
            edit_Comment = comment.get_text().strip()
            edit_Link = link['href']  #link href 속성값
            print('○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●')
            print(f'인덱스번호\t:\t{index_numb} 번\n게시글 제목\t:\t{edit_Title}\n글수\t:\t{edit_Comment} 개\n게시글 링크\t:\t{edit_Link}')

코드실행 결과

  ○ 크롤링 : 제목 데이터 + 게시글별 댓글

    - '크롤링 : 제목 데이터 + 댓글 수' 에 이어서 작성

    -  게시글에 대한 세부페이지에 등록된 댓글을 추출

    - Selenium을 활용했다면 게시글 클릭 후 페이지 이동을 자동화하여 크롤링했겠지만, 이번에는 requests만 사용했기에 '크롤링 url값 + link['href'] 값'을 조합하여 세부내용 페이지에 requests.get()으로 데이터 크롤링 및 댓글을 추출하였음

    - 댓글 추출시 문단나뉨현상과 많은 뛰어쓰기 로 데이터 상태품질이 낮아 별도 가공함

    - 가공을 위해  .replace('\n','') , .replace('\t','')를 사용했음

# 함수정의 : 게시글별 댓글 추출
def NOTICE_COMMENT():
    #크롤링 대상 사이트에 요청세팅
    url = '크롤링 url'
    my_Req = requests.get(url)
    my_Soup = BeautifulSoup(my_Req.content, 'html.parser')
    all_Data_area = my_Soup.select('div.list_item.symph_row') #div 태그 중 클래스네임이 list_item 인것 / select는 리스트로 가져옴
    # 구분번호, 타이틀, 댓글수, 링크
    for index_numb, v in enumerate(all_Data_area[:5]):
        title = v.select_one('span.subject_fixed')
        comment = v.select_one('span.rSymph05')
        link = v.select_one('a.list_subject')
        if title != None:
            edit_Title = title.get_text().strip()
            edit_Comment = comment.get_text().strip()
            edit_Link = link['href']  #link href 속성값
            edit_Link_second = edit_Link.replace('news/','') #edit_link에서 news/를 공부으로 대체하는 전처리
            edit_url = url.replace('.html','/') #기존 url 크롤링 페이지에서 .html을 / 로 대체하는 전처리
            
            #게시글 상세보기 → 해당 상세보기페이지
            detail_Content = edit_url + edit_Link_second
            #상세보기 페이지에 데이터 요청세팅
            my_Req_Second = requests.get(detail_Content)
            my_Soup_Second = BeautifulSoup(my_Req_Second.content,'html.parser')
            #select 문으로 div태그 중 comment_view 네임인 데이터들을 리스트로 선택
            comment_All = my_Soup_Second.select('div.comment_view')
            for i in comment_All:
                detial_Comment = i.get_text().strip().replace('\n',' ').replace('\t',' ')
            print(f'★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\n{edit_Title} 댓글\n{detial_Comment}')

 

  ○ 크롤링 : 엑셀저장

    - 상위 작성한 코드들 기반하여 openpyxl 라이브러리 활용하여 엑셀저장 및 커스터마이징하여 데이터 저장 실행

#### 엑셀 기본 코드
import requests, openpyxl
from bs4 import BeautifulSoup

excel_file = openpyxl.Workbook() #Workbook 생성
excel_sheet = excel_file.active #workbook 활성화
excel_sheet.title = '변경할 시트명' # active 시트 이름 변경
excel_file.save('경로/파일명.xlsx') # 파일저장 경로 지정
excel_sheet.append(['Value 1', 'Value 2', 'Value 3']) #엑셀 필드명 지정
excel_sheet.column_dimensions['A'].width = 20 #엑셀 필드 너비 지정

font = Font(bold=True, color='FF0000', underline='single') #폰트 지자인 적용 : bold, 칼러 지정, 밑줄 지정
cell.font = font

cell.hyperlink = 'url주소' #하이퍼링크 지정

alignment = Alignment(horizontal='center', vertical='top') #셀 수직, 수평 정렬위치 지정
cell.alignment = alignment

alignment = Alignment(wrap_text=True) #자동 줄바꿈 지정
cell.alignment = alignment

#셀 테두리 지정
from openpyxl.styles import Border, Side

thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin')) #얇은 테두리
cell.border = thin_border


thick_border = Border(left=Side(style='thick'), right=Side(style='thick'), top=Side(style='thick'), bottom=Side(style='thick')) #두꺼운 테두리
cell.border = thick_border

#셀 배경색 지정
from openpyxl.styles import PatternFill

fill = PatternFill(start_color='FF0000', end_color='FF0000', fill_type='solid')
cell.fill = fill

 

    - 기존 데이터 크롤링 코드에 위에서 언급한 엑셀 커스터마이징을 위한 코드를 적절히 사용

# 함수정의 : 엑셀저장
def SAVE_EXCEL():
    #엑셀설정
    excel_file = openpyxl.Workbook() #파일생성
    excel_sheet = excel_file.active #시트선택
    excel_sheet.title = '크롤링 데이터' #시트명 변경
    field_name = ['구분','제목','댓글'] #필드명 지정
    excel_sheet.append(field_name)
    #엑셀설정 : 디자인
    wrap_alignment = Alignment(wrap_text=True) #줄바꿈
    center_alignment = Alignment(horizontal='center', vertical='center') #수평/수직 정렬
    bold_font = Font(bold=True)
    hyperlink_font = Font(color = colors.BLUE, underline = 'single')
    header_font = Font(bold=True, size=20)  #엑셀 필드명 스타일
    # thin_border = Border(left=Side(style='thin'), 
    #                  right=Side(style='thin'), 
    #                  top=Side(style='thin'), 
    #                  bottom=Side(style='thin'))
    # ↑ 테두리효과
    for cell in excel_sheet[1]:  
        cell.font = header_font   #필드명에 디자인 효과 적용
        cell.alignment = center_alignment #필드명 정렬
    excel_sheet.column_dimensions['A'].width = 10
    excel_sheet.column_dimensions['B'].width = 60
    excel_sheet.column_dimensions['C'].width = 80    
    
    #크롤링 대상 사이트에 요청세팅
    url = '크롤링 url'
    my_Req = requests.get(url)
    my_Soup = BeautifulSoup(my_Req.content, 'html.parser')
    all_Data_area = my_Soup.select('div.list_item.symph_row') #div 태그 중 클래스네임이 list_item 인것 / select는 리스트로 가져옴
    row=2 #엑셀디자인을 위해 행수세팅
    # 구분번호, 타이틀, 댓글수, 링크
    for index_numb, v in enumerate(all_Data_area[:5]):
        title = v.select_one('span.subject_fixed')
        comment = v.select_one('span.rSymph05')
        link = v.select_one('a.list_subject')
        if title != None:
            edit_Title = title.get_text().strip()
            edit_Comment = '[' + comment.get_text().strip() + ']'
            edit_Link = link['href']  #link href 속성값
            edit_Link_second = edit_Link.replace('news/','') #edit_link에서 news/를 공부으로 대체하는 전처리
            edit_url = url.replace('.html','/') #기존 url 크롤링 페이지에서 .html을 / 로 대체하는 전처리
            #게시글 상세보기 → 해당 상세보기페이지
            detail_Content = edit_url + edit_Link_second
            #데이터 엑셀에 적재
            core_data_1 = [index_numb, edit_Title, edit_Comment]
            excel_sheet.append(core_data_1)
            excel_sheet.cell(row = row, column = 2).hyperlink = detail_Content  #2번째 컬럼에 하이퍼링크 적용
            excel_sheet.cell(row = row, column = 2).font = hyperlink_font  #2번째 컬럼에 색상 적용
            #데이터 엑셀에 적재 : 디자인 적용
            for cell in excel_sheet[row]:
                cell.alignment = wrap_alignment #자동 줄바꿈
                if cell.column != 2:
                    cell.font = bold_font #bold 효과
            row += 1 #행 추가시 디자인 효과 적용

            #상세보기 페이지에 데이터 요청세팅
            my_Req_Second = requests.get(detail_Content)
            my_Soup_Second = BeautifulSoup(my_Req_Second.content,'html.parser')
            #select 문으로 div태그 중 comment_view 네임인 데이터들을 리스트로 선택
            comment_All = my_Soup_Second.select('div.comment_view')
            for i in comment_All:
                detial_Comment = i.get_text().strip().replace('\n',' ').replace('\t',' ')
                #데이터 엑셀에 적재
                core_data_2 = ['','',detial_Comment]
                excel_sheet.append(core_data_2)
                #데이터 엑셀에 적재 : 디자인 적용
                for cell in excel_sheet[row]:
                    cell.alignment = wrap_alignment #자동 줄바꿈
                row += 1 #행 추가시 디자인 효과 적용

    #엑셀 저장 및 종료
    excel_file.save('C:/python/api_crawling/크롤링_토이프로젝트.xlsx')
    excel_file.close()

    SAVE_EXCEL()

크롤링 데이터 엑셀저장. (테두리색 지정관련 코드는 작성했지만 별도 적용하지 않았음)