기본적으로 신경써야 하는 부분

데이터 구조 설계, Block 우회, reverse engineering 여부, 멀티 프로세스(worker, job, instance), 파이프라인 설계

1. 데이터 구조 설계

 - 요구사항이 무엇인지 정확히 확인하는 게 좋지만, 대부분의 요구사항은 뭉뚱그려질 때가 많다. 그래서 대부분은 초기에 기본 구조로 만들고 필요시 추가하는 게 낫다.

 - 컬럼을 target schema에 추가하는 경우 type을 신경써야 한다.

 - 요구사항에서 가장 중요한 점은 index값을 무엇으로 잡느냐인 거 같다. 나머지는 어차피 미리 정해 두어도 바뀐다.

 - 필수적인 data dimension 파악을 통해 설계해야 한다.

 - target db에 insert된 시각은 있으면 좋다. (mysql의 경우, CURRENT_TIMESTAMP 이용)

 - 시점에 대한 값은 잘 구분해주는 게 디버깅할 때도 유리하다. cron 기준 트리거된 시간과 실제 worker에서 작업이 시작된 시간 등을 이용하면 실제 어떤 Q의 duration을 계산할 수 있고, 퍼포먼스 계산을 용이하게 해준다.

2. Block 우회

 - 현실적인 문제이다. 거꾸로 어떻게 bot을 인식하는가를 체크하고 피해야 한다.

 - ip와 browser_id의 조합으로 인식하는 경우가 많다. 사실 그 2개를 철저히 refresh하면 잡아내기 쉽지 않다.

 - browserid는 사실 client 입장에서 server가 나를 어떻게 판별할지 추정하기 어렵고, ip 변경은 네트워크 속도 저하라는 cost가 있다.

 - VPN을 유료로 써야 그나마 IP 변경으로 인한 속도 저하의 cost를 줄일 수 있다.

 - VPN의 주요 위험 중 하나는 VPN의 연결이 제대로 안 돼있는 상태에서 연결이 시도되는 것이다. 이런 케이스에서 중요한 ip를 block 시키는 사례가 생길 수 있으니 웬만하면 외부ip임을 확인하고 거기서만 테스트 하는 것을 권장한다.

-외부망에 크롤링 서버를 만들면, 내부 target db에 접속하기 어려워질 수 있으므로, instance 추가 시 방화벽을 고려해야 한다. 예시로 SQS에 쌓아 놓고, 접근 가능한 다른 region의 instance에서 가져가는 방식도 채택 가능하다.

 - block이라는 게 꼭 접속이 차단되는 형태는 아니다. garbage data를 주는 경우도 있다. block 인식을 잘못하면 data가 오염될 수 있다.

3. reverse engineering 여부

 - 대부분 웹에 노출되는 정보들은 api를 통해 출력되는 경우가 많다. api를 통해 파라미터만 바꿔서 가져올 수 있다면, 크롤링의 cost는 대폭 낮아진다.

 - 그래서 보안이 강한 사이트들은 대부분 api를 post 형태로 숨기거나, auth를 요구하면서 hmac 같은 걸 활용해 임시 키를 발급한다.(요새는 네이버가 많이 쓰는 듯하다)

 - 이게 가능한지에 따라 크롤링 퍼포먼스, cost, 아키텍쳐 설계가 급격하게 달라지니 꼭 확인이 필요하다.

 - 불가능해서 front로 작업해야 한다면, 크로미움이나 playwright + xvfn 조합을 많이 쓴다.

 

4. 멀티 프로세스

 - 3번에서 전체 reverse engineering이 가능하다면, 요구 수준에 따라 멀티 프로세싱이 필요하지 않을 수도 있다.

 - 다만 그렇지 못 하다면, 대부분은 크롤링의 가성비를 위해 멀티 프로세싱 처리와 그를 위한 구조 설계가 필요하다.

 - instance의 메모리를 관리하기 위한 health check를 만들어 적절한 worker 수준을 찾는 테스트도 필요하다.

 - 1 instance -> n workers -> job processing 구조를 위해 Q를 관리할 무언가가 있으면 좋다. [SQS나 카프카]

 - instance는 EC2를 쓸 수도 있지만, heavy하지 않은 경우 lightsail이 가성비가 더 좋다.

 

