g2g-crawling06: div.noresult-main-title

div.noresult-main-title 업데이트(20220304)

크롤링하는 페이지에 새로운 문제가 발생했다.

div.noresult-main-title 태그로 인해 server랑 currency 값을 불러올 수 없었다.

noresult

URL에서 가져올 수 있겠지만 우선 함수를 빠져나가는 방식으로 업데이트 했다.

1
2
3
4
5
6
def get_data():
...

if (soup.find("div", class_="noresult-main-title").string == "The offer you try to view is no longer available."):
driver.close()
return False

if return을 추가하니 해당 페이지가 정상 title로 복구됐다… 작동 확인도 못해봤다.

g2g-crawling05: 엑세스 키 관리 문제

엑세스키 노출

파일 하나에서 함수를 모두 관리했었고, 당연 문제가 됐다.

이전 업데이트 때 깜빡하고 GCP acc 키가 제거되지 않은 상태로 push 해버린 것.

조취(20220303)

당장 연결 권한을 바꿔야한다.
그 이후, 같은 실수가 발생하지 않도록 관리방법이 추가되야한다.

불특정된 사람들이 깃헙을 유심히 볼만한 정도는 아니라 다행이라 생각한다.

엑세스키 제거 후 재발급

commit을 일일히 삭제하는 방법을 생각했지만, 어딘가엔 데이터가 남을 가능성이 있어 GCP에서 서비스 키를 새로 발급받았다.

이걸로 일단 노출된 키로 접근하는 불상사는 막았다.

새로운 py 파일 생성

source를 계속 업데이트 한다면 역시 까먹어도 문제 없도록 엑세스 함수를 숨기거나 따로 관리해야한다.

기존에 사용하던 crawl_URL_boost.py에서 SQL에 접근하는 데이터가 들어있는 connecting, dataInsertPsycopg2, accGBQ을 crawl_acc_sql.py 파일로 옮겨 개인 모듈로 관리한다.

crawl_acc_sql.py에 사용되는 모듈들을 boost에서 옮겨담는다.

crawl_acc_sql.py
1
2
3
4
5
import pandas_gbq
import psycopg2
from psycopg2 import connect, extensions
import psycopg2.extras as extras
from google.oauth2 import service_account

crawl_URL_boost.py에는 crawl_acc_sql 모듈을 추가함에 따라 기존 함수들도 모듈에 맞춰 수정했다.

crawl_URL_boost.py
1
2
3
4
5
6
7
8
9
import crawl_acc_sql as accsql


if __name__ == "__main__":

conn = accsql.connecting()
accsql.dataInsertPsycopg2(conn, data=df)

accsql.accGBQ(df)

혹시 모를 에러를 대비해 .backup 로컬 폴더에 수정된 날짜를 파일명에 추가해 이전 버전을 백업했다.

새로운 sql을 추가하지 않는 이상 crawl_acc_sql.py를 편집할 일은 없으므로 계정 키를 항상 지워서 업로드해야하는 불편함이 줄었다.

내부링크

g2g-crawling04: 동적페이지 로드 문제 발생

동적 페이지 자동 크롤링을 새벽마다 돌리면서 URL이 변경되지 않는 동작의 경우 요소를 찾을 때 까지 대기하는 코드가 때때로 에러를 일으킨다.

다음에 error가 식별됐을 때 Error 명을 식별해서 try - except 구문으로 예외처리하고 time.sleep 함수를 넣어준 다음 try 재귀 함수를 돌리는 코드를 추가 작성해 업데이트할 예정이다.

try & 재귀함수 추가(20220228)

get_data() 함수에 try - except로 구성된 재귀함수 try_clickable()을 추가했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def get_data():

def try_clickable(xpath_name):
try:
i = 0
soup = soup_wait_clickable(xpath_name)

seller.extend(get_data_seller(
"a.flex.prechekout-non-produdct-details > div.seller-details.m-l-sm > div.seller__name-detail"))
price.extend(get_data_seller(
"div.hide > div > div > div > div > div > span.offer-price-amount"))
stock.extend(get_data_stock())
except IndexError as e:
i += 1
print(e+str(i))
time.sleep(1)
soup = try_clickable(xpath_name)
return soup

