본문 바로가기

Python/crawling

[Python] BeautifulSoup으로 수집한 데이터를 txt 파일로 저장하기

실제 뉴스 기사에서 표 데이터를 추출하고, txt 파일로 저장해보았다.

(https://github.com/aruymeek/python_BeautifulSoup/tree/master/crawlingtest)

 

 

https://www.corporateknights.com/reports/2020-global-100/2020-global-100-ranking-15795648/

 

2020 Global 100 ranking | Corporate Knights

  For more detailed dataset, return to 2020 Global 100 landing page and click 'Free results.'   Want to dive deep into the data? Purchase our

www.corporateknights.com

위와 같은 표에서 데이터를 추출하려고 한다.

 

1. 클래스 생성하기

먼저, class를 활용하여 편리하게 처리하기 위해 CompanyModel 클래스를 생성했다.

class CompanyModel:

    def __init__(self, _name, _cate, _country, _score, _rank20, _rank19):
        self.name = _name
        self.category = _cate
        self.country = _country
        self.score = _score
        self.rank2020 = _rank20
        self.rank2019 = _rank19

    def SaveFormat(self):
        data = '{0};{1};{2};{3};{4};{5}'.format(self.name, self.category, self.country, self.score, self.rank2020, self.rank2019)

        return data

CompanyModel 클래스로 만든 객체는 name, category, country, score, rank2020, rank2019 등의 값을 갖고 있다. 각각의 속성 값을 출력하는 SaveFormat 메서드도 추가하였다.

 

2. 표 형태의 자료 가져오기

필요한 모듈을 import 해준다. requests는 https(url, 웹)에서 html 소스 코드를 가져오는 데에 필요하다.

import requests
from bs4 import BeautifulSoup

 

이제 뉴스 기사 페이지의 html을 가져온다.

url = 'https://www.corporateknights.com/reports/2020-global-100/2020-global-100-ranking-15795648/'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')

requests.get(url)

위와 같이, requesets.get(url)만으로는 python이 읽어들이는 객체 구조를 만들어낼 수가 없다. 따라서 BeautifulSoup()을 함께 사용해야 한다.

 

BeautifulSoup(response.text, 'html.parser')

BeautifulSoup()requests를 통해 얻은 html을 python이 읽어들일 수 있는 객체로 변환해준다(parsing). 첫번째 인자인 response.textrequests로 얻은 html을 문자열로 반환한 것을 의미한다. 두번째 인자는 어떤 parser를 사용할 것인지를 확인하는 부분인데, 'html.parser'는 기본적으로 내장되어 있는 parser이다. 설치가 필요 없고, 속도도 적당해 사용하기 좋은 것 같다.

 

아무튼, 이렇게 soup에 뉴스 기사 페이지의 html을 담았다.

 

tbody = soup.select_one('#table_1 tbody')

id가 '#table_1'인 <table> 하위의 <tbody>는 한 개 존재하므로 select_one()을 사용하여 <tbody> 태그를 추출하여 tbody라고 지정한다.

 

companyList = []

for tr in tbody.select('tr'):
    rank2020 = int(tr.select('td')[0].text)  
    
    rank2019 = tr.select('td')[1].text            
    if rank2019.isdigit() == False:                   
        rank2019 = rank2020
    else:
        rank2019 = int(rank2019)

    name = tr.select('td')[2].text    

    category = tr.select('td')[3].text 
           
    country = tr.select('td')[4].text             

    score = float(tr.select('td')[5].text.replace('%', ''))

    company = CompanyModel(name, category, country, score, rank2020, rank2019)
    companyList.append(company)

기사 내용 표에 포함된 기업의 정보를 담기 위해 companyList라는 빈 리스트를 먼저 만들어둔다.

 

<tbody> 태그 안에 각 기업의 정보들이 <tr> 태그로 묶여 있기 때문에 for문으로 정보를 수집한다. <tr> 안에는 또다시 여러개의 <td>가 포함되어 있는데, 한 행이 <tr> 해당하는 행의 각 열이 <td>라고 생각하면 된다. <tr> 태그 안의 <td>가 여러개이기 때문에 select()를 사용하고, 열 index를 활용해서 각 정보를 뽑아낸다.

 

name, category, country, score, rank2020, rank2019 등 6개의 정보를 확인했으면, CompanyModel 클래스를 활용해 company라는 인스턴스를 생성한다. 그리고 처음에 만들었던 companyList에 하나씩 쌓는다.

 

3. txt 파일로 저장하기

뉴스기사를 통해 수집한 companyList를 txt 파일로 저장해보자.

 

먼저, 원하는 경로에 파일을 open 해준다. 이때, 데이터를 입력해야 하므로 파일 형식을 'w'(쓰기)로 설정해야 한다.

f = open('C:/Users/Desktop/company list.txt', 'w', encoding='utf-8')

for c in companyList:
    data = c.SaveFormat()
    
    f.write(data)
    f.write('\n')

f.close()

SaveFormat 메서드를 활용해서 데이터를 한줄씩 저장했다. 설정해두었던 형태에 맞게 세미콜론(;)으로 각 속성 값이 잘 구분되어 저장되어 있는 것을 확인할 수 있다.

 

.write()를 통해 리스트에서 하나씩 불러온 기업의 정보를 담는다. 특히 파일 형식이 'w'인 경우, 파일 작성(?) 완료 후에 .close()를 해주어야 정상적으로 저장이 된다.

 

4. txt 파일 읽기

저장을 했으니, 불러와서 데이터를 읽어볼 수도 있다. 이번에는 원본 파일은 따로 수정은 하지 않을 것이기 때문에 파일 형식을 'r'(읽기)로 지정하고 열어준다.

f = open('C:/Users/Desktop/company list.txt', 'r', encoding='utf-8')

lines = f.readlines()
companyList2 = []

for line in lines:
    words = line.split(';')
    
    name = words[0]
    category = words[1]
    country = words[2]
    score = words[3]
    rank2020 = words[4]
    rank2019 = words[5]
    
    company = CompanyModel(name, category, country, score, rank2020, rank2019)
    companyList2.append(company)

f.close()

.readlines()는 불러온 파일의 데이터를 \n으로 구분하여 리스트의 형태로 반환하게 된다. 이 리스트를 lines에 담고 for문을 활용하여 한 줄 씩 읽어보면 된다.

 

처음 데이터를 저장할 때에 세미콜론(;)으로 구분하였기 때문에 .split(;)을 사용해 쉽게 자료를 구분해낼 수 있다. 그리고 순서에 맞게 name, category, country, score, rank2020, rank2019에 할당하면 된다.

 

그리고 이 값들을 다시 CompanyModel 클래스를 통해 company라는 인스턴스를 생성한다. html을 통해 자료를 수집하던 과정과 구분하기 위해 companyList2라는 리스트를 새로 만들었고, 이 리스트에 인스턴스를 하나씩 쌓는다.

 

클래스 객체를 리스트에 넣었기 때문에 companyList2를 출력해보면 위와 같은 결과가 나온다.

 

예쁘게 출력하려면,

for c in companyList:
    data = '{0:>3}  {1:<45} {2:0<5}%'.format(c.rank2020, c.name, c.score)
    print(data)

짠! 완성!!

 

뭔가 돌고 돈 것 같지만, BeautifulSoup이랑 파일 쓰기/읽기, 클래스 활용 연습은 제대로 한 것 같다.