5. 파이프라인 설계

 - master data => Event trigger(or airflow) => SQS(엄격한 관리가 필요하고 데이터가 많다면 Kafka) => (VPN converter) => workers => target db

 - 기본 구조는 이렇게 잡고, 이걸 무엇으로 달성할 것인지를 데이터양이나 필요 수준에 따라 선택해야 한다.

 - master data는 구글 시트는 안 쓰는 것이 좋다. 써야 한다면 구글 시트를 주기적으로 db에 업데이트 하는 프로세스를 만들고 해당 테이블을 참조하는 걸 권장한다. 생각보다 gcp는 에러가 많다.

- master data를 extract할 때, gcp는 생각보다 불안정하다. gcp를 쓰더라도 retry 간격이 1분은 되어야 한다. 궁극적으로는 db table 같은 곳에 cache를 하고 쓰는 걸 권장한다. cache했다면, master data를 주로 수정하는 사람은 업데이트 주기를 인지할 필요가 있다.(수정 시점과 cache 데이터의 기준 시점이 달라 의도하지 않은 결과가 나올 수 있음)

 

현상: 특정 작업 완료 후 app을 taskkill.exe로 종료했더니, 해당 위치에서 무한 펜딩. Executor 프로그램은 켜져있고, 오케스트레이터에서도 해당 job을 running으로 인식.

 

조치1(activity의 버그로 추정)

 - start process의 타임아웃을 명시적으로 2000ms로 부여

조치1을 deploy했음에도 여전히 간헐적으로 증상 발생

조치2(taskkill의 연쇄작용으로 추정)

 - taskkill 대신에 kill process activity로 변경

 - 현재까지 이상 없음

1. 내가 할 수 있는 일과 내가 해야 되는 일의 범주 차이

  일의 주체가 고민되는 이유는 궁극적으로 R&R이 명확하지 않아서이다. R&R이 명확하지 않다는 것은 다음과 같은 케이스로 나눠보자.

i) 업무 프로세스 자체가 실적, 책임 소재의 회색 영역에 있는 경우

ii) 업무 프로세스는 명확하지만, 각 프로세스별 R&R이 없는 경우

iii) 업무 프로세스가 명확하지 않은 경우

  i의 경우엔 일의 임팩트가 강하지 않기 때문에 어디로든 배정되지 않았을 확률이 높다. 내가 목마른 사람이 아니라면 굳이 우물을 팔 필요가 없는 일이다. ii의 경우 R&R을 정할만한 윗 사람을 찾아서 issue raise를 해야 한다. R&R이 의도대로 정리되길 원한다면 명확한 프로세스라도 issue raise를 할 때, 내가 의도하고 이해하는 형태로 정리할 필요가 있다. iii의 경우는 서순에 따른 task를 정리하고 각 Role의 자리를 대략적으로라도 만들면서 이해당사자들과 각 role의 이해도를 맞춰야 한다. ii, iii의 경우 결국 process가 정리되는데 이때 내가 해야 되는 일인지에 더 초점을 맞춰야 한다.

  개발자(실무 수행자)에 빌붙는 중간자가 많은 이유는, 할 수 있기 때문에 해줬는데 그 공을 다른 사람이 가져가기 때문이다. 결국 실무 수행자는 실제 생산해내는 가치에 비해 과소평가 되기 십상인데, 이렇게 되는 본질적인 이유는 '내가 할 수 있기 때문에 해줘서'라고 본다. 할 수 있기 때문에 해주는 순간 그 process의 책임자는 개발자가 되고 performance는 process를 관리했던 사람이 내는 것으로 인식되는 경향이 있기 때문이다.

 