클릭 동작이 들어간 soup_wait_clickable 부분만 업데이트 했지만, 만약 클릭 동작이 필요없는 페이지에서도 IndexError가 발생한다면 앞부분에도 추가해야한다.

getdata[함수] 요소 추가(20220304)

다른 문제로 데이터를 재확인하다가 중복 데이터가 지난 업데이트인 2월 28일부터 들어있음을 확인했다.

1
SELECT * FROM poe where date = '2022-03-04' order by currency, server, seller;

으악 아니야

일단 당장 새로 갈아끼울 수 있는 3/4 데이터만이라도 삭제한다

1
2
3
4
5
--PostgreSQL
delete from poe where date = '2022-03-04';

--GBQ
delete from DB_G2G.poe where date = '2022-03-04';

ㅈ됨. 나중에 데이터를 쓸 때 2/27이나 3/4 데이터를 사용해서 어떻게든 처리해야겠지

처음 문제는 time.sleep() 값이 부족한줄 알고 4까지 넣어봤지만 여전히 중복 데이터가 들어갔다.
아무래도 get_data에서 처리하는 soup 변수가 문제있는거같다.
28일에 백업해둔 코드를 실행해보고 문제가 발생하지 않아 확신했다.

try 함수를 넣으면서 soup의 위치가 get_data.try_clickable()로 들어감..
기존 soup는 get_data()에 위치했으며, get_data.get_data_seller(selector) or get_data.get_data_stock()에 적용 시 문제 없이 get_data의 soup로 들어갔다.

하지만 이제 내가 넣어야하는 soup는 get_data.try_clickable() 아래에 있는 soup를 넣어야했다.

따라서 getdata 함수를 아래와 같이 요소를 추가했다.

1
2
3
4
5
def get_data():
def get_data_seller(soup,selector):
...
def get_data_stock(soup):
...

마찬가지로 getdata 함수를 사용하는 구문에도 soup 요소를 추가했다.

2022-02-28 ~ 2022-03-03 오또케…

외부링크

스팀: 넥스트 페스티벌 Feb-2022

Steam Next Fest

개발중 혹은 출시예정 게임의 데모버전을 1주간 플레이 가능한 스팀 행사: NEXT FESTIVAL의 2022년 2월 버전

Next-Fest

많은 유저의 플레이 후기를 듣고 구매하기엔 힘든 인디게임 특성상 플레이어가 30분이 됐건 반나절이 넘던 부담없이 플레이하고 찜해놓을 수 있다는 장점이 크다.

플레이

아직 많은 게임을 해보지는 않았다.

Godlike_Burger

코옵 게임인 overcooked!와 비슷하게 레스토랑 디펜스 형식을 사용한 <Godlike_Burger>

godlike-burger

타이쿤류 모바일 게임같은 인터페이스와 대비되어 스토리 배경과 소재가 굉장히 어둡다. Pegi-18로 분류되지 않을까 싶다.

로그라이크, 디펜스, PvE, 경영, 함정을 이용한 전략 시뮬 등 다양한 장르를 녹여놨다.

4시간 이전에 그만둔 이유는 스테이지간 레벨 간격이 너무 넓었다. 첫 행성에서 신경써야하는 손놈은 한 종족밖에 없었으나,
플레이가 너무 지루해 두번째 생성으로 넘어가고 종족이 4종류가 되고, 극단적으로 늘어난 종족과 높은 종족 능력치가 부족한 정보력과 자원의 빈공간을 강하게 파고들었다.
게다가 대응해야할 손놈이 1에서 4로 늘고 레시피도 복잡해지면서 플레이어는 갑작스레 뇌지컬 요구를 강하게 받았고 화면 사방에 조그맣게 보이는 손놈 아이콘을 통해 자원 유통해야 했지만 4종족이나 되는 복잡성에 강한 스트레스도 주어졌다.
그렇다고 첫 행성으로 돌아가기엔 1종족 상대는 또 극단적인 자동화로 게임플레이가 극단적으로 지루해진다.
데모버전이라 많은걸 넣지 못하고 초반/중반/중후반의 세 스테이지만 넣은거같지만 초반-중반 과정의 난이도차가 너무 심하기에 적당한 데모 플레이를 위한 초중반 스테이지 추가가 필요하다 생각한다.

