상세 컨텐츠

본문 제목

[Python] Steam 게임평 크롤링 코드

프로그래밍

by ∫2tdt=t²+c 2017. 3. 17. 00:56

본문

작년에 게임과 관련된 한국어 감성분석 사전을 만들어볼까 하는 생각에 Steam에 등록된 한국어 게임평들을 모두 긁어온 적이 있었는데요, 결론부터 밝히자면 그 수가 적어서 유의미한 분석을 할 수는 없었습니다. 

그래서 당분간 묵혀두었는데, 기껏 짜놓은 코드 하드 속에서 용량만 차지하게 냅두느니, 먼지 털고 올려두면 누군가에게 도움이 될 수 있지 않을까 하는 생각에 공유하기로 마음 먹었습니다.


Python3 기반의 코드이고, 간단하므로 크게 설명할 건덕지는 없을듯합니다. BeautifulSoup 라이브러리가 필요합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import urllib
import urllib.request
import urllib.parse
import bs4
import re
import os
from concurrent.futures import ThreadPoolExecutor
 
 
def deleteTag(x):
    return re.sub("<[^>]*>", "", x)
 
 
def getComments(appid, lang='all'):
    def makeArgs(appid, lang, page):
        params = {
            'userreviewsoffset': 10 * (page - 1),
            'p': page,
            'workshopitemspage': page,
            'readytouseitemspage': page,
            'mtxitemspage': page,
            'itemspage': page,
            'screenshotspage': page,
            'videospage': page,
            'artpage': page,
            'allguidepage': page,
            'webguidepage': page,
            'integratedguidepage': page,
            'discussionspage': page,
            'numperpage': 10,
            'browsefilter': 'toprated',
            'appid': appid,
            'appHubSubSection': 10,
            'l': 'english',
            'filterLanguage': lang,
            'searchText': '',
            'forceanon': 1
        }
        return urllib.parse.urlencode(params)
 
    def innerHTML(s, sl=0):
        ret = ''
        for i in s.contents[sl:]:
            if i is str:
                ret += i.strip()
            else:
                ret += str(i)
        return ret
 
    def fText(s):
        if len(s): return innerHTML(s[0]).strip()
        return ''
 
    retList = []
    colSet = set()
 
    print("Processing: %d" % appid)
    page = 1
    while 1:
        try:
            f = urllib.request.urlopen(
                "http://steamcommunity.com/app/" + str(appid) + "/homecontent/?" + makeArgs(appid, lang, page))
            data = f.read().decode('utf-8')
        except:
            break
        soup = bs4.BeautifulSoup(data, "html.parser")
        cs = soup.select(".apphub_Card")
        if not len(cs): break
        for link in cs:
            url = link.get('data-modal-content-url')
            url = url[29:].replace('recommended/', '')
            if url in colSet: return retList
            colSet.add(url)
            helpful = fText(link.select('.found_helpful'))
            cat = fText(link.select('.title'))
            cont = innerHTML(link.select('.apphub_CardTextContent')[0], 2)
            cont = deleteTag(re.sub("([\t\r\n ]|<br>|</br>)+", ' ', cont)).strip()
 
            ng = 0
            nb = 0
            nf = 0
            nh = re.search("^([0-9]+) of ([0-9]+)", helpful)
            if nh:
                ng = int(nh.group(1))
                nb = int(nh.group(2)) - ng
            nh = re.search("([0-9]+) people found this review funny", helpful)
            if nh:
                nf = int(nh.group(1))
            retList.append((url, cat, cont, ng, nb, nf))
        page += 1
 
    return retList
 
 
def fetch(i):
    outname = 'comments/%d.txt' % (i * 10)
    try:
        if os.stat(outname).st_size > 0: return
    except:
        None
# english 대신에 korean을 넣으면 한국어를 긁어옵니다.
    rs = getComments(i * 10, 'english')
    if not len(rs): return
    f = open(outname, 'w', encoding='utf-8')
    for r in rs:
        f.write('%d\t%s\t%s\t%s\t%d\t%d\t%d\n' % (i * 10, r[0], r[1], r[2], r[3], r[4], r[5]))
    f.close()
 
 
with ThreadPoolExecutor(max_workers=5) as executor:
#일단 게임ID가 어디서부터 어디까지인지 몰라 다음과 같이 지정했습니다.
    for i in range(1, 100000):
        executor.submit(fetch, i)


사실 크롤링이라는게 어려운 기법이 필요한것은 아니고 은근과 끈기를 얼마나 가지고 (서버의 감시를 피해서) 잘 긁어오느냐가 관건인건데, 스팀에서 전체 게임 목록을 따로 제공해주지 않길래 게임의 고유 ID를 1부터 십만까지 뒤지며 차례로 다 검사해봤습니다. 나중에 게임이 더 늘어나면 해당 조사 범위를 조절할 필요가 있겠죠?

관련글 더보기