2. 할 수 있지만 해서는 안 되는 일

  따라서 개발자는 할 수 있는 것과 해야 되는 일을 명확히 구분해야만, 덜 괴로울 수 있다. 이것을 위해 문서를 정리해야 한다면 기꺼이 정리해야만 공을 뺏기고 과를 뒤집어 씌워지는 경우를 피할 수 있다. 특히 할 수 있지만 해서는 안 되는 일은, 프로세스 자체가 장기적인 변화 관리가 필요한데 내가 해야 되는 일은 아닌 경우이다. 생각보다 이런 프로세스들의 변화 관리는 한번에 찾아오고, 이를 소화하기 위한 리소스는 매번 내가 해야 할 일이 급할 때 한번에 온다. 이 일이 내가 해야 되는 일인지 파악되기 전에 무작정 하게 되면, 피라미들에게만 유명한 만능 잡부가 된다.

  만능 잡부가 되는 게 좋지 않은 이유는, '잡'일만 그렇게 찾아오기 때문이다. 그게 진정 실적이 되는 일이라면, 여기로 넘어오지 않을 확률이 높다. 리소스는 많이 드는데 실적은 잘 안 되는, 하지만 소소하게 피라미들에게는 그럭저럭 먹을만한 정도의 귀찮은 일들은 많이 한다고 스스로에게 도움이 되지는 않는다.

3. 사일로 완화를 위한 프로세스 관리

  이런 일들을 많이 해주면 개인적인 보람이 있을지도 모르겠다. 다만 조직의 관점에서는 생산성 없는 앵무새들에게 유인을 제공하는 행위가 될 수 있다. 성과가 없는 사람이 self-manage를 하지 않으면, 그 사람은 도태되도록 두어야 마땅하다. 이것이 실력주의나 공리주의처럼 들릴 수도 있다. 하지만 대부분 장기적인 성과는 일에 재능이 있느냐 보다도, 일에 얼마나 몰입하느냐에 더 의존한다. 조직은 그어놓은 선때문에 흥하고 그 선에 의해 쪼개진다. 쪼개지는 것을 막기 위해선, 더 몰입하고 실제 생산성을 추구할 수 있게 유인을 제공해야 한다.

   실제 생산성을 관찰하기 위해서는 업무 방향성을 관찰할 수 있는 도구가 필요하다. 또한 그 도구는 업무 투명성의 '비용'을 효과적으로 낮출 수 있어야 한다. 누가 어떤 일을 했는지, 그 일의 결과를 통해 어떤 것들을 할 수 있는지, 나는 왜 이 일을 해야 하는지 쉽게 알 수 있어야 한다. 그래야 협력 단계에서 각자의 할 일과 성과를 공정하게, 기꺼이 지게 할 수 있기 때문이다. Jira, confluence, redmine 등등 업무 관리 툴은 그런 방향성을 가지고 관리, 업데이트 될 필요가 있다.

현상: AR봇이 특정 시간대에 api로 수행시키는 경우, 중간에 돌다가 로깅을 남기지 않고 봇에서 펜딩되는 현상이 생김. 해당 시간대가 아니면 발생하지 않고, 오케스트레이터에서는 정상 수행중으로 로깅

refer: 이런 케이스가 UR에서 발생하는 경우 job max timeout을 설정하여 trigger하는 케이스를 들었음. 다만 이렇게 체크되는 경우 오랜 시간을 기다려 retry되게 구성할 수밖에 없음. (근본적인 원인 해결은 아님) + AR에서는 job 단위 parameter를 따로 전송하지 못 함.(process level까지만 전달. job 단위 trigger parameter는 오케 -> bot 으로만 내려감)

 

추정 원인

1. 오케스트레이터와의 인증 패킷을 교환하는 과정에서 방화벽 감시 툴이 패킷을 block함

2. ar의 경우 process 단위를 연결해 uirobot을 실행시키더라도 인증 상태의 유효기간(timeout)이 실시간으로 체크되지 않는 경향성이 있어, 중간에 패킷이 block되어 인증이 실패하더라도 오케스트레이터 입장에서는 그냥 잘 돌고 있다고 간주

 

 

디버그 방식

1. nslookup cloud.uipath.com 으로 ip 체크

2. wireshark를 깔고 해당 시간대 레코딩