IXION

그리스 신화: 죄를 지어 수레바퀴에 걸린 인간의 이름을 따온 IXION

사운드가 안나와서 가오갤 이후 오랜만에 그래픽드라이버 업데이트를…

IXION-play

우주 배경의 타이쿤이라는 모선에서 이뤄지는 도시 경영 시뮬레이션.

공개된 내용은 다른 은하로 발진하기까지 과정이 끝이다.

플레이하면서 <프로스트 펑크>와 유사한 인상을 받았고 <파피용> 소설로부터 영감을 받은 작품으로 느껴졌다.

IXION-demo

데모 플레이는 아포칼립스 배경(혹은 도시경영)의 시뮬레이션에 익숙한 유저에 대한 데이터 수집이 목적인 구성으로 느껴졌다.

한글의 문제인진 모르겠지만 목표 등을 나타내는 인터페이스가 잘 들어오지 않아 사이클 낭비로 식량 부족 문제가 발생해 리트라이했다.

외부링크

pandas_gbq python과 google bigquery 연동하기

GCP 엑세스키 발급받기를 선행

라이브러리 설치

google-cloud-bigquery

python과 google bigquery를 연동하기 위한 라이브러리

1
$ pip install google-cloud-bigquery

pandas-gbq

dataframe을 gbq에 업로드하기 위한 라이브러리

1
$ pip install pandas-gbq

GBQ 연동

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from google.oauth2 import service_account

## json 파일 내용
credentials = service_account.Credentials.from_service_account_info(
{
"type": "",
"project_id": "",
"private_key_id": "",
"private_key": "",
"client_email": "",
"client_id": "",
"auth_uri": "",
"token_uri": "",
"auth_provider_x509_cert_url": "",
"client_x509_cert_url": ""
}
)

## json 파일 경로를 사용하고 싶으면 `from_service_account_file` 함수를 사용하면된다
credentials = service_account.Credentials.from_service_account_file("URL.json")

업로드 하고싶은 dataframe(df)을 만들어두자.

pandas.to_gbq로 연동하면 브라우저 창을 통해 쿠키를 받아야하므로 pandas_gbq를 사용한다.

1
2
3
4
5
6
7
import pandas_gbq

project_id = "project_id"
table_name = "data_set_name.table_name"

pandas_gbq.context.credentials = credentials
pandas_gbq.context.project = project_id

dataframe 업로드

if_exists 요소의 기본값 = “fail”

  • fail: 테이블이 존재하면 pandas_gbq.gbq.TableCreationError 발생
  • replace: 테이블이 있는 경우 다시 만들고 데이터 삽입
  • append: 테이블에 데이터 삽입(테이블이 없는 경우 테이블 생성)
1
pandas_gbq.to_gbq(df, table_name, project_id=project_id, if_exists="append")

bigquery data 받아오기

1
2
3
4
import pandas as pd

query = "SELECT * FROM `data_set_name.table_name`"
readdf = pd.read_gbq(query=query, project_id=project_id, credentials=credentials, dialect='standard')

외부링크

pyspark 설치

pyspark

대용량 data를 관리하기 위한 유사 SQL 라이브러리

사전 준비

  • python 설치
  • java 설치
  • spark 다운로드
  • winutils 다운로드

python 설치

파이썬 혹은 아나콘다를 설치한다.
python 버전 3 이상으로 설치한다.

java 설치

나의 경우 java가 설치되어있고 JAVA_HOME 환경변수까지 설정되있기에 그대로 사용했으나 아니라면 오라클에 로그인하고 java를 설치한다.

