local 환경에서 결제 시스템을 구축 중 웹훅을 받기 위해 ngrok 을 사용 중

다음 날 테스트해보니 웹훅이 날라오지 않아 의아하여 문제점을 파악

 

일단 테스트 요청을 보내보니 ngrok 터미널 창에서는 HTTP Requests가 분명 200 으로 날라오지만 전혀 출력은 안되는 상황

 

결과 값에 Invalid Host header 이 나오게 되었다. 분명 어제까진 잘 됐는데?... 뭐지..?

 

그래서 GPT랑 삽질 중

 

원인이 localhost:3000이 외부 접속을 막고 있다는 답변이 나옴

그래서 HOST:0.0.0.0 을 설정하여 모든 연결을 허용하여 똑같이 해보았다.

 

 

결과는 동일함.

애초에 연결이 막혔으면 200이 나오지 않았을까 싶기도 했다.

 

 

일단 Invalid Host header 에러에 대한 설명이다.

  • 이 에러는 개발 서버가 수신 허용하는 Host 헤더와 실제 요청의 Host 헤더가 다를 때 발생합니다. 주로 다음과 같은 상황에서 발생합니다:
    1. localhost 대신 IP 주소 또는 도메인으로 접속할 때
    2. 프록시나 리버스 프록시(Nginx, Caddy 등)를 사용하는 경우
    3. 외부 장치(다른 컴퓨터나 모바일)에서 개발 서버에 접근할 때

 

https://stackoverflow.com/questions/45425721/invalid-host-header-when-ngrok-tries-to-connect-to-react-dev-server

 

Invalid Host Header when ngrok tries to connect to React dev server

I'm trying to test my React application on a mobile device. I'm using ngrok to make my local server available to other devices and have gotten this working with a variety of other applications. How...

stackoverflow.com

ngrok http 8080 --host-header="localhost:8080"

 

위 링크에서 방법을 발견했다.

 

 

ngrok http 8080

 

 

나의 기존 실행 방식은 이러했다. 문제점은 수신 허용 Host가 실제 요청 Host 헤더와 다른 경우로 생각된다.

 

ngrok http 3000 이렇게 단독 실행으로 실행하게 되면 Host의 헤더는 https://xxxx.ngrok-free.app 로 생성된다고 한다.

 

즉, 외부에서 요청은 이렇게 전달된다.

Host: xxxx.ngrok-free.app

 

내부서버 로컬서버는 localhost만 신뢰하기 때문에 이러한 오류가 발생된 것으로 판단된다.

 

 

 --host-header="localhost:8080"

이 코드는 ngrok이 들어오는 요청의 헤더를 로컬서버에서 받을 수 있게 localhost로 강제 설정

 

즉, Browser → ngrok 나의 로컬서버 로 연결될 때 localhost로 들어온 것처럼 처리 하는 것이다.

'Python > 오류해결' 카테고리의 다른 글

mysql caching_sha2_password에러  (0) 2024.02.28
자동화 브라우저 다운로드 저장 위치 설정

 

 

  • 현재 상황

- chrome에서는 설정에서 다운로드 저장위치를 설정 할 수 있다.

 

1. 오른쪽 상단 ... 클릭

2. 설정

3. 다운로드에서 경로 설정

해서 내 크롬 프로필에 상태를 저장할 수 있다.

 

위 상태가 나의 프로필에 저장되는것 그래서 그 프로필(계정)으로 다운로드를 진행하면 정해진 폴더로 다운로드가 진행됨.

 



나의 경우 다운로드를 많이 진행해야 하며 서버마다 크롤러를 설정해야하는 상황 그래서 정적인 위치설정은 의미가 없음

(서버마다 저장 위치가 다르기 때문)

 

- 여기서 의문은 서버마다 프로필을 생성하면 되지 않느냐? 라고 물어볼 수 있다.

 

대답은