3. ip.src, ip.dst로 필터링해 결과 체크

 

=> 관련 target 방화벽 해제 후 이슈 없음

 

 

상황: Sentry로 부터 오는 error log의 Json 파싱을 하는 과정에서 이슈별 파싱되는 컬럼이 달라, get & transform하는 function에서 explode한 후, 컬럼들을 표준화할 필요가 생김

col_list = ['a', 'b', 'c']

df = df.explode('d')
df = pd.concat([df.drop(['d'], axis=1), df['d'].apply(pd.Series)], axis=1)
df = df.reindex(columns=col_list, fill_value=0)

이 상황에서 대부분은 for문을 써도 크게 퍼포먼스가 떨어질 것 같지는 않지만, index를 다시 정리하는 걸 이용해 이렇게 추가할 수 있다는 게 신기해서 남겨둔다.

주의 사항: fill_value를 None으로 설정하면 인식 못 하고 pd.NaN이 된다. None을 채워야 한다면 후속 fillna 처리가 필요하다.

 

일반적인 reindex 사용은 row index의 결측값에 대해 ffill(직전값)이나 bfill(직후값)을 채우는 방식으로 보인다.

▶ Data Table 중복값 제거 (모든 행이 정확히 같은 케이스일 때)

- dtData = dtData .AsEnumerable().Distinct(System.Data.DataRowComparer.Default)

 

 Data Table 중복값 제거 (Group By 후 첫번째 행 선택)

   중복 시 값 선택: First (특정 건이 선택되도록 유도해야 한다면 sort 선행)

   type 체크 필요(Of String 부분은 맞춰서 변경)

 

- dtData = dtData.AsEnumerable().GroupBy(Function(i) i.Field(Of String)("컬럼명")).Select(Function(g)         g.First).CopyToDataTable()

 

 DataTable의 차집합 , 교집합, 합집합

▷ 차집합
- dtData1 = dtData1.AsEnumerable().Except(dtData2.AsEnumerable(), System.Data.DataRowComparer.Default)

▷ 교집합
- dtData1 = dtData1 .AsEnumerable().Intersect(dtData2.AsEnumerable(), System.Data.DataRowComparer.Default)

▷ 합집합
- dtData1 = dtData1 .AsEnumerable().Union(dtData2.AsEnumerable(), System.Data.DataRowComparer.Default)

 

Datatable내의 값과 비교할 때 타입이 맞지 않아 발생하는 이슈 방지(ex. 1 != "1")

dtOutput = (From p In dtInput.Select Where p(Column_Index).ToString.Equals("StringValue") Select p).ToArray.CopyToDataTable <==== 둘 다 타입을 String으로 맞춰버리기 때문에 엑셀 서식으로 인한 타입 차이 제거

Captcha는 크게 recaptcha와 hCaptcha가 있다.

1. reCaptcha

reCaptcha는 다음과 같이 출력된다

reCaptcha 예시

reCaptcha는 구글에서 만든 API이다.

여러 방식의 우회가 있을 수 있지만, STT(Speech to Text)를 활용하는 게 일반적이다.

 

기본적으로 Captcha는 인간이 분류하기는 쉽고 로봇은 분류하기 어려운 시각 구분 정보를 이용한다.

이 경우 특정 pass를 시각 정보로만 제공하면, 시각 장애인인 사람은 captcha를 통과할 수 없다.

대기업이라면(?) 웹 접근성 가이드에 따라 반드시 다른 수단을 제공해야 한다.

따라서 구글이 만든 reCaptcha는 거의 항상 Speech 문제로 변경 가능하다.

(거의 항상이라고 쓴 이유는 STT를 단위 시간에 너무 많이 호출하는 경우 '너 로봇인 거 같애'라는 문구를 출력하면서 변환 안 시켜주기 때문)

STT는 이전부터 상당히 기술이 많이 좋아져서 웬만해서는 사람 수준으로 인식 가능하다.

RPA에서는 STT를 이용한 extension을 이용하는 게 좋은데, 대표적인 게 Buster: Captcha solver for human 이다.