이후 JAVA_HOME 환경 변수를 추가해준다.

JAVA_HOME

시스템 변수, 사용자 변수 중 원하는 영역에 추가해준다.
난 여러 계정을 사용하지 않으니 그냥 사용자 변수에 넣어줬다.

Spark 다운로드

Spark tgz 압축파일을 다운받는다.

SPARK

원하는 경로에 압축 풀고 위와 동일하게 환경 변수를 설정해준다.

SPARK-경로
SPARK_HOME

winutils 다운로드

위에서 받은 spark 버전과 동일 버전의 winutils.exe를 받아준다.

winutils용 폴더를 만들고 bin 파일 아래에 넣어준다.

winutils-경로

hadoop 환경변수도 설정한다.

HADOOP_HOME

Path 설정

마지막으로 path 값에

  • %JAVA_HOME%\bin
  • %SPARK_HOME%\bin
  • %HADOOP_HOME%\bin
    을 넣어준다.

path

pyspark 실행

CMD(혹은 Anaconda Prompt)를 열어서 pyspark 설치한다.

1
> pip install pyspark

1
> pyspark

pyspark

외부링크

unique 요소만 포함하는 set

특정 집합을 만들다보니 같은 값이 저장되지 않는 리스트를 만들 필요가 생겼다.
set() 형식을 사용하면 unique한 객체만 포함하는 집합을 만들 수 있다.

단, 사용하려면 set()으로 객체를 초기화 해야한다.

1
2
set = set([1,1,1,1,2,3,10,10,31])
print(set)
{1, 2, 3, 10, 31}

dictionary와 동일한 {, }로 사용되지만 dictionary와 다르게 key가 없는 list 형식이다.

외부링크

g2g-crawling03: G2G poe 현거래 매물 수집시작

저번 토요일(02/05) poe 리그 시작일에 맞춰 한국시간 06시마다 매일 현거래 매물 데이터를 postSQL과 GBQ에 수집중이다.

postSQL

pandas_gbq python과 google bigquery 연동하기

GBQ

2, 3 시즌 이상 데이터가 쌓이면 반복되는 데이터로 머신 러닝을 돌려 유저가 빠지는(혹은 매물이 폭락하는) 기간을 예상할 수 있을 것이다.

그 전에 google data studio를 이용해서 대쉬보드도 관리할 예정이다.

datetime 날짜와 시간

datatime

datetime 모듈은 특정 시간과 날짜를 불러오는 파이썬 내장 모듈이다.

날짜와 시간 함수

datatime 모듈을 사용하면 시간 날짜 UTC timezone을 불러올 수 있고, 내장 함수로 data, time 등을 사용해 날짜 혹은 시간만 지정하고 특정 시간대를 지정해 불러올 수도 있다.

1
2
3
4
5
import datetime

print(datetime.date.today())
print(datetime.datetime.today())
print(datetime.time(12,59,33, microsecond=333333, tzinfo=datetime.timezone.utc))

2022-02-03
2022-02-03 03:36:07.407621
12:59:33.333333+00:00

strftime(원하는 형식으로 변경)

strftime을 사용하면 불러온 시간을 원하는 형식으로 바꿀 수 있다.

1
print(datetime.datetime.today().strftime("%Y/%m/%d %H:%M:%S"))

2022/02/03 03:40:36

외부링크

time 모듈로 일시정지

python 내장 함수인 time 모듈을 사용하면 시간 관련 추출이나 간섭을 할 수 있다.

그 중 이번에 내가 사용할 함수는 sleep() 함수로 지정한 시간 단위만큼 프로그램 실행 중간에 딜레이를 줄 수 있다.

특히 이번에 크롤링 중인 페이지가 동적페이지에 하나의 URL에서 클릭으로 HTML 코드만 바뀌는 옵션이 들어가 selenium만으로 크롤링하면 때때로 에러가 발생했다.

1
2
3
import time

time.sleep(3)

을 사용하면 코드 중간에 3초간 딜레이 줄 수 있다.

외부링크