▶ 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 이다.

(2025. 03. 10. 기준으로 buster가 chrome에서 지원 종료된다고 뜨는 걸 확인했다 비슷한 STT 확장 프로그램을 가져다 써도 될 거 같다)

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')

import numpy as np
import pandas as pd

# None과의 비교
print(None == None)  # True
print(None == np.nan)  # False
print(None == pd.NaT)  # False

# NaN과의 비교
print(np.nan == np.nan)  # False
print(np.nan == None)  # False
print(np.nan == pd.NaT)  # False

# NaT와의 비교
print(pd.NaT == pd.NaT)  # False
print(pd.NaT == None)  # False
print(pd.NaT == np.nan)  # False

None의 경우에만 그나마 == 비교가 가능하다.

다른 경우는 그 자신을 == 처리해도 False가 나온다.

[문제는 이래도 에러가 안 난다.]

nan이나 NaT같은 값들은 특정 value를 지니는 게 아니라, 애초에 비교 자체를 상정하지 않는, 일종의 상태 label 같은 느낌이다.

꼭 np.isnan()이나 pd.isna() 같은 걸로 비교하자.

 

type이 제대로 안 잡혀서 이슈가 있다면

type을 다 지정한 dict를 만들어서 매핑하는 게 가장 확실한 방법이다.

 

다만 시리즈 개수가 엄청 많은데, 최대한 자동으로 잡고 싶다면

df.convert_dtypes()를 쓰면 된다.

특히 엑셀이나 구글 시트에서 읽어온 dict를 dataframe으로 바꾸고 하는 과정에서

무조건 object로 잡히고 이걸 다시 각각 변환해줘야 하는 귀찮음이 있는데

이런 경우 용이하게 자동으로 잡아줄 수 있다.

대부분 개념적으로 특정 키값에 대해 테이블 데이터를 붙이는 작업을 하기 때문에

Outer는 쳐다도 안 보고 Inner나 left join을 찾게 된다.

문제는 한 쪽 부분만 존재하게 되는 케이스이다.

예를 들어,

예약수와 조회수를 같이 보여줘야 한다고 치자.

논리적으로 예약수가 0이라도 조회수는 0이 아닐 가능성이 높다.

조회수를 count하는 방식에 따라 조회수가 0인데도 예약수가 0일 수도 있다.

[가령 특정 채널을 통해 들어오는 조회는 조회수 count가 안 되는 경우]

1. 양쪽이 모두 한쪽만 있는 가능성이 있음

2. 양쪽이 모두 키값에 대해 unique함

이러면 outer로 join해줘야 한다. 한 쪽이 unique하지 않다면 논리적으로 group by 해서 unique하게 만들고 작업하는 게 수월하다.

Query로 치면 Union All로 하면 된다.

 

보통 left join을 검색하면 이런 그림이 나온다

틀린 그림은 아니지만 이렇게 되면 마치 A의 row도 유지될 거라는 착각을 하게 된다.

정확히 이 그림이 의미하는 바는 'A의 정보'가 유지된다는 거지 구조가 유지된다는 의미는 아니다.

예를 들어, A의 키값에 대해 B에 2가지 상태값으로 row가 나뉘어져 있다면

A에 대해 여러 row가 매핑되면서 A의 row가 늘어나게 된다.

따라서 각 테이블 형식의 데이터를 키값으로 A에는 전혀 없는 B의 컬럼을 붙이는 과정이라도

둘 다 키값에 대해 같은 기준으로 묶여있는지 체크가 필요하다.

케이스: 구글시트에서 여러 config 시트를 가져와 for문을 돌리면서 컬럼별 데이터 타입 변환

최초 접근은 이랬다.

dfs = [df1, df2, df3, df4]

for df in dfs:
	for col in df.columns:
    	if '날짜' in col or '일자' in col:
        	df[col] = pd.to_datetime(df[col])
        elif '금액' in col or '수수료율' in col or '분담률' in col:
        	df[col] = df[col].astype(float)
        else:
        	df[col] = df[col].astype(str)

inplace가 안 되는 현상(함수에서 가져다 쓰려고 하면 기존 object 형태)

찾아보니 iteration이 진행되는 전제에 문제가 있었다.

iteration은 기본적으로 해당 iteration의 item에 대한 기존값의 변경을 전제하지 않는다.

그걸 전제하면 update나 delete 행위가 iteration을 하는 변수 자체에 가해지는 경우,

최초에 설정된 iterator가 변경되기 때문이다. [ex) 1~5까지 반복하는데 첫번째 loop에서 4,5를 지워버리는 식]

그렇다고 모든 update 행위가 안 되는 것은 아니어 보인다. 논리적으로 성립 가능한 update 행위도 있을 수는 있다.

다만 이런 가능성은 습관적으로 배제하는 것이 좋다.

dfs = [df1, df2, df3, df4]
sheets = {}

for idx, df in enumerate(dfs):
	for col in df.columns:
    	if '날짜' in col or '일자' in col:
        	con_df[col] = pd.to_datetime(df[col])
        elif '금액' in col or '수수료율' in col or '분담률' in col:
        	df[col] = df[col].astype(float)
        else:
        	df[col] = df[col].astype(str)
    sheets[idx] = df

iteration을 수행하면서 해당 변수를 업데이트/삭제를 하는 경우 새로운 container에 담는 습관을 들여야 겠다.

(특히 형변환)

+ Recent posts