[C#] ###Clicker 개선판
개선은 진작 했지만, 관련해서 포스팅하지 않은 이유는 "티스토리 계정을 까먹어서..." 자동 로그인 해둔 것들은 포맷 후에 다시 로그인해야 하니 여간 귀찮은 일이 아니다. 어찌어찌 로그인해서
u-bvm.tistory.com
여기에서 이어진다.
최근에 돌리면서 마음에 안 드는 부분이 좀 있었기 때문에 고쳐보고자 했다.
- 파이어폭스 지원
- 크롬이 자원을 많이 먹는다는 느낌이 들어서 파폭이라면 괜찮지 않을까 싶었다. - URL 캐싱
- 아무래도 지금까지의 방식으론 이미 클릭해서 포인트를 얻을 수 없는 부분도 클릭을 계속하기 때문에
- 불필요한 부하를 만들고 있다고 생각했다.
- 사실 처음 만들 때부터 의도한 부분이긴 한데, 하다 보니 뒤로 밀렸다.
1. 파이어폭스 지원
private ChromeOptions GetChromeOptions()
{
var options = new ChromeOptions();
options.AddArguments("--headless", "--incognito");
return options;
}
private FirefoxOptions GetFirefoxOptions()
{
var options = new FirefoxOptions();
options.AddArguments("--headless");
options.BrowserExecutableLocation = "C:/Program Files/Mozilla Firefox/firefox.exe";
return options;
}
private ChromeDriverService GetChromeDriverService()
{
_chromeService = ChromeDriverService.CreateDefaultService();
_chromeService.SuppressInitialDiagnosticInformation = true;
_chromeService.HideCommandPromptWindow = true;
return _chromeService;
}
private FirefoxDriverService GetFirefoxDriverService()
{
string geckodriverPath = "C:/geckodriver/geckodriver.exe";
_firefoxService = FirefoxDriverService.CreateDefaultService(geckodriverPath);
_firefoxService.SuppressInitialDiagnosticInformation = true;
_firefoxService.HideCommandPromptWindow = true;
return _firefoxService;
}
셀레니움은 파이어폭스도 지원하기 때문에 그냥 크롬 코드를 갖다가 박으면 된다.
하지만 파이어폭스는 크롬처럼 자동으로 드라이버를 내려받기 않기 때문에,
geckodriver에서 드라이버 파일을 다운로드하고, 그 경로를 지정해 주어야 한다.
파폭으로도 동작하긴 했는데...
크롬보다 자원을 더 먹어서 흔적기관으로 남지 않을까 싶다.
2. URL 캐싱
광고 표시 위치가 다르면 같은 내용의 광고라도 별개의 광고로 취급한다.
따라서 URL 캐싱은 iframe
별로 해야 한다는 것을 알 수 있다.
다음과 같은 딕셔너리를 선언한다.
private ConcurrentDictionary<string, HashSet<string>> _ALinkCache = new ConcurrentDictionary<string, HashSet<string>>();
여러 스레드가 공유하기 때문에 ConcurrentDictionary
를 사용한다.
Key
인 string
은 iframe
의 이름, Value
인 HashSet
은 해당 광고 링크를 가진다.
private void FindAndClickN(IWebDriver driver, string url)
{
var iframes = WaitForIframes(driver);
foreach (var iframe in iframes)
{
string iframeId = iframe.GetAttribute("id");
if (_NLinkCache.TryGetValue(iframeId, out HashSet<string> clickedNLinks))
{
TryClickNInIframe(driver, iframe, url, clickedNLinks);
}
else
{
clickedNLinks = new HashSet<string>();
if (_NLinkCache.TryAdd(iframeId, clickedNLinks))
{
TryClickNInIframe(driver, iframe, url, clickedNLinks);
}
}
}
}
// ...
private void ClickN(IWebDriver driver, HashSet<string> clickedNLinks)
{
var NLinks = driver.FindElements(By.TagName("a"))
.Where(link => link.GetAttribute("href") != null);
foreach (var link in NLinks)
{
string href = link.GetAttribute("href");
if (clickedNLinks.Add(href))
{
((IJavaScriptExecutor)driver).ExecuteScript($"window.open('{href}');");
}
}
}
FindAndClickN()
은 진입점이다.
키 값이 있으면 그 밸류를 넘겨주고, 없다면 새로 만들어 넣는다.
ClickN
은 실제로 탭 열기가 이루어지는 부분이다.
링크가 HashSet
에 추가가 된다는 것은 해당 링크를 클릭한 적이 없다는 것이므로 클릭으로 이어진다.
추가가 실패했다면 이미 값이 있다는 것이고 클릭으로 이어지지 않는다.
따라서 어느정도 클릭이 이루어진 이터레이션 후반부에선 클릭이 일어나는 빈도가 낮아지게 되고,
이는 자연스레 부하 감소로 연결된다.
3. 다른 방식?
현재는 Job
이 완료될 때 마다 브라우저 인스턴스를 완전히 종료 후,
새로운 브라우저 인스턴스를 생성해 Job
을 처리한다.
계속 같은 인스턴스를 쓰니 원인을 알 수 없는 에러가 많이 생겼었다.
현재 방식으로 바꾸니 문제가 생기지 않아서 그대로 쓰고 있는데...
이젠 생성한 브라우저 인스턴스를 계속 돌려 써도 괜찮지 않을까 싶다.
그리고 클릭 처리에 관한 것이다.
손으로 할 땐, 클릭을 했는지 안 했는지 일일이 다 기억할 수 없어서
무식하게 다 클릭 했었고, 그 방식을 이렇게 구현한 것이다.
이전엔 순회하며 링크를 모아두고 일괄적으로 클릭처리를 하는 방법을 떠올리기도 했다.
아마 이게 좀 더 효율적일 수 있겠다 생각은 든다.
주기적으로 표시되는 iframe
이 내용이 달라지기 때문에,
달라진 이후에 미리 캐시한 링크를 클릭해도 포인트가 지급되는지 알아볼 필요가 있겠다.
추가
이거 뭔가 이상하다 했더니...
같은 곳으로 연결되는 iframe이라도 사이트에 따라 링크가 달라져서
제대로 필터링을 못하고 있었다.
이걸 어떻게 해결할지 생각을 좀 해봐야 할 듯하다.
private void ClickN(IWebDriver driver, HashSet<string> clickedNLinks)
{
var NLinks = driver.FindElements(By.TagName("a"))
.Where(link => link.GetAttribute("href") != null);
foreach (var link in NLinks)
{
string href = link.GetAttribute("href");
string pattern = @"/(?:x\d+|Top\d+)/.*";
var match = Regex.Match(href, pattern);
if (clickedNLinks.Add(match.Value))
{
((IJavaScriptExecutor)driver).ExecuteScript($"window.open('{href}');");
Logger.Debug($"Clicked '{match.Value}' : {href}");
}
else
{
Logger.Debug($"Click Passed : {href}");
}
}
}
각 링크들엔 패턴이 있었기 때문에 정규식으로 걸러내니까 잘 되더라.
로그를 확인하니 의도한 대로 동작했다.
'Study > C++ & C#' 카테고리의 다른 글
[C++] 채팅서버에 DB 실장 (0) | 2024.05.14 |
---|---|
[C++] ChatRoom 구현 (0) | 2024.04.09 |
[C++] Session 다중 접속 (0) | 2024.04.05 |
[C++] Boost.Asio 에코 서버 (0) | 2024.03.29 |
[C#] ###Clicker 개선판 (0) | 2024.03.18 |