다운로드의 파일마다 위치를 다르게 하고 싶은 경우에는 프로필을 100개 1000개 생성 하는 건 그냥 노가다이기 때문에 이것도 고려하여 방법을 찾아보았다.

 

 

이것을 해결하기 위해 CDP(Chrome DevTools Protocol)  를 사용

 

https://chromedevtools.github.io/devtools-protocol/

 

Chrome DevTools Protocol

Chrome DevTools Protocol - version tot

chromedevtools.github.io

 

 

python 기준으로 설명하며 chromium 기반의 라이브러리에 사용이 가능하다

 

await page._client.send("Page.setDownloadBehavior", {
    "behavior": "allow",
    "downloadPath": download_folder
})

 

위 코드가 chrome 다운로드 경로 위치를 설정하는 코드이다.

기본 경로로 다운로드 설정이 되어있어도 위 코드가 먼저 동작하도록 되어있다.

downloadPath에 사용자의 환경에 맞게 (파일이름, 번호, 특성 등) 파일위치를 자유롭게 설정할 수 있다.

 

서버마다 위치가 다른 문제도 해결 가능하며,

파일 특성 마다 저장 위치를 유동적으로 다르게 설정할 수 있어 자동화 브라우저를 통해 다중 다운로드를 하는 경우에 유용하게 사용할 수 있는 기능이다.

크롤링과 스크래핑?

크롤링 (Crawling) 뉴스 기사 수집

크롤링은 웹사이트의 모든 페이지를 탐색하며 정보를 수집 구조화하는 과정을 의미합니다.

크롤러가 자동으로 웹사이트의 페이지 링크를 따라가며 데이터를 수집합니다.

스크래핑 (Scraping) 유튜브 구독자 좋아요 등 수집

스크래핑은 웹 페이지의 특정 데이터만 추출하는 작업을 의미합니다.

주로 특정 웹사이트에서 필요한 데이터만을 추출하여 가공하는 데 사용됩니다.

 

  • 크롤링: 웹사이트 전체 또는 다수의 페이지를 대상으로 데이터 수집
  • 스크래핑: 특정 페이지에서 필요한 데이터만 추출

 

스크래핑은 별점과 같은 특정 정보를 가져오는 것을 의미 

 

크롤링은 url을 이동하며 웹페이지를 조작해 데이터를 수집하는 것을 의미

 

url, 작성자, 날짜 등의 정보를 수집하기 위해선 Locator가 필요

Locator DOM 트리에서 특정 요소를 선택하는 데 사용

DOM 문서 객체 모델(Document Object Model) HTML, XML 문서를 표현하고 조작하는 데 사용되는 프로그래밍 인터페이스

 

Locator란 즉, CSS selector, Xpath를 가르킴

 

DOMtree란 html코드의 구조를 계층적으로 나타낸 것을 의미

*****HTML*****

<!DOCTYPE html>
<html>
<head>
    <title>Let's crawling</title>
</head>
<body>
    <h1>crawling Nice</h1>
    <p>crawling</p>
</body>
</html>

*****DOMTREE*****

html
├── head
│   └── title
│       └── "Let's crawling"
└── body
    ├── h1
    │   └── "crawling Nice"
    └── p
        └── "crawling"

 

DOMtree의 요소는 노드로 표시

  1. 루트 노드 (Root Node): 트리의 최상위 노드로, HTML 문서 전체를 나타냅니다.
  2. 요소 노드 (Element Node): HTML 요소를 나타내며, 트리의 가지(branch)에 해당합니다.
  3. 텍스트 노드 (Text Node): HTML 요소의 텍스트 내용을 나타냅니다.

DOMTREE형으로 구조화 하여 css xpath 원하는 요소를 찾는 것이 중요

 

css selector

 

css selector는 컴포넌트화된 환경에서 유일한 선택자를 찾기 어려움.

<div class="container">
    <div class="item">
        <h2>Title</h2>
        <p>Content</p>
    </div>
    <div class="item">
        <h2>text</h2>
        <p>item</p>
    </div>