https://chromewebstore.google.com/detail/buster-captcha-solver-for/mpbjkejclgfgadiemmefgebjfooflfhl

 

Buster: Captcha Solver for Humans

Save time by asking Buster to solve CAPTCHAs for you.

chromewebstore.google.com

보통 selector는 제대로 잡기 어려워서 image click으로 잡기 십상인데

이미지 인식을 정확히 하기 위해 extension 고정핀으로 image를 노출시키지 않는 것을 추천한다.

고정핀 활성화가 되지 않은 모습
아래 buster의 이미지를 클릭하면 STT로 전환되며 자동으로 Captcha가 풀리게 된다.

 

2. hCaptcha

hCaptcha 예시

hCaptcha는 구글 reCaptcha가 접속정보를 추적하는 거 같다는 비판이 일어 만들어진 캡차라고 한다.

hCaptcha는 많은 봇들이 STT로 우회하는 것을 알고 있어서 STT 옵션을 주지 않는다.

사악한 샘플 ㅠ 인간인 나도 답을 모르겠다

이 경우는 2가지 방법이 있는데 하나는 cookie를 세팅하는 모듈을 만드는 것이다.

묘하게 불친절해 보이는 번역이라 어울린다

자동화 하는 로컬에 hCaptcha 쿠키를 설정방식인데, 쿠키를 삭제하지 않는다면 24시간 지속된다.

다른 하나는 역시나 extension에 의존하는 것이다.

https://chromewebstore.google.com/detail/captcha-%ED%95%B4%EA%B2%B0%EC%82%AC-%EC%9E%90%EB%8F%99-hcaptcha-r/hlifkpholllijblknnmbfagnkjneagid

 

CAPTCHA 해결사: 자동 hCAPTCHA reCAPTCHA 자유롭게

모든 페이지에서 AI를 사용하여 hCAPTCHA 및 reCAPTCHA를 해결하세요. 더 쉬운 데이터 수집을 위해 NoCoding Data Scraper와의 원활한 통합

chromewebstore.google.com

"Captcha 해결사" 라는 extension인데 켜놓으면 알아서 captcha를 풀어준다. (hCaptcha든 reCaptcha든 다 풀어줌)

다만 이 extension은 단점이 두 가지 있다.

1) reCaptcha가 이 extension이 있으면 아예 Captcha를 안 보여주는 경우가 있다. (recaptcha 사이트에 접근하는 봇은 영향도가 있음)

2) 문제가 어렵다 보니 아예 못 풀 수도 있다. (인내심을 가지고 기다리면 많이 틀리다가 어찌저찌 넘어가는 간다 ㅠ)

 

3. 결론

hCaptcha가 나왔을 때 특별히 recaptcha랑 안 겹치고 daily 과제라면 extension을 쓸 수 있다.

하지만 과제 안정성을 위해서는 쿠키를 세팅하도록 하자.

reCaptcha는 꼭 buster가 아니라도 STT를 안 해줄 수 없기 때문에,

상대적으로 뚫기가 쉬우니 웬만하면 과제 진행을 해도 무방하다.

단 reCaptcha도 횟수가 많으면 컷당하니 로그인 시도가 잦으면 안 된다.

 

 

날짜별 변환. 날짜 형식은 yyyyMMdd에서 조절

TODAY: TIME_FORMAT(TIMESTAMPADD(HOUR, 9, CURRENT_TIMESTAMP), 'yyyyMMdd')
D-1: TIME_FORMAT(TIMESTAMPADD(DAY, -1, TIMESTAMPADD(HOUR, 9, CURRENT_TIMESTAMP)), 'yyyyMMdd')
D-7: TIME_FORMAT(TIMESTAMPADD(DAY, -7, TIMESTAMPADD(HOUR, 9, CURRENT_TIMESTAMP)), 'yyyyMMdd')
M-1: TIME_FORMAT(TIMESTAMPADD(MONTH, -1, TIMESTAMPADD(HOUR, 9, CURRENT_TIMESTAMP)), 'yyyyMMdd')

+ Recent posts