</div>

 

div.item p -> 텍스트 기반으로 검색할 수 없어 2개의 p태그가 선택됨

이걸 xpath화 시키면

//div[@class="container"]/div[@class="item"]/h2[text()="text"]/following-sibling::p

2번째 p태그를 찾을 수 있음

<xpath>
<css selector>

 

부모 및 자식 선택

  1. ancestor: 현재 노드의 모든 조상 노드를 선택합니다.
  2. descendant: 현재 노드의 모든 후손 노드를 선택합니다.
  • XPath: XPath에서는 // 연산자를 사용하여 문서의 모든 위치에서 요소를 선택할 수 있습니다. 또한 ancestor 및 descendant 축을 사용하여 특정 요소의 조상 또는 후손 요소를 선택할 수 있습니다.
  • CSS 선택자: CSS 선택자는 > 및 공백을 사용하여 특정 요소의 직계 자식 또는 하위 요소를 선택할 수 있습니다. 그러나 CSS 선택자에는 조상요소를 선택하는 방법이 없습니다.
  • 직계 자식 선택: .parent > .child
  • 하위 요소 선택: .container .descendant

형제 선택

  1. following-sibling: 현재 노드의 다음 형제 노드들을 선택합니다.
  2. preceding-sibling: 현재 노드의 이전 형제 노드들을 선택합니다.
  • XPath: XPath에서는 following-sibling 및 preceding-sibling 축을 사용하여 현재 요소의 다음 형제 또는 이전 형제 요소를 선택할 수 있습니다.
  • CSS 선택자: CSS 선택자에서는 + 및 ~ 연산자를 사용하여 현재 요소의 다음 형제 요소를 선택할 수 있습니다. 그러나 이전 형제 요소를 선택하는 방법은 없습니다. (+ 는 다음 요소중 첫번째,  ~ 형제요소중 p 요소)

css selector -> p + span (이전 선택 X)

  • 다음 형제 선택: //p/following-sibling::span[1]
  • 이전 형제 선택: //span/preceding-sibling::p[1]  -> ??? 뭐가 선택될지

자식선택

  • XPath: XPath에서는 child 축을 사용하여 현재 요소의 모든 자식 요소를 선택할 수 있습니다.
  • CSS 선택자: CSS 선택자에서는 공백을 사용하여 현재 요소의 모든 하위 요소를 선택할 수 있습니다.

 

.container .child span:nth-child(2)

 

//div[@class="parent"]/div[@class="child"]/span[2]

or

//div[@class="parent"]/div[@class="child"]/span[text()='두 번째 스팬']

 

xpath 장점

  • 양방향 제어 가능 (부모 자식 상위 하위 검색가능)
  • 축(Axes)/함수를 사용하여 특정 노드 선택가능
  • 최신 및 구식 브라우저도 호환가능

단점

  • shadow DOM에는 사용 불가능(iframe -> 개별화된 DOM tree)
  • 속도가 느리고 복잡함

 

css selector 장점

  • 속도가 빠르고 읽기 쉬우며 배우기 쉬움
  • 단일 제어이므로 안정적

단점

  • 단방향이므로 제한적임
  • 복잡하거나 컴포넌트화 되어있으면 로케이터를 구성하기 어려움
  • 텍스트를 기반으로 selector를 구성하지 못함

 

 

Xpath Vs CSS Selector: Key Differences

XPath vs CSS selector Key differences explained with examples, this blog helps to choose the best locators for the automation framework.

testsigma.com

 

puppyteer를 python에서 사용하기 위해 pyppeteer를 사용하는 중이며

사용하기 위해서 공식 문서를 보며 숙지하는게 좋다.

https://miyakogi.github.io/pyppeteer/

 

Pyppeteer’s documentation — Pyppeteer 0.0.25 documentation

Pyppeteer is to be as similar as puppeteer, but some differences between python and JavaScript make it difficult. These are differences between puppeteer and pyppeteer. Element selector method name ($ -> querySelector) In python, $ is not usable for method

miyakogi.github.io

 

비동기로 크롤링을 하기 때문에 pyppeteer를 사용하게 되었다.

 

주로

await page.goto(url, {"waitUntil": "domcontentloaded"})
  • 'load': 페이지의 'load' 이벤트가 발생할 때까지 기다립니다.
  • 'domcontentloaded': 페이지의 'DOMContentLoaded' 이벤트가 발생할 때까지 기다립니다.
  • 'networkidle0': 네트워크가 사용되지 않는 상태(네트워크 요청이 0개 이하)가 되었을 때까지 기다립니다.
  • 'networkidle2': 네트워크가 0.5초 이상 유휴 상태가 될 때까지 기다립니다.

아무 값 없이 url만 입력하게 되면 네트워크 활동이 없을 때 까지 기다리는 상태

그래서 크롤링을 하기 위해선 domcontentloaded를 주로 사용

 

크롤링하려는 웹사이트의 환경과 조건에 맞게 사용

 

먼저 크롤링을 하기 위해 html을 가져와야 하는데 여러 기능이나 패키지가 있겠지만
BeautifulSoup를 사용하여 전체의 html가져와 데이터 파싱 또는 클릭, 입력 기능을 사용할 수 있게 된다.

 

 

click 을 사용하기 위해서 추가 할 selector를 검색 (xpath는 적용 X)

await page.click(selector)

 

만약 클릭을 해야한다 evaluate를 사용하여 click을 시도하는 방법도 있다.

python안에서 javascript코드를 전달해주는 느낌이다.
예시)  2500만큼 스크롤

async def scroll_page(page):
    await page.evaluate("window.scrollBy(0, 2500);")
    await asyncio.sleep(2)

 

스크롤을 한 후 딜레이를 주어 로드 시간을 기다린다.

 

type은 값을 입력하는 기능

await page.type(selector, email)

위와 같이 텍스트박스나 입력란의 selector를 지정하여 오른쪽에는 저장된 값을 넣어주면 된다.

 

 

getenv로 mysql에 대한 정보를 전달하고 있는데
print할 때 정상적으로 출력되는걸 볼 수 있었습니다.

 

caching_sha2_password
분명 잘 찍히기도 하고 다른곳에서는 정상작동 되는걸 보니 

 

mysql-connector-python을 재설치 해보았습니다.

pip uninstall mysql-connector-python

 

 

최신버전으로 재 설치 후 동작해보면 오류가 사라지고 잘 해결됩니다.

 

pip install mysql-connector-python

 

(아마 버전문제라고 생각됩니다....ㅎㅎ)

'Python > 오류해결' 카테고리의 다른 글

localhost ngrok Invalid Host header 에러 해결  (0) 2025.07.31

원하는 동작

1,2,3은 각자 비동기를 자기 일을 하고 있으면서 일정 횟수가 되면 작업을 중지 한다. 그리고 나머지가 끝날때까지 대기

 

그렇게 while문으로 무한대기중이지만 

3개의 작업이 모두 종료되어 조건이 일치할 때 나오도록 설계하였습니다.

 

여기서 문제점은

1,2,3 중에 하나가 작업이 먼저 종료되어 대기중에 있을 때 나머지도 동작을 멈춘다는것입니다.

 

그 이유는 파이썬은 GIL(Global Interpreter Lock) 한 번에 하나의 스레드만 바이트코드를 실행할 수 있는 조건이라서

해당 스레드 즉, 먼저 작업이 종료된 스레드가 CPU를 점유하고 다른스레드의 자리를  마련할 공간이 없어 계속 멈춘상태로 있는 것입니다.

 

그러기위해선 sleep을 주어 스레드가 실행될 공간을 마련해 주는 것 입니다. 이렇게 공평하게 자리를 나눠갖게 하는 것이 해결방법이었습니다.

 

await asyncio.sleep(3)

+ Recent posts