first commit
This commit is contained in:
commit
9ab761eff7
|
|
@ -0,0 +1,8 @@
|
||||||
|
__pycache__/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
Lib/
|
||||||
|
.vscode/
|
||||||
|
Include/
|
||||||
|
Scripts/
|
||||||
|
pyvenv.cfg
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Python3 샘플 코드 #
|
||||||
|
|
||||||
|
|
||||||
|
import requests
|
||||||
|
apikey ='X9Tz3JqC%2FJcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6%2Bt3dyLZV3gh2TPXdNhxsRQwaKP673Q%3D%3D'
|
||||||
|
apikey2 = 'X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q=='
|
||||||
|
apikey3 = "X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q=="
|
||||||
|
|
||||||
|
# 디코딩된 API 키 사용
|
||||||
|
# apikey2 ='X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q=='
|
||||||
|
# url = 'http://kipo-api.kipi.or.kr/openapi/service/trademarkInfoSearchService'
|
||||||
|
url = 'http://kipo-api.kipi.or.kr/openapi/service/trademarkInfoSearchService/getWordSearch'
|
||||||
|
params = {
|
||||||
|
'serviceKey' : apikey3, # 디코딩된 키 사용
|
||||||
|
'searchString' : 'love*롯데',
|
||||||
|
'searchRecentYear' : '0',
|
||||||
|
'title' : '사랑 : LOVE',
|
||||||
|
'fullText' : '',
|
||||||
|
'drawing' : '',
|
||||||
|
'bigDrawing' : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get(url, params=params)
|
||||||
|
print(response.content)
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"01": "공업/과학 및 사진용 및 농업/원예 및 임업용 화학제; 미가공 인조수지, 미가공 플라스틱; 소화 및 화재예방용 조성물; 조질제 및 땜납용 조제; 수피용 무두질제; 공업용 접착제; 퍼티 및 기타 페이스트 충전제; 퇴비, 거름, 비료; 산업용 및 과학용 생물학적 제제",
|
||||||
|
"02": "페인트, 니스, 래커; 방청제 및 목재 보존제; 착색제, 염료; 인쇄, 표시 및 판화용 잉크; 미가공 천연수지; 도장용/장식용/인쇄용/미술용 금속박(箔) 및 금속분(紛)",
|
||||||
|
"03": "비의료용 화장품 및 세면용품; 비의료용 치약; 향료, 에센셜 오일; 표백제 및 기타 세탁용 제제; 세정/광택 및 연마재",
|
||||||
|
"04": "공업용 오일 및 그리스, 왁스; 윤활제; 먼지흡수제, 먼지습윤제 및 먼지흡착제; 연료 및 발광체; 조명용 양초 및 심지",
|
||||||
|
"05": "약제, 의료용 및 수의과용 제제; 의료용 위생제; 의료용 또는 수의과용 식이요법 식품 및 제제, 유아용 식품; 인체용 및 동물용 식이보충제; 플라스터, 외상치료용 재료; 치과용 충전재료, 치과용 왁스; 소독제; 해충구제제; 살균제, 제초제",
|
||||||
|
"06": "일반금속 및 그 합금, 광석; 금속제 건축 및 구축용 재료; 금속제 이동식 건축물; 비전기용 일반금속제 케이블 및 와이어; 소형금속제품; 저장 또는 운반용 금속제 용기; 금고",
|
||||||
|
"07": "기계, 공작기계, 전동공구; 모터 및 엔진(육상차량용은 제외); 기계 커플링 및 전동장치 부품(육상차량용은 제외); 농기구(수동식 수공구는 제외); 부란기(孵卵器); 자동판매기",
|
||||||
|
"08": "수동식 수공구 및 수동기구; 커틀러리; 휴대 무기(화기는 제외); 면도기",
|
||||||
|
"09": "과학, 연구, 항법, 측량, 사진, 영화, 시청각, 광학, 계량, 측정, 신호, 탐지, 시험, 검사, 구명 및 교육용 기기; 전기 분배 또는 전기 사용의 전도, 전환, 변형, 축적, 조절 또는 통제를 위한 기기; 음향/영상 또는 데이터의 기록/전송/재생 또는 처리용 장치 및 기구; 기록 및 내려받기 가능한 미디어, 컴퓨터 소프트웨어, 빈 디지털 또는 아날로그 기록 및 저장매체; 동전작동식 기계장치; 금전등록기, 계산기; 컴퓨터 및 컴퓨터주변기기; 잠수복, 잠수마스크, 잠수용 귀마개, 다이버 및 수영용 노즈클립, 잠수용 장갑, 잠수용 호흡장치; 소화기기",
|
||||||
|
"10": "외과용, 내과용, 치과용 및 수의과용 기계기구; 의지(義肢), 의안(義眼) 및 의치(義齒); 정형외과용품; 봉합용 재료; 장애인용 치료 및 재활보조장치; 안마기; 유아수유용 기기 및 용품; 성활동용 기기 및 용품",
|
||||||
|
"11": "조명용, 가열용, 냉각용, 증기발생용, 조리용, 건조용, 환기용, 급수용, 위생용 장치 및 설비",
|
||||||
|
"12": "수송기계기구; 육상, 항공 또는 해상을 통해 이동하는 수송수단",
|
||||||
|
"13": "화기(火器); 탄약 및 발사체; 폭약; 폭죽",
|
||||||
|
"14": "귀금속 및 그 합금; 보석, 귀석 및 반귀석; 시계용구",
|
||||||
|
"15": "악기; 악보대 및 악기용 받침대; 지휘봉",
|
||||||
|
"16": "종이 및 판지; 인쇄물; 제본재료; 사진; 문방구 및 사무용품(가구는 제외); 문방구용 또는 가정용 접착제; 제도용구 및 미술용 재료; 회화용 솔; 교재; 포장용 플라스틱제 시트, 필름 및 가방; 인쇄활자, 프린팅블록",
|
||||||
|
"17": "미가공 및 반가공 고무, 구타페르카, 고무액(gum), 석면, 운모(雲母) 및 이들의 제품; 제조용 압출성형형태의 플라스틱 및 수지; 충전용, 마개용 및 절연용 재료; 비금속제 신축관, 튜브 및 호스",
|
||||||
|
"18": "가죽 및 모조가죽; 수피; 수하물가방 및 운반용 가방; 우산 및 파라솔; 걷기용 지팡이; 채찍 및 마구(馬具); 동물용 목걸이, 가죽끈 및 의류",
|
||||||
|
"19": "건축용 및 구축용 비금속제 건축재료; 건축용 비금속제 경질관(硬質管); 아스팔트, 피치, 타르 및 역청; 비금속제 이동식 건축물; 비금속제 기념물",
|
||||||
|
"20": "가구, 거울, 액자; 보관 또는 운송용 비금속제 컨테이너; 미가공 또는 반가공 뼈, 뿔, 고래수염 또는 나전(螺鈿); 패각; 해포석(海泡石); 호박(琥珀)(원석)",
|
||||||
|
"21": "가정용 또는 주방용 기구 및 용기; 조리기구 및 식기(포크, 나이프 및 스푼은 제외); 빗 및 스펀지; 솔(페인트 솔은 제외); 솔 제조용 재료; 청소용구; 비건축용 미가공 또는 반가공 유리; 유리제품, 도자기제품 및 토기제품",
|
||||||
|
"22": "로프 및 노끈; 망(網); 텐트 및 타폴린; 직물제 또는 합성재료제 차양; 돛; 하역물운반용 및 보관용 포대; 충전재료(고무/플라스틱/종이 및 판지제는 제외); 직물용 미가공 섬유 및 그 대용품",
|
||||||
|
"23": "직물용 실(絲)",
|
||||||
|
"24": "직물 및 직물대용품; 가정용 린넨; 직물 또는 플라스틱제 커튼",
|
||||||
|
"25": "의류, 신발, 모자",
|
||||||
|
"26": "레이스, 장식용 끈 및 자수포, 의류장식용 리본 및 나비매듭리본; 단추, 훅 및 아이(hooks and eyes), 핀 및 바늘; 조화(造花); 머리장식품; 가발",
|
||||||
|
"27": "카펫, 융단, 매트, 리놀륨 및 기타 바닥깔개용 재료; 비직물제 벽걸이",
|
||||||
|
"28": "오락용구, 장난감; 비디오게임장치; 체조 및 스포츠용품; 크리스마스트리용 장식품",
|
||||||
|
"29": "식육, 생선, 가금 및 엽조수; 고기진액; 보존처리/냉동/건조 및 조리된 과일 및 채소; 젤리, 잼, 콤폿; 달걀; 우유, 치즈, 버터, 요구르트 및 기타 유제품; 식용 유지(油脂)",
|
||||||
|
"30": "커피, 차(茶), 코코아 및 그 대용물; 쌀, 파스타 및 국수; 타피오카 및 사고(sago); 곡분 및 곡물 조제품; 빵, 페이스트리 및 과자; 초콜릿; 아이스크림, 셔벗 및 기타 식용 얼음; 설탕, 꿀, 당밀(糖蜜); 식품용 이스트, 베이킹 파우더; 소금, 조미료, 향신료, 보존처리된 허브; 식초, 소스 및 기타 조미료; 얼음",
|
||||||
|
"31": "미가공 농업, 수산양식, 원예 및 임업 생산물; 미가공 곡물 및 종자; 신선한 과실 및 채소, 신선한 허브; 살아 있는 식물 및 꽃; 구근(球根), 모종 및 재배용 곡물종자; 살아있는 동물; 동물용 사료 및 음료; 맥아",
|
||||||
|
"32": "맥주; 비알코올성 음료; 광천수 및 탄산수; 과실음료 및 과실주스; 시럽 및 비알코올성 음료용 제제",
|
||||||
|
"33": "알코올성 음료(맥주는 제외); 음료제조용 알코올성 제제",
|
||||||
|
"34": "담배 및 대용담배; 권연 및 여송연; 흡연자용 전자담배 및 기화기; 흡연용구; 성냥",
|
||||||
|
"35": "광고업; 사업관리/조직 및 경영업; 사무처리업",
|
||||||
|
"36": "금융, 통화 및 은행업; 보험서비스업; 부동산업",
|
||||||
|
"37": "건축서비스업; 설치 및 수리서비스업; 채광업/석유 및 가스 시추업",
|
||||||
|
"38": "통신서비스업",
|
||||||
|
"39": "운송업; 상품의 포장 및 보관업; 여행알선업",
|
||||||
|
"40": "재료처리업; 폐기물 재생업; 공기 정화 및 물 처리업; 인쇄 서비스업; 음식 및 음료수 보존업",
|
||||||
|
"41": "교육업; 훈련제공업; 연예오락업; 스포츠 및 문화활동업",
|
||||||
|
"42": "과학적, 기술적 서비스업 및 관련 연구, 디자인업; 산업분석, 산업연구 및 산업디자인 서비스업; 품질 관리 및 인증 서비스업; 컴퓨터 하드웨어 및 소프트웨어의 디자인 및 개발업",
|
||||||
|
"43": "식음료제공서비스업; 임시숙박시설업",
|
||||||
|
"44": "의료업; 수의업; 인간 또는 동물을 위한 위생 및 미용업; 농업, 수산양식, 원예 및 임업 서비스업",
|
||||||
|
"45": "법무서비스업; 유형의 재산 및 개인을 물리적으로 보호하기 위한 보안서비스업; 이성(異性) 소개업, 온라인 소셜 네트워킹 서비스업; 장례업; 베이비시팅업"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import requests
|
||||||
|
class KiprisApiClient:
|
||||||
|
def __init__(self, service_key):
|
||||||
|
self.service_key = service_key
|
||||||
|
self.base_url = "http://plus.kipris.or.kr/"
|
||||||
|
|
||||||
|
def search_patents(self, word, year, patent, utility, numOfRows=30, pageNo=1):
|
||||||
|
params = {
|
||||||
|
'accessKey': self.service_key,
|
||||||
|
'word': word,
|
||||||
|
'year': year,
|
||||||
|
'patent': str(patent).lower(),
|
||||||
|
'utility': str(utility).lower(),
|
||||||
|
'numOfRows': numOfRows,
|
||||||
|
'pageNo': pageNo
|
||||||
|
}
|
||||||
|
response = requests.get(self.base_url, params=params)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# API 키와 검색하고자 하는 단어 설정
|
||||||
|
service_key = '/loM8C4yXZsTLAmp7PLoq2UCl5zg/OFhZCibzO/D968='
|
||||||
|
word = '스키에이트'
|
||||||
|
client = KiprisApiClient(service_key)
|
||||||
|
|
||||||
|
# API 호출 예제
|
||||||
|
# word = '전자회로'
|
||||||
|
year = '0' # 검색 년도 범위
|
||||||
|
patent = True
|
||||||
|
utility = False
|
||||||
|
numOfRows = 30
|
||||||
|
pageNo = 1
|
||||||
|
|
||||||
|
result = client.search_patents(word, year, patent, utility, numOfRows, pageNo)
|
||||||
|
if 'items' in result:
|
||||||
|
for item in result['items']:
|
||||||
|
print(f"발명의 명칭: {item['inventionTitle']}, 등록번호: {item['registerNumber']}")
|
||||||
|
else:
|
||||||
|
print("검색 결과가 없습니다.")
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import requests
|
||||||
|
|
||||||
|
class Kipris:
|
||||||
|
def __init__(self, url, params=None):
|
||||||
|
self.url = url
|
||||||
|
self.params = params
|
||||||
|
self.results = []
|
||||||
|
|
||||||
|
def fetch_and_decode(self):
|
||||||
|
# API 요청 및 응답 받기
|
||||||
|
response = requests.get(self.url, params=self.params)
|
||||||
|
decoded_data = response.content.decode('utf-8')
|
||||||
|
return decoded_data
|
||||||
|
|
||||||
|
def parse_xml(self, xml_data):
|
||||||
|
# XML 데이터 파싱
|
||||||
|
root = ET.fromstring(xml_data)
|
||||||
|
total_items = 0
|
||||||
|
status_registered = 0
|
||||||
|
status_published = 0
|
||||||
|
|
||||||
|
# 'body/items/item' 경로에 맞춰 'item' 태그를 순회하면서 필요한 데이터 추출
|
||||||
|
for item in root.findall('.//body/items/item'):
|
||||||
|
total_items += 1
|
||||||
|
application_status = item.find('applicationStatus').text if item.find('applicationStatus') is not None else None
|
||||||
|
|
||||||
|
# 각 상태의 개수를 카운트
|
||||||
|
if application_status == "등록":
|
||||||
|
status_registered += 1
|
||||||
|
if application_status == "공개":
|
||||||
|
status_published += 1
|
||||||
|
|
||||||
|
if application_status in ["등록", "공개"]:
|
||||||
|
result = {
|
||||||
|
"index_no": item.find('indexNo').text if item.find('indexNo') is not None else None,
|
||||||
|
"application_number": item.find('applicationNumber').text if item.find('applicationNumber') is not None else None,
|
||||||
|
"application_date": item.find('applicationDate').text if item.find('applicationDate') is not None else None,
|
||||||
|
"publication_number": item.find('publicationNumber').text if item.find('publicationNumber') is not None else None,
|
||||||
|
"publication_date": item.find('publicationDate').text if item.find('publicationDate') is not None else None,
|
||||||
|
"registration_date": item.find('registrationDate').text if item.find('registrationDate') is not None else None,
|
||||||
|
"registration_number": item.find('registrationNumber').text if item.find('registrationNumber') is not None else None,
|
||||||
|
"applicant_name": item.find('applicantName').text if item.find('applicantName') is not None else None,
|
||||||
|
"agent_name": item.find('agentName').text if item.find('agentName') is not None else None,
|
||||||
|
"title": item.find('title').text if item.find('title') is not None else None,
|
||||||
|
"drawing_url": item.find('drawing').text if item.find('drawing') is not None else None,
|
||||||
|
"big_drawing_url": item.find('bigDrawing').text if item.find('bigDrawing') is not None else None,
|
||||||
|
"full_text": item.find('fullText').text if item.find('fullText') is not None else None,
|
||||||
|
"application_status": application_status,
|
||||||
|
"classification_code": item.find('classificationCode').text if item.find('classificationCode') is not None else None
|
||||||
|
}
|
||||||
|
self.results.append(result)
|
||||||
|
|
||||||
|
# 상태 개수와 총 아이템 개수 출력
|
||||||
|
print(f"검색된 item 총 개수: {total_items}")
|
||||||
|
print(f"등록 상태인 item 개수: {status_registered}")
|
||||||
|
print(f"공개 상태인 item 개수: {status_published}")
|
||||||
|
|
||||||
|
def get_results(self):
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
xml_data = self.fetch_and_decode()
|
||||||
|
self.parse_xml(xml_data)
|
||||||
|
return self.get_results()
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import requests
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
# 디코딩된 API '
|
||||||
|
apikey2 = 'X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q=='
|
||||||
|
|
||||||
|
# 인코딩 api
|
||||||
|
# apikey1 = 'X9Tz3JqC%2FJcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6%2Bt3dyLZV3gh2TPXdNhxsRQwaKP673Q%3D%3D'
|
||||||
|
|
||||||
|
url = 'http://kipo-api.kipi.or.kr/openapi/service/trademarkInfoSearchService/getWordSearch'
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'serviceKey': apikey2, # 디코딩된 키 사용
|
||||||
|
'searchString': '스키에이트',
|
||||||
|
'searchRecentYear': '0',
|
||||||
|
'title': '',
|
||||||
|
'fullText': '',
|
||||||
|
'drawing': '',
|
||||||
|
'bigDrawing': ''
|
||||||
|
}
|
||||||
|
|
||||||
|
response1 = requests.get(url, params=params)
|
||||||
|
print(f"response1 : {response1.content}")
|
||||||
|
root = ET.fromstring(response1.content)
|
||||||
|
# response2 = requests.get(
|
||||||
|
# 'http://kipo-api.kipi.or.kr/openapi/service/patUtiModInfoSearchSevice/getWordSearch?serviceKey=X9Tz3JqC%2FJcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6%2Bt3dyLZV3gh2TPXdNhxsRQwaKP673Q%3D%3D&word=%EC%8A%A4%ED%82%A4%EC%97%90%EC%9D%B4%ED%8A%B8&year=0&patent=true&utility=true'
|
||||||
|
# )
|
||||||
|
# print(f"response2 : {response2.content}")
|
||||||
|
|
||||||
|
|
||||||
|
# Extracting items
|
||||||
|
items = []
|
||||||
|
for item in root.findall(".//item"):
|
||||||
|
data = {
|
||||||
|
"applicantName": item.find("applicantName").text,
|
||||||
|
"applicationNumber": item.find("applicationNumber").text,
|
||||||
|
"applicationDate": item.find("applicationDate").text,
|
||||||
|
"applicationStatus": item.find("applicationStatus").text,
|
||||||
|
"publicationNumber": item.find("publicationNumber").text,
|
||||||
|
"publicationDate": item.find("publicationDate").text,
|
||||||
|
"registrationNumber": item.find("registrationNumber").text,
|
||||||
|
"registrationDate": item.find("registrationDate").text,
|
||||||
|
"agentName": item.find("agentName").text if item.find("agentName") is not None else "",
|
||||||
|
"drawing": item.find("drawing").text,
|
||||||
|
"bigDrawing": item.find("bigDrawing").text
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
import sys
|
||||||
|
from PyQt5.QtWidgets import QApplication, QMessageBox, QSizePolicy, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit, QGridLayout
|
||||||
|
from PyQt5.QtGui import QPixmap
|
||||||
|
from PyQt5.QtCore import Qt, QByteArray, QBuffer, QIODevice
|
||||||
|
from web_scraper import WebScraper
|
||||||
|
|
||||||
|
class MainApp(QWidget):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.initUI()
|
||||||
|
self.scraper = WebScraper()
|
||||||
|
self.scraper.setup_browser()
|
||||||
|
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
# 검색 레이아웃
|
||||||
|
searchLayout = QHBoxLayout()
|
||||||
|
self.searchLabel = QLabel('검색어 입력:', self)
|
||||||
|
self.searchLineEdit = QLineEdit(self)
|
||||||
|
self.searchLineEdit.returnPressed.connect(self.start_search)
|
||||||
|
self.searchButton = QPushButton('검색 실행', self)
|
||||||
|
self.searchButton.clicked.connect(self.start_search)
|
||||||
|
|
||||||
|
searchLayout.addWidget(self.searchLabel)
|
||||||
|
searchLayout.addWidget(self.searchLineEdit)
|
||||||
|
searchLayout.addWidget(self.searchButton)
|
||||||
|
|
||||||
|
# 로그 레이아웃
|
||||||
|
logLayout = QVBoxLayout()
|
||||||
|
self.logTextEdit = QTextEdit(self)
|
||||||
|
self.logTextEdit.setReadOnly(True)
|
||||||
|
|
||||||
|
logLayout.addWidget(self.logTextEdit)
|
||||||
|
|
||||||
|
# 메인 레이아웃
|
||||||
|
mainLayout = QVBoxLayout()
|
||||||
|
mainLayout.addLayout(searchLayout)
|
||||||
|
mainLayout.addLayout(logLayout)
|
||||||
|
|
||||||
|
self.setLayout(mainLayout)
|
||||||
|
self.setWindowTitle('Web Scraper')
|
||||||
|
self.setGeometry(300, 300, 300, 400)
|
||||||
|
|
||||||
|
def wrap_text(self, text, width=40):
|
||||||
|
"""주어진 너비에 맞게 텍스트를 줄바꿈합니다."""
|
||||||
|
words = text.split()
|
||||||
|
wrapped_text = ''
|
||||||
|
line_length = 0
|
||||||
|
|
||||||
|
for word in words:
|
||||||
|
if line_length + len(word) + 1 > width:
|
||||||
|
wrapped_text += '\n'
|
||||||
|
line_length = 0
|
||||||
|
wrapped_text += word + ' '
|
||||||
|
line_length += len(word) + 1
|
||||||
|
|
||||||
|
return wrapped_text.strip()
|
||||||
|
|
||||||
|
def start_search(self):
|
||||||
|
term = self.searchLineEdit.text()
|
||||||
|
if not term:
|
||||||
|
self.logTextEdit.append("검색어를 입력해주세요.")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.logTextEdit.append("검색을 시작합니다...")
|
||||||
|
results = self.scraper.search_for_term(term)
|
||||||
|
if results:
|
||||||
|
self.logTextEdit.append("검색 완료. 결과를 처리합니다...")
|
||||||
|
self.show_results(results)
|
||||||
|
else:
|
||||||
|
self.logTextEdit.append("검색 결과가 없거나 오류가 발생했습니다.")
|
||||||
|
QMessageBox.information(self, "검색 결과 없음", "검색 결과가 없으므로 지재권에 안심하시면 됩니다.", QMessageBox.Ok)
|
||||||
|
|
||||||
|
|
||||||
|
def show_results(self, results):
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 결과 위젯 생성
|
||||||
|
self.results_widget = QWidget()
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
self.results_widget.setLayout(layout)
|
||||||
|
|
||||||
|
# 결과 갯수 확인 및 레이아웃 동적 생성
|
||||||
|
total_count = int(results['total_count'])
|
||||||
|
set_count = min(total_count, 10)
|
||||||
|
grid_layout = QGridLayout()
|
||||||
|
layout.addLayout(grid_layout)
|
||||||
|
grid_index = 0
|
||||||
|
grid_columns = 5
|
||||||
|
|
||||||
|
for i in range(1, set_count + 1):
|
||||||
|
result_key = f"result_{i}"
|
||||||
|
if result_key in results:
|
||||||
|
result = results[result_key]
|
||||||
|
|
||||||
|
# 테두리 설정
|
||||||
|
border_style = ''
|
||||||
|
if result['admin_status'] == '등록':
|
||||||
|
border_style = 'border: 4px solid red;'
|
||||||
|
elif result['admin_status'] == '공고':
|
||||||
|
border_style = 'border: 3px solid black;'
|
||||||
|
|
||||||
|
# 각 결과에 대한 레이아웃 생성
|
||||||
|
item_layout = QVBoxLayout()
|
||||||
|
item_widget = QWidget() # 위젯 생성
|
||||||
|
|
||||||
|
# item_layout의 크기 정책 설정
|
||||||
|
item_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)
|
||||||
|
|
||||||
|
# 이미지 처리
|
||||||
|
image_label = QLabel()
|
||||||
|
image_label.setFixedSize(150, 150)
|
||||||
|
image_data = self.scraper.fetch_image_data(result['IDimageURL'])
|
||||||
|
pixmap = QPixmap()
|
||||||
|
pixmap.loadFromData(image_data)
|
||||||
|
# QLabel의 크기에 맞게 이미지 크기 조정
|
||||||
|
scaled_pixmap = pixmap.scaled(image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||||
|
image_label.setPixmap(scaled_pixmap)
|
||||||
|
# QLabel의 가로 세로 중앙에 이미지 표시
|
||||||
|
image_label.setAlignment(Qt.AlignCenter)
|
||||||
|
# 이미지 표시 위젯의 크기 조정 정책 설정
|
||||||
|
image_label.setScaledContents(True)
|
||||||
|
|
||||||
|
#이미지 중앙배치를 위해
|
||||||
|
horizontal_layout = QHBoxLayout()
|
||||||
|
horizontal_layout.addWidget(image_label)
|
||||||
|
horizontal_layout.setAlignment(Qt.AlignCenter)
|
||||||
|
item_layout.addLayout(horizontal_layout)
|
||||||
|
|
||||||
|
# item_layout.addWidget(image_label)
|
||||||
|
|
||||||
|
# 정보 텍스트
|
||||||
|
# info_text = f"상표권명: {result['title']}\n등록상태: {result['admin_status']}\nCategory: {result['product_category']}\nApplicant: {result['applicant']}\nPublication Date: {result['publication_date']}\nRegistration Date: {result['registration_date']}"
|
||||||
|
info_text = f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>상표권명: {result['title']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>등록상태: {result['admin_status']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 9pt;'>카테고리: {result['product_category']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 9pt;'>권리자: {result['applicant']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 9pt;'> {result['publication_date']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 9pt;'> {result['registration_date']}</span>"
|
||||||
|
|
||||||
|
info_label = QLabel(info_text)
|
||||||
|
info_label.setToolTip(self.wrap_text(result['category_description'], 50))
|
||||||
|
image_label.setToolTip(self.wrap_text(result['category_description'], 50))
|
||||||
|
item_layout.addWidget(info_label)
|
||||||
|
|
||||||
|
image_label.setStyleSheet(border_style)
|
||||||
|
info_label.setStyleSheet(border_style)
|
||||||
|
|
||||||
|
# 레이아웃에 위젯 추가
|
||||||
|
row = grid_index // grid_columns
|
||||||
|
col = grid_index % grid_columns
|
||||||
|
grid_layout.addLayout(item_layout, row, col)
|
||||||
|
grid_index += 1
|
||||||
|
|
||||||
|
# 결과 위젯에 닫기 버튼 추가
|
||||||
|
close_button = QPushButton("Close")
|
||||||
|
close_button.clicked.connect(self.close_results_widget)
|
||||||
|
layout.addWidget(close_button)
|
||||||
|
|
||||||
|
# 결과 위젯을 메인 윈도우에 추가
|
||||||
|
self.results_widget.setGeometry(300, 300, 600, 300) # 위치와 크기 설정
|
||||||
|
self.results_widget.setWindowTitle('Search Results') # 타이틀 설정
|
||||||
|
self.results_widget.show()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error displaying results: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def close_results_widget(self):
|
||||||
|
# 결과 위젯닫기 함수를 호출할 때 사용하는 메서드
|
||||||
|
self.results_widget.close()
|
||||||
|
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
self.scraper.close_browser()
|
||||||
|
event.accept()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
ex = MainApp()
|
||||||
|
ex.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
@ -0,0 +1,248 @@
|
||||||
|
import sys
|
||||||
|
import asyncio
|
||||||
|
from tkinter import EXCEPTION
|
||||||
|
import qasync
|
||||||
|
from PyQt5.QtWidgets import QApplication, QGridLayout, QSizePolicy, QProgressDialog, QMessageBox, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtGui import QPixmap
|
||||||
|
from web_scraper_async import WebScraper
|
||||||
|
|
||||||
|
class MainApp(QWidget):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.initUI()
|
||||||
|
self.scraper = WebScraper()
|
||||||
|
self.results_widget = None # 초기에는 결과 위젯이 없음
|
||||||
|
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
searchLayout = QHBoxLayout()
|
||||||
|
self.searchLabel = QLabel('검색어 입력:', self)
|
||||||
|
|
||||||
|
self.searchButton = QPushButton('검색 실행', self)
|
||||||
|
# 검색 버튼 클릭 시 start_search를 비동기로 실행
|
||||||
|
self.searchButton.clicked.connect(self.on_search_button_clicked)
|
||||||
|
self.searchButton.setEnabled(False) # 초기에 버튼을 비활성화합니다.
|
||||||
|
|
||||||
|
self.searchLineEdit = QLineEdit(self)
|
||||||
|
# 엔터 키를 누를 때 start_search를 비동기로 실행
|
||||||
|
self.searchLineEdit.returnPressed.connect(self.on_search_button_clicked)
|
||||||
|
# self.searchLineEdit.setEnabled(False) # 초기에 입력 필드도 비활성화
|
||||||
|
|
||||||
|
|
||||||
|
searchLayout.addWidget(self.searchLabel)
|
||||||
|
searchLayout.addWidget(self.searchLineEdit)
|
||||||
|
searchLayout.addWidget(self.searchButton)
|
||||||
|
|
||||||
|
logLayout = QVBoxLayout()
|
||||||
|
self.logTextEdit = QTextEdit(self)
|
||||||
|
self.logTextEdit.setReadOnly(True)
|
||||||
|
|
||||||
|
logLayout.addWidget(self.logTextEdit)
|
||||||
|
|
||||||
|
mainLayout = QVBoxLayout()
|
||||||
|
mainLayout.addLayout(searchLayout)
|
||||||
|
mainLayout.addLayout(logLayout)
|
||||||
|
|
||||||
|
self.setLayout(mainLayout)
|
||||||
|
self.setWindowTitle('Web Scraper')
|
||||||
|
self.setGeometry(300, 300, 300, 400)
|
||||||
|
|
||||||
|
def start_search_process(self):
|
||||||
|
# 검색 프로세스 시작 시 QProgressDialog 설정
|
||||||
|
self.progress_dialog = QProgressDialog("검색 진행 중... 잠시만 기다려 주세요.", "취소", 0, 100, self)
|
||||||
|
self.progress_dialog.setWindowTitle("검색 진행 상태")
|
||||||
|
self.progress_dialog.setWindowModality(Qt.WindowModal)
|
||||||
|
self.progress_dialog.setAutoClose(True)
|
||||||
|
self.progress_dialog.setAutoReset(True)
|
||||||
|
self.progress_dialog.canceled.connect(self.cancel_search)
|
||||||
|
self.progress_dialog.show()
|
||||||
|
|
||||||
|
# 검색 시작
|
||||||
|
asyncio.ensure_future(self.perform_search(self.searchLineEdit.text()))
|
||||||
|
|
||||||
|
async def perform_search(self, term):
|
||||||
|
# 검색어를 사용하여 실제 검색 수행을 시뮬레이션합니다.
|
||||||
|
for i in range(101):
|
||||||
|
if self.progress_dialog.wasCanceled():
|
||||||
|
break
|
||||||
|
self.progress_dialog.setValue(i) # 진행 상황 업데이트
|
||||||
|
await asyncio.sleep(0.05) # 검색 시간을 시뮬레이션
|
||||||
|
if not self.progress_dialog.wasCanceled():
|
||||||
|
self.progress_dialog.setValue(100) # 작업 완료
|
||||||
|
|
||||||
|
def cancel_search(self):
|
||||||
|
# 검색 취소 로직
|
||||||
|
print("검색이 사용자에 의해 취소되었습니다.")
|
||||||
|
|
||||||
|
def on_search_button_clicked(self):
|
||||||
|
asyncio.ensure_future(self.start_search())
|
||||||
|
|
||||||
|
async def prepare_search(self):
|
||||||
|
# setup_browser에서 페이지 로딩을 관리하므로 여기서는 로딩 완료를 기다립니다.
|
||||||
|
print(f"prepare_search Start : is_page_loaded : {self.scraper.is_page_loaded}")
|
||||||
|
while not self.scraper.is_page_loaded:
|
||||||
|
print(f"is_page_loaded : {self.scraper.is_page_loaded}")
|
||||||
|
await asyncio.sleep(1) # 로딩 상태를 주기적으로 체크합니다.
|
||||||
|
|
||||||
|
self.searchButton.setEnabled(True)
|
||||||
|
# self.searchLineEdit.setEnabled(True)
|
||||||
|
|
||||||
|
async def start_search(self):
|
||||||
|
term = self.searchLineEdit.text()
|
||||||
|
if not term:
|
||||||
|
self.logTextEdit.append("검색어를 입력해주세요.")
|
||||||
|
QMessageBox.warning(self, "경고", "검색어를 입력해주세요.")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.logTextEdit.append("검색을 시작합니다...")
|
||||||
|
try:
|
||||||
|
results = await self.scraper.search_for_term(term)
|
||||||
|
if results:
|
||||||
|
self.logTextEdit.append("검색 완료. 결과를 처리합니다...")
|
||||||
|
await self.show_results(results)
|
||||||
|
else:
|
||||||
|
self.logTextEdit.append("검색 결과가 없거나 오류가 발생했습니다.")
|
||||||
|
QMessageBox.information(self, "검색 결과 없음", "검색 결과가 없으므로 지재권에 안심하시면 됩니다.", QMessageBox.Ok)
|
||||||
|
except Exception as e:
|
||||||
|
self.logTextEdit.append(f"검색 중 오류 발생: {str(e)}")
|
||||||
|
|
||||||
|
async def show_results(self, results):
|
||||||
|
try:
|
||||||
|
# 이전 결과 위젯이 있으면 닫기
|
||||||
|
if self.results_widget is not None:
|
||||||
|
self.results_widget.close() # 기존 위젯 닫기
|
||||||
|
self.results_widget = None # 참조 제거
|
||||||
|
|
||||||
|
# 새 결과 위젯 생성
|
||||||
|
self.results_widget = QWidget()
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
self.results_widget.setLayout(layout)
|
||||||
|
total_count = results['total_count']
|
||||||
|
if total_count is not int:
|
||||||
|
total_count = int(results['total_count'])
|
||||||
|
set_count = min(total_count, 10)
|
||||||
|
grid_layout = QGridLayout()
|
||||||
|
layout.addLayout(grid_layout)
|
||||||
|
grid_index = 0
|
||||||
|
grid_columns = 5
|
||||||
|
|
||||||
|
for i in range(1, set_count + 1):
|
||||||
|
result_key = f"result_{i}"
|
||||||
|
if result_key in results:
|
||||||
|
result = results[result_key]
|
||||||
|
print(f"result num {i}: {result}")
|
||||||
|
|
||||||
|
# 테두리 설정
|
||||||
|
border_style = ''
|
||||||
|
if result['admin_status'] == '등록':
|
||||||
|
border_style = 'border: 4px solid red;'
|
||||||
|
elif result['admin_status'] == '공고':
|
||||||
|
border_style = 'border: 3px solid black;'
|
||||||
|
|
||||||
|
item_layout = QVBoxLayout()
|
||||||
|
item_widget = QWidget()
|
||||||
|
item_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)
|
||||||
|
print("item_widget set")
|
||||||
|
|
||||||
|
try:
|
||||||
|
image_label = QLabel()
|
||||||
|
image_label.setFixedSize(150, 150)
|
||||||
|
image_data = await self.scraper.fetch_image_data(result['IDimageURL'])
|
||||||
|
print(f"image_data : {image_data}")
|
||||||
|
|
||||||
|
pixmap = QPixmap()
|
||||||
|
if image_data:
|
||||||
|
pixmap.loadFromData(image_data)
|
||||||
|
scaled_pixmap = pixmap.scaled(image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||||
|
image_label.setPixmap(scaled_pixmap)
|
||||||
|
image_label.setAlignment(Qt.AlignCenter)
|
||||||
|
image_label.setScaledContents(True)
|
||||||
|
print(f"image URL : [{result['IDimageURL']}]")
|
||||||
|
print("image set")
|
||||||
|
horizontal_layout = QHBoxLayout()
|
||||||
|
horizontal_layout.addWidget(image_label)
|
||||||
|
horizontal_layout.setAlignment(Qt.AlignCenter)
|
||||||
|
item_layout.addLayout(horizontal_layout)
|
||||||
|
print("horizontal_layout set")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" 이미지 데이터 처리 중 에러 : {e}")
|
||||||
|
|
||||||
|
info_text = f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>상표권명: {result['title']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>등록상태: {result['admin_status']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 9pt;'>카테고리: {result['product_category']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 9pt;'>권리자: {result['applicant']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 9pt;'> {result['publication_date']}</span><br>\n" \
|
||||||
|
f"<span style='font-size: 9pt;'> {result['registration_date']}</span>"
|
||||||
|
|
||||||
|
print(f"info_text : {info_text}")
|
||||||
|
info_label = QLabel(info_text)
|
||||||
|
print(f"result['category_description'] : {result['category_description']}")
|
||||||
|
tooltip_text = self.wrap_text(result['category_description'], 50)
|
||||||
|
info_label.setToolTip(tooltip_text)
|
||||||
|
image_label.setToolTip(tooltip_text)
|
||||||
|
item_layout.addWidget(info_label)
|
||||||
|
|
||||||
|
image_label.setStyleSheet(border_style)
|
||||||
|
info_label.setStyleSheet(border_style)
|
||||||
|
|
||||||
|
print("item_layout set")
|
||||||
|
|
||||||
|
# 레이아웃에 위젯 추가
|
||||||
|
row = grid_index // grid_columns
|
||||||
|
col = grid_index % grid_columns
|
||||||
|
grid_layout.addLayout(item_layout, row, col)
|
||||||
|
# grid_layout.addLayout(item_layout, grid_index // grid_columns, grid_index % grid_columns)
|
||||||
|
grid_index += 1
|
||||||
|
|
||||||
|
close_button = QPushButton("Close")
|
||||||
|
close_button.clicked.connect(self.close_results_widget)
|
||||||
|
layout.addWidget(close_button)
|
||||||
|
print("close_button set")
|
||||||
|
|
||||||
|
self.results_widget.setGeometry(300, 300, 800, 600)
|
||||||
|
self.results_widget.setWindowTitle('Search Results')
|
||||||
|
print("Search Results results_widget set")
|
||||||
|
self.results_widget.show()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error displaying results: {e}")
|
||||||
|
|
||||||
|
def wrap_text(self, text, width=40):
|
||||||
|
"""주어진 너비에 맞게 텍스트를 줄바꿈합니다."""
|
||||||
|
words = text.split()
|
||||||
|
wrapped_text = ''
|
||||||
|
line_length = 0
|
||||||
|
|
||||||
|
for word in words:
|
||||||
|
if line_length + len(word) + 1 > width:
|
||||||
|
wrapped_text += '\n'
|
||||||
|
line_length = 0
|
||||||
|
wrapped_text += word + ' '
|
||||||
|
line_length += len(word) + 1
|
||||||
|
|
||||||
|
return wrapped_text.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def close_results_widget(self):
|
||||||
|
self.results_widget.close()
|
||||||
|
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
asyncio.create_task(self.scraper.close_browser())
|
||||||
|
super().closeEvent(event) # QWidget의 기본 closeEvent 처리
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = qasync.QApplication(sys.argv)
|
||||||
|
ex = MainApp()
|
||||||
|
loop = qasync.QEventLoop(app)
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
ex.show()
|
||||||
|
|
||||||
|
# `setup_browser`가 완료될 때까지 기다립니다.
|
||||||
|
loop.create_task(ex.scraper.setup_browser())
|
||||||
|
loop.create_task(ex.prepare_search())
|
||||||
|
|
||||||
|
# `loop.run_forever()`를 사용하여 이벤트 루프를 시작합니다.
|
||||||
|
loop.run_forever()
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
import sys
|
||||||
|
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit, QMessageBox, QGridLayout, QSizePolicy
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtGui import QPixmap
|
||||||
|
from web_scraper_with_re import WebScraper
|
||||||
|
|
||||||
|
class MainApp(QWidget):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.initUI()
|
||||||
|
self.scraper = WebScraper()
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
self.searchLayout = QHBoxLayout()
|
||||||
|
self.searchLabel = QLabel('검색어 입력:', self)
|
||||||
|
self.searchLineEdit = QLineEdit(self)
|
||||||
|
self.searchButton = QPushButton('검색 실행', self)
|
||||||
|
self.searchButton.clicked.connect(self.start_search)
|
||||||
|
self.searchLineEdit.returnPressed.connect(self.start_search)
|
||||||
|
self.searchButton.setEnabled(False) # 버튼을 초기에 비활성화합니다.
|
||||||
|
|
||||||
|
self.searchLayout.addWidget(self.searchLabel)
|
||||||
|
self.searchLayout.addWidget(self.searchLineEdit)
|
||||||
|
self.searchLayout.addWidget(self.searchButton)
|
||||||
|
|
||||||
|
self.logLayout = QVBoxLayout()
|
||||||
|
self.logTextEdit = QTextEdit(self)
|
||||||
|
self.logTextEdit.setReadOnly(True)
|
||||||
|
self.logLayout.addWidget(self.logTextEdit)
|
||||||
|
|
||||||
|
self.mainLayout = QVBoxLayout()
|
||||||
|
self.mainLayout.addLayout(self.searchLayout)
|
||||||
|
self.mainLayout.addLayout(self.logLayout)
|
||||||
|
|
||||||
|
self.setLayout(self.mainLayout)
|
||||||
|
self.setWindowTitle('Web Scraper')
|
||||||
|
self.setGeometry(300, 300, 300, 400)
|
||||||
|
|
||||||
|
self.prepare_search() # 페이지 로딩과 관련된 초기 설정
|
||||||
|
|
||||||
|
def prepare_search(self):
|
||||||
|
# self.scraper.setup_browser() # 브라우저 설정
|
||||||
|
self.searchButton.setEnabled(True) # 페이지 로드 후 버튼 활성화
|
||||||
|
|
||||||
|
def start_search(self):
|
||||||
|
term = self.searchLineEdit.text()
|
||||||
|
if not term:
|
||||||
|
QMessageBox.warning(self, "경고", "검색어를 입력해주세요.")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.logTextEdit.append("검색을 시작합니다...")
|
||||||
|
results = self.scraper.search_for_term(term)
|
||||||
|
if results:
|
||||||
|
self.logTextEdit.append("검색 완료. 결과를 처리합니다...")
|
||||||
|
self.show_results(results)
|
||||||
|
else:
|
||||||
|
self.logTextEdit.append("검색 결과가 없거나 오류가 발생했습니다.")
|
||||||
|
QMessageBox.information(self, "검색 결과 없음", "검색 결과가 없으므로 지재권에 안심하시면 됩니다.", QMessageBox.Ok)
|
||||||
|
|
||||||
|
def show_results(self, results):
|
||||||
|
# 이전 결과 위젯이 있으면 닫기
|
||||||
|
if self.results_widget is not None:
|
||||||
|
self.results_widget.close()
|
||||||
|
|
||||||
|
self.results_widget = QWidget()
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
self.results_widget.setLayout(layout)
|
||||||
|
total_count = results['total_count']
|
||||||
|
set_count = min(total_count, 10)
|
||||||
|
grid_layout = QGridLayout()
|
||||||
|
layout.addLayout(grid_layout)
|
||||||
|
grid_index = 0
|
||||||
|
grid_columns = 5
|
||||||
|
|
||||||
|
for i in range(1, set_count + 1):
|
||||||
|
result_key = f"result_{i}"
|
||||||
|
if result_key in results:
|
||||||
|
result = results[result_key]
|
||||||
|
item_layout = QVBoxLayout()
|
||||||
|
item_widget = QWidget()
|
||||||
|
item_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)
|
||||||
|
|
||||||
|
image_label = QLabel()
|
||||||
|
image_label.setFixedSize(150, 150)
|
||||||
|
image_data = self.scraper.fetch_image_data(result['IDimageURL'])
|
||||||
|
if image_data:
|
||||||
|
pixmap = QPixmap()
|
||||||
|
pixmap.loadFromData(image_data)
|
||||||
|
scaled_pixmap = pixmap.scaled(image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||||
|
image_label.setPixmap(scaled_pixmap)
|
||||||
|
image_label.setAlignment(Qt.AlignCenter)
|
||||||
|
image_label.setScaledContents(True)
|
||||||
|
item_layout.addWidget(image_label)
|
||||||
|
|
||||||
|
info_text = f"{result['title']} - {result['status']}"
|
||||||
|
info_label = QLabel(info_text)
|
||||||
|
item_layout.addWidget(info_label)
|
||||||
|
|
||||||
|
grid_layout.addLayout(item_layout, grid_index // grid_columns, grid_index % grid_columns)
|
||||||
|
grid_index += 1
|
||||||
|
|
||||||
|
self.results_widget.setGeometry(300, 300, 800, 600)
|
||||||
|
self.results_widget.setWindowTitle('Search Results')
|
||||||
|
self.results_widget.show()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
ex = MainApp()
|
||||||
|
ex.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
import requests
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
class KipoApiClientError(Exception):
|
||||||
|
"""Custom exception class for Kipo API client errors, includes the error code."""
|
||||||
|
def __init__(self, message, code=None):
|
||||||
|
super().__init__(message)
|
||||||
|
self.code = code
|
||||||
|
print(f"Error : {code} - {message}")
|
||||||
|
|
||||||
|
class KipoApiClient:
|
||||||
|
def __init__(self, service_key):
|
||||||
|
self.service_key = service_key
|
||||||
|
self.base_url = 'http://kipo-api.kipi.or.kr/openapi/service/patUtiModInfoSearchSevice/getWordSearch'
|
||||||
|
|
||||||
|
def search_patents(self, word, year, patent=True, utility=True):
|
||||||
|
params = {
|
||||||
|
'serviceKey': self.service_key,
|
||||||
|
'word': word,
|
||||||
|
'year': year,
|
||||||
|
'patent': str(patent).lower(),
|
||||||
|
'utility': str(utility).lower()
|
||||||
|
}
|
||||||
|
response = requests.get(self.base_url, params=params)
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise KipoApiClientError("HTTP 오류: 상태 코드 {}".format(response.status_code))
|
||||||
|
|
||||||
|
return self.parse_response(response.content)
|
||||||
|
|
||||||
|
def parse_response(self, xml_response):
|
||||||
|
root = ET.fromstring(xml_response)
|
||||||
|
if root.find('.//cmmMsgHeader') is not None:
|
||||||
|
return self.handle_error(root)
|
||||||
|
else:
|
||||||
|
return self.extract_data(root)
|
||||||
|
|
||||||
|
def handle_error(self, root):
|
||||||
|
err_msg = root.find('.//errMsg').text
|
||||||
|
return_auth_msg = root.find('.//returnAuthMsg').text
|
||||||
|
return_reason_code = int(root.find('.//returnReasonCode').text)
|
||||||
|
error_message = f"API Error {return_reason_code}: {return_auth_msg} ({err_msg})"
|
||||||
|
|
||||||
|
if return_reason_code == 30:
|
||||||
|
raise KipoApiClientError("Unregistered Service Key Error\n등록되지 않은 서비스키", code=return_reason_code)
|
||||||
|
elif return_reason_code == 20:
|
||||||
|
raise KipoApiClientError("Access Denied Error\n서비스 접근거부", code=return_reason_code)
|
||||||
|
elif return_reason_code == 22:
|
||||||
|
raise KipoApiClientError("Service Request Limit Exceeded Error\n서비스 요청제한횟수 초과에러", code=return_reason_code)
|
||||||
|
elif return_reason_code == 32:
|
||||||
|
raise KipoApiClientError("Unregistered IP Error\n등록되지 않은 IP", code=return_reason_code)
|
||||||
|
elif return_reason_code == 1:
|
||||||
|
raise KipoApiClientError("Application Error\n어플리케이션 에러", code=return_reason_code)
|
||||||
|
elif return_reason_code == 4:
|
||||||
|
raise KipoApiClientError("HTTP Error\nHTTP 에러", code=return_reason_code)
|
||||||
|
elif return_reason_code == 12:
|
||||||
|
raise KipoApiClientError("No OpenAPI Service Error\n해당 오픈 API 서비스가 없거나 폐기됨", code=return_reason_code)
|
||||||
|
elif return_reason_code == 31:
|
||||||
|
raise KipoApiClientError("Deadline Expired Error\n활용기간 만료", code=return_reason_code)
|
||||||
|
elif return_reason_code == 99:
|
||||||
|
raise KipoApiClientError("Unknown Error\n기타에러", code=return_reason_code)
|
||||||
|
else:
|
||||||
|
raise KipoApiClientError("Unexpected Error: " + error_message, code=return_reason_code)
|
||||||
|
|
||||||
|
def extract_data(self, root):
|
||||||
|
items = []
|
||||||
|
for item in root.findall('.//item'):
|
||||||
|
data = {
|
||||||
|
'registerStatus': item.find('registerStatus').text if item.find('registerStatus') is not None else None,
|
||||||
|
'inventionTitle': item.find('inventionTitle').text if item.find('inventionTitle') is not None else None,
|
||||||
|
'ipcNumber': item.find('ipcNumber').text if item.find('ipcNumber') is not None else None,
|
||||||
|
'registerNumber': item.find('registerNumber').text if item.find('registerNumber') is not None else None,
|
||||||
|
'registerDate': item.find('registerDate').text if item.find('registerDate') is not None else None,
|
||||||
|
'applicationNumber': item.find('applicationNumber').text if item.find('applicationNumber') is not None else None,
|
||||||
|
'applicationDate': item.find('applicationDate').text if item.find('applicationDate') is not None else None,
|
||||||
|
'openNumber': item.find('openNumber').text if item.find('openNumber') is not None else None,
|
||||||
|
'openDate': item.find('openDate').text if item.find('openDate') is not None else None,
|
||||||
|
'publicationNumber': item.find('publicationNumber').text if item.find('publicationNumber') is not None else None,
|
||||||
|
'publicationDate': item.find('publicationDate').text if item.find('publicationDate') is not None else None,
|
||||||
|
'abstractContent': item.find('astrtCont').text if item.find('astrtCont') is not None else None,
|
||||||
|
'bigDrawing': item.find('bigDrawing').text if item.find('bigDrawing') is not None else None,
|
||||||
|
'drawing': item.find('drawing').text if item.find('drawing') is not None else None,
|
||||||
|
'applicantName': item.find('applicantName').text if item.find('applicantName') is not None else None
|
||||||
|
}
|
||||||
|
items.append(data)
|
||||||
|
print(f"성공적인 응답결과 : {items}")
|
||||||
|
return items
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import requests
|
||||||
|
from src.kipo_api_client import KipoApiClient
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
service_key = 'X9Tz3JqC%2FJcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6%2Bt3dyLZV3gh2TPXdNhxsRQwaKP673Q%3D%3D'
|
||||||
|
client = KipoApiClient(service_key)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 테스트 실행: 'view'라는 단어로 특허 검색을 시도합니다.
|
||||||
|
result = client.search_patents('view', '2022', True, True)
|
||||||
|
# 결과를 콘솔에 출력합니다.
|
||||||
|
print("Test Success:", result)
|
||||||
|
except Exception as e:
|
||||||
|
print("Test Failed:", e)
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
from kipris_api_from_publicdata import Kipris
|
||||||
|
|
||||||
|
# 테스트를 위한 URL 및 파라미터 설정
|
||||||
|
apikey = 'X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q=='
|
||||||
|
search_keyword = "써지오"
|
||||||
|
url = 'http://kipo-api.kipi.or.kr/openapi/service/trademarkInfoSearchService/getWordSearch'
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'serviceKey': apikey,
|
||||||
|
'searchString': search_keyword,
|
||||||
|
'searchRecentYear': '0',
|
||||||
|
'title': '',
|
||||||
|
'fullText': '',
|
||||||
|
'drawing': '',
|
||||||
|
'bigDrawing': ''
|
||||||
|
}
|
||||||
|
|
||||||
|
# XMLParser 인스턴스 생성 및 실행
|
||||||
|
parser = Kipris(url, params)
|
||||||
|
results = parser.run()
|
||||||
|
|
||||||
|
# 모든 아이템의 결과 출력
|
||||||
|
if results:
|
||||||
|
print("결과가 있는 아이템 목록:")
|
||||||
|
for index, item in enumerate(results, start=1):
|
||||||
|
print(f"\n아이템 {index} 정보:")
|
||||||
|
for key, value in item.items():
|
||||||
|
print(f"{key}: {value}")
|
||||||
|
else:
|
||||||
|
print("결과가 없습니다.")
|
||||||
|
|
||||||
|
|
||||||
|
# ※ 행정 상태 도움말
|
||||||
|
# 거절 : 출원 후 특허 심사과정에서 실체적인 특허 등록요건을 만족하지 못할 경우에 심사관이 취하는 행정처분
|
||||||
|
# 등록 : 심사관이 심사한 결과 등록요건에 적합하여 설정등록을 받을 수 있다는 내용의 행정처분
|
||||||
|
# 소멸 : 특허등록 후 존속기간이 만료되어 권리가 소멸된 상태
|
||||||
|
# 무효 : 출원 또는 등록된 상태에 대하여 특정 사유로 인해 그 권리나 행위가 무효화 된 상태
|
||||||
|
# 취하 : 출원한 특허가 등록되기전 여러 사유로 인하여 출원이 취소된 상태
|
||||||
|
# 포기 : 출원인의 포기서 제출, 등록료 불납 등으로 등록결정이나 권리를 포기한 상태
|
||||||
|
# 공개 : 출원이나 등록사실이 일반 공중에게 공표된 상태로 출원 후 18개월이 지난 건
|
||||||
|
# *조기공개신청시 18개월 미만도 공개가능
|
||||||
|
#
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
from src.kipo_api_client import KipoApiClient, KipoApiClientError
|
||||||
|
|
||||||
|
class TestKipoApiClient(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
apikey = '''X9Tz3JqC%2FJcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6%2Bt3dyLZV3gh2TPXdNhxsRQwaKP673Q%3D%3D'''
|
||||||
|
self.client = KipoApiClient(apikey)
|
||||||
|
|
||||||
|
def test_search_patents_successful(self):
|
||||||
|
# 예상되는 성공적인 응답을 시뮬레이션합니다.
|
||||||
|
xml_response = '''
|
||||||
|
<OpenAPI_ServiceResponse>
|
||||||
|
<item>
|
||||||
|
<registerStatus>1000319440000</registerStatus>
|
||||||
|
<inventionTitle>회전체의센서와센서의엔코더</inventionTitle>
|
||||||
|
<ipcNumber>G01D 5/00</ipcNumber>
|
||||||
|
...
|
||||||
|
</item>
|
||||||
|
</OpenAPI_ServiceResponse>'''.encode('utf-8') # 문자열을 UTF-8로 인코딩
|
||||||
|
|
||||||
|
with patch('requests.get') as mocked_get:
|
||||||
|
mocked_get.return_value.status_code = 200
|
||||||
|
mocked_get.return_value.content = xml_response
|
||||||
|
response = self.client.search_patents('view', '0', True, True)
|
||||||
|
self.assertEqual(len(response), 1)
|
||||||
|
self.assertEqual(response[0]['inventionTitle'], '회전체의센서와센서의엔코더')
|
||||||
|
|
||||||
|
def test_handle_error(self):
|
||||||
|
# 에러 상황을 시뮬레이션하여 에러 처리를 테스트합니다.
|
||||||
|
xml_response = '''
|
||||||
|
<OpenAPI_ServiceResponse>
|
||||||
|
<cmmMsgHeader>
|
||||||
|
<errMsg>서비스 오류</errMsg>
|
||||||
|
<returnAuthMsg>등록되지 않은 서비스키 오류</returnAuthMsg>
|
||||||
|
<returnReasonCode>30</returnReasonCode>
|
||||||
|
</cmmMsgHeader>
|
||||||
|
</OpenAPI_ServiceResponse>'''.encode('utf-8') # 문자열을 UTF-8로 인코딩
|
||||||
|
|
||||||
|
with patch('requests.get') as mocked_get:
|
||||||
|
mocked_get.return_value.status_code = 200
|
||||||
|
mocked_get.return_value.content = xml_response
|
||||||
|
with self.assertRaises(KipoApiClientError) as context:
|
||||||
|
self.client.search_patents('view', '0', True, True)
|
||||||
|
self.assertIn('등록되지 않은 서비스키 오류', str(context.exception))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
from web_scraper import WebScraper
|
||||||
|
|
||||||
|
def test_scraper():
|
||||||
|
scraper = WebScraper()
|
||||||
|
scraper.setup_browser()
|
||||||
|
try:
|
||||||
|
scraper.navigate_to_page("http://kdtj.kipris.or.kr/kdtj/searchLogina.do?method=loginTM#page1")
|
||||||
|
keyword = "스키에이트"
|
||||||
|
results = scraper.search_for_term(keyword)
|
||||||
|
if results:
|
||||||
|
print("Search Results:")
|
||||||
|
for key, value in results.items():
|
||||||
|
print(f"{key}: {value}")
|
||||||
|
else:
|
||||||
|
print("No results found or an error occurred.")
|
||||||
|
finally:
|
||||||
|
scraper.close_browser()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_scraper()
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
# import asyncio
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
import random, requests, json
|
||||||
|
from PIL import Image
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
class WebScraper:
|
||||||
|
def __init__(self):
|
||||||
|
self.results = {}
|
||||||
|
self.playwright = None
|
||||||
|
self.browser = None
|
||||||
|
self.context = None
|
||||||
|
self.page = None
|
||||||
|
filename = 'categories.json'
|
||||||
|
self.category_description = self.load_category_descriptions(filename)
|
||||||
|
self.url = "http://kdtj.kipris.or.kr/kdtj/searchLogina.do?method=loginTM#page1"
|
||||||
|
|
||||||
|
def setup_browser(self):
|
||||||
|
"""브라우저 설정 및 인스턴스 생성"""
|
||||||
|
self.playwright = sync_playwright().start()
|
||||||
|
self.browser = self.playwright.chromium.launch(headless=True) # For testing, set headless to True
|
||||||
|
self.context = self.browser.new_context(user_agent=random.choice([
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.0.0",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 OPR/85.0.0.0"
|
||||||
|
]))
|
||||||
|
self.page = self.context.new_page()
|
||||||
|
self.navigate_to_page(self.url)
|
||||||
|
|
||||||
|
def navigate_to_page(self, url):
|
||||||
|
"""주어진 URL로 이동"""
|
||||||
|
self.page.goto(url)
|
||||||
|
|
||||||
|
# def search_for_term_success(self, term):
|
||||||
|
# """검색어로 검색하고 결과 수집"""
|
||||||
|
# try:
|
||||||
|
# self.page.fill("#queryText", term)
|
||||||
|
# self.page.click(".input_btn")
|
||||||
|
# # JavaScript에 의해 결과가 동적으로 로드되기를 기다립니다.
|
||||||
|
# loaded = self.page.wait_for_function("document.querySelector('form#listForm section.search_section article') != null", timeout=10000)
|
||||||
|
# if not loaded:
|
||||||
|
# print("검색 결과가 시간 내에 로드되지 않았습니다.")
|
||||||
|
# return None
|
||||||
|
|
||||||
|
# # 결과 로딩이 확인된 후, 실제 요소를 수집
|
||||||
|
# self.page.wait_for_selector("form#listForm section.search_section", state="visible", timeout=10000)
|
||||||
|
# total_count = self.page.text_content("form#listForm section.search_section div p span.total")
|
||||||
|
# if total_count:
|
||||||
|
# self.results['total_count'] = total_count.strip()
|
||||||
|
# articles = self.page.query_selector_all("form#listForm section.search_section article")
|
||||||
|
# for i, article in enumerate(articles):
|
||||||
|
# img_element = article.query_selector("div.thumb a img")
|
||||||
|
# if img_element:
|
||||||
|
# trademark_image = img_element.get_attribute("src")
|
||||||
|
# name_element = article.query_selector("div.search_section_title h1.stitle a b")
|
||||||
|
# if name_element:
|
||||||
|
# trademark_name = name_element.text_content()
|
||||||
|
# self.results[f"result_{i+1}"] = {"image": trademark_image, "name": trademark_name}
|
||||||
|
# else:
|
||||||
|
# print(f"이미지 요소가 없는 기사: {i+1}")
|
||||||
|
# return self.results
|
||||||
|
# except Exception as e:
|
||||||
|
# print(f"오류 발생 : {e}")
|
||||||
|
# return None
|
||||||
|
|
||||||
|
|
||||||
|
def search_for_term(self, term):
|
||||||
|
"""검색어로 검색하고 결과 수집"""
|
||||||
|
try:
|
||||||
|
self.page.fill("#queryText", term)
|
||||||
|
self.page.click(".input_btn")
|
||||||
|
|
||||||
|
# JavaScript에 의해 결과가 동적으로 로드되기를 기다립니다.
|
||||||
|
loaded = self.page.wait_for_function("document.querySelector('form#listForm section.search_section article') != null")
|
||||||
|
if not loaded:
|
||||||
|
print("검색 결과가 시간 내에 로드되지 않았습니다.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 검색 결과가 없는지 확인
|
||||||
|
nodata_info = self.page.query_selector(".nodata_info")
|
||||||
|
if nodata_info:
|
||||||
|
print("검색 결과가 없습니다.")
|
||||||
|
# 특정 메서드 호출 및 함수 종료
|
||||||
|
# self.handle_no_search_results()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# 결과 로딩이 확인된 후, 실제 요소를 수집
|
||||||
|
self.page.wait_for_selector("form#listForm section.search_section", state="visible", timeout=10000)
|
||||||
|
total_count = self.page.text_content("form#listForm section.search_section div p span.total")
|
||||||
|
if total_count:
|
||||||
|
self.results['total_count'] = total_count.strip()
|
||||||
|
articles = self.page.query_selector_all("form#listForm section.search_section article")
|
||||||
|
for i, article in enumerate(articles):
|
||||||
|
id_and_name_element = article.query_selector("div:nth-child(1) span input[type='checkbox']")
|
||||||
|
trademark_name = id_and_name_element.get_attribute("title") if id_and_name_element else "No name found"
|
||||||
|
|
||||||
|
applno = id_and_name_element.get_attribute("value") if id_and_name_element else "No ID found"
|
||||||
|
if applno:
|
||||||
|
trademark_image_url_by_id = f"http://kdtj.kipris.or.kr/kdtj/remoteFile.do?method=bigImageTM&applno={applno}&no={applno}_tm000001.jpg"
|
||||||
|
|
||||||
|
img_element = article.query_selector("div:nth-child(2) div a img")
|
||||||
|
trademark_image = img_element.get_attribute("src") if img_element else "No image found"
|
||||||
|
|
||||||
|
admin_status_element = article.query_selector("div:nth-child(1) h1 a:nth-child(1) span")
|
||||||
|
admin_status = admin_status_element.text_content() if admin_status_element else "No status found"
|
||||||
|
|
||||||
|
product_category_element = article.query_selector("div:nth-child(2) ul li:nth-child(1) a span")
|
||||||
|
product_category = product_category_element.text_content() if product_category_element else "No category found"
|
||||||
|
if product_category:
|
||||||
|
category_desc = self.add_category_description(product_category)
|
||||||
|
|
||||||
|
applicant_element = article.query_selector("div:nth-child(2) ul li:nth-child(2) span[title]")
|
||||||
|
applicant = applicant_element.get_attribute("title") if applicant_element else "No applicant found"
|
||||||
|
|
||||||
|
publication_date_element = article.query_selector("div:nth-child(2) ul li:nth-child(8)")
|
||||||
|
publication_date = publication_date_element.text_content().strip() if publication_date_element else "No publication date found"
|
||||||
|
|
||||||
|
registration_date_element = article.query_selector("div:nth-child(2) ul li:nth-child(6)")
|
||||||
|
registration_date = registration_date_element.text_content().strip() if registration_date_element else "No registration date found"
|
||||||
|
|
||||||
|
# name_element = article.query_selector("div:nth-child(1) h1 a:nth-child(2) font b")
|
||||||
|
# trademark_name = name_element.text_content() if name_element else "No name found"
|
||||||
|
|
||||||
|
# Use the title attribute of the checkbox input for the trademark name
|
||||||
|
name_element = article.query_selector("div:nth-child(1) span input[type='checkbox']")
|
||||||
|
trademark_name = name_element.get_attribute("title") if name_element else "No name found"
|
||||||
|
|
||||||
|
if not admin_status == "소멸":
|
||||||
|
self.results[f"result_{i+1}"] = {
|
||||||
|
"ID": applno,
|
||||||
|
"title": trademark_name,
|
||||||
|
"admin_status": admin_status,
|
||||||
|
"imageURL": trademark_image,
|
||||||
|
"IDimageURL": trademark_image_url_by_id,
|
||||||
|
"product_category": product_category,
|
||||||
|
"category_description": category_desc,
|
||||||
|
"applicant": applicant,
|
||||||
|
"publication_date": publication_date,
|
||||||
|
"registration_date": registration_date,
|
||||||
|
}
|
||||||
|
return self.results
|
||||||
|
else:
|
||||||
|
print("No total count element found, possibly incorrect selector or page structure has changed.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"오류 발생 : {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def download_image(url, applno):
|
||||||
|
"""이미지를 다운로드하고 applno를 파일 이름으로 사용하여 저장합니다."""
|
||||||
|
response = requests.get(url)
|
||||||
|
if response.status_code == 200:
|
||||||
|
filename = f"{applno}.jpeg" # 파일 이름을 ID로 설정
|
||||||
|
with open(filename, 'wb') as file:
|
||||||
|
file.write(response.content)
|
||||||
|
print(f"이미지가 성공적으로 저장되었습니다: {filename}")
|
||||||
|
else:
|
||||||
|
print(f"이미지 다운로드 실패: HTTP {response.status_code}")
|
||||||
|
|
||||||
|
def load_category_descriptions(self, filename):
|
||||||
|
"""JSON 파일에서 카테고리 설명을 로드합니다."""
|
||||||
|
with open(filename, 'r', encoding='utf-8') as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
def add_category_description(self, category_code):
|
||||||
|
"""주어진 카테고리 코드에 따라 설명을 반환합니다."""
|
||||||
|
return self.category_description.get(category_code, "카테고리 설명을 찾을 수 없습니다.")
|
||||||
|
|
||||||
|
def fetch_image_data(self, url):
|
||||||
|
"""주어진 URL로부터 이미지 데이터를 가져와 반환합니다."""
|
||||||
|
response = requests.get(url)
|
||||||
|
if response.status_code == 200:
|
||||||
|
# 서버 응답 헤더에서 Content-Type 확인
|
||||||
|
content_type = response.headers.get('Content-Type', '')
|
||||||
|
if 'image' in content_type:
|
||||||
|
return response.content
|
||||||
|
else:
|
||||||
|
# Content-Type이 이미지가 아니면, 데이터를 이미지로 변환
|
||||||
|
try:
|
||||||
|
image = Image.open(BytesIO(response.content))
|
||||||
|
with BytesIO() as buffer:
|
||||||
|
image.save(buffer, 'JPEG') # 예시로 JPEG 포맷을 사용
|
||||||
|
return buffer.getvalue()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"이미지 변환 실패: {e}")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
print(f"이미지 다운로드 실패: HTTP {response.status_code}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def close_browser(self):
|
||||||
|
"""브라우저 리소스 정리"""
|
||||||
|
if self.context:
|
||||||
|
self.context.close()
|
||||||
|
self.browser.close()
|
||||||
|
self.playwright.stop()
|
||||||
|
|
||||||
|
|
@ -0,0 +1,228 @@
|
||||||
|
import asyncio, aiofiles, aiohttp
|
||||||
|
from playwright.async_api import async_playwright
|
||||||
|
import random, requests, json
|
||||||
|
from PIL import Image
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
class WebScraper:
|
||||||
|
def __init__(self):
|
||||||
|
self.results = {}
|
||||||
|
self.playwright = None
|
||||||
|
self.browser = None
|
||||||
|
self.context = None
|
||||||
|
self.page = None
|
||||||
|
filename = 'categories.json'
|
||||||
|
self.category_description = self.load_category_descriptions(filename)
|
||||||
|
self.url = "http://kdtj.kipris.or.kr/kdtj/searchLogina.do?method=loginTM#page1"
|
||||||
|
self.is_page_loaded = False
|
||||||
|
|
||||||
|
async def setup_browser(self):
|
||||||
|
"""브라우저 설정 및 인스턴스 생성"""
|
||||||
|
self.playwright = await async_playwright().start()
|
||||||
|
self.browser = await self.playwright.chromium.launch(headless=False)
|
||||||
|
self.context = await self.browser.new_context(
|
||||||
|
user_agent=random.choice([
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.0.0",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 OPR/85.0.0.0",
|
||||||
|
]))
|
||||||
|
# ]),
|
||||||
|
# cache_enabled=True # 캐시 활성화
|
||||||
|
# )
|
||||||
|
self.page = await self.context.new_page()
|
||||||
|
await self.page.route('**/*.{png,jpg,jpeg,svg,gif,css}', lambda route: route.abort())
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.page.goto(self.url, wait_until='networkidle')
|
||||||
|
self.is_page_loaded = True
|
||||||
|
print("Page loaded successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to load the page: {e}")
|
||||||
|
self.is_page_loaded = False # 로드 실패 처리
|
||||||
|
|
||||||
|
|
||||||
|
async def navigate_to_page(self, url):
|
||||||
|
try:
|
||||||
|
await self.page.goto(url, wait_until='networkidle')
|
||||||
|
self.is_page_loaded = True
|
||||||
|
print("Page loaded successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to load the page: {e}")
|
||||||
|
self.is_page_loaded = False # 로드 실패 처리
|
||||||
|
|
||||||
|
|
||||||
|
async def search_for_term(self, term):
|
||||||
|
"""검색어로 비동기적으로 검색하고 결과를 수집합니다."""
|
||||||
|
try:
|
||||||
|
self.results = {}
|
||||||
|
# await self.page.fill("#queryText", term)
|
||||||
|
await self.page.fill("#keywordTextarea", term)
|
||||||
|
|
||||||
|
print(f"검색어 입력 : {term}")
|
||||||
|
await self.page.click(".input_btn")
|
||||||
|
print(f"검색버튼 클릭")
|
||||||
|
|
||||||
|
# JavaScript에 의해 결과가 동적으로 로드되기를 기다립니다.
|
||||||
|
loaded = await self.page.wait_for_function(
|
||||||
|
"document.querySelector('form#listForm section.search_section article') != null"
|
||||||
|
)
|
||||||
|
if not loaded:
|
||||||
|
print("검색 결과가 시간 내에 로드되지 않았습니다.")
|
||||||
|
return None
|
||||||
|
print(f"결과가 동적으로 로드되기를 기다림 : {loaded}")
|
||||||
|
|
||||||
|
# 검색 결과가 없는지 확인
|
||||||
|
nodata_info = await self.page.query_selector(".nodata_info")
|
||||||
|
if nodata_info:
|
||||||
|
print("검색 결과가 없습니다.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 결과 로딩이 확인된 후, 실제 요소를 수집
|
||||||
|
await self.page.wait_for_selector("form#listForm section.search_section", state="visible", timeout=10000)
|
||||||
|
total_count = await self.page.text_content("form#listForm section.search_section div p span.total")
|
||||||
|
total_count = total_count.strip()
|
||||||
|
total_count = int(total_count.replace(',', ''))
|
||||||
|
print(f"total_count : {total_count}")
|
||||||
|
|
||||||
|
if total_count:
|
||||||
|
self.results['total_count'] = total_count
|
||||||
|
articles = await self.page.query_selector_all("form#listForm section.search_section article")
|
||||||
|
print(f"articles : {len(articles)} 개")
|
||||||
|
for i, article in enumerate(articles):
|
||||||
|
id_and_name_element = await article.query_selector("div:nth-child(1) span input[type='checkbox']")
|
||||||
|
trademark_name = await id_and_name_element.get_attribute("title") if id_and_name_element else "No name found"
|
||||||
|
print(f"trademark_name : {trademark_name}")
|
||||||
|
|
||||||
|
applno = await id_and_name_element.get_attribute("value") if id_and_name_element else "No ID found"
|
||||||
|
trademark_image_url_by_id = (
|
||||||
|
f"http://kdtj.kipris.or.kr/kdtj/remoteFile.do?method=bigImageTM&applno={applno}&no={applno}_tm000001.jpg"
|
||||||
|
if applno else None
|
||||||
|
)
|
||||||
|
print(f"trademark_image_url_by_id : {trademark_image_url_by_id}")
|
||||||
|
|
||||||
|
img_element = await article.query_selector("div:nth-child(2) div a img")
|
||||||
|
trademark_image = await img_element.get_attribute("src") if img_element else "No image found"
|
||||||
|
print(f"trademark_image : {trademark_image}")
|
||||||
|
|
||||||
|
admin_status_element = await article.query_selector("div:nth-child(1) h1 a:nth-child(1) span")
|
||||||
|
admin_status = await admin_status_element.text_content() if admin_status_element else "No status found"
|
||||||
|
print(f"admin_status : {admin_status}")
|
||||||
|
|
||||||
|
product_category_element = await article.query_selector("div:nth-child(2) ul li:nth-child(1) a span")
|
||||||
|
product_category = await product_category_element.text_content() if product_category_element else "No category found"
|
||||||
|
category_desc = self.add_category_description(product_category) if product_category else "No category description"
|
||||||
|
print(f"product_category : {product_category}")
|
||||||
|
print(f"category_desc : {category_desc}")
|
||||||
|
|
||||||
|
applicant_element = await article.query_selector("div:nth-child(2) ul li:nth-child(2) span[title]")
|
||||||
|
applicant = await applicant_element.get_attribute("title") if applicant_element else "No applicant found"
|
||||||
|
print(f"applicant : {applicant}")
|
||||||
|
|
||||||
|
publication_date_element = await article.query_selector("div:nth-child(2) ul li:nth-child(8)")
|
||||||
|
if publication_date_element:
|
||||||
|
publication_date_content = await publication_date_element.text_content()
|
||||||
|
publication_date = publication_date_content.strip() if publication_date_content else "No publication date found"
|
||||||
|
else:
|
||||||
|
publication_date = "No publication date found"
|
||||||
|
print(f"publication_date : {publication_date}")
|
||||||
|
|
||||||
|
registration_date_element = await article.query_selector("div:nth-child(2) ul li:nth-child(6)")
|
||||||
|
if registration_date_element:
|
||||||
|
registration_date_content = await registration_date_element.text_content()
|
||||||
|
registration_date = registration_date_content.strip() if registration_date_content else "No registration date found"
|
||||||
|
else:
|
||||||
|
registration_date = "No registration date found"
|
||||||
|
print(f"registration_date : {registration_date}")
|
||||||
|
|
||||||
|
if not (admin_status == "소멸" or admin_status == "거절"):
|
||||||
|
self.results[f"result_{i+1}"] = {
|
||||||
|
"ID": applno,
|
||||||
|
"title": trademark_name,
|
||||||
|
"admin_status": admin_status,
|
||||||
|
"imageURL": trademark_image,
|
||||||
|
"IDimageURL": trademark_image_url_by_id,
|
||||||
|
"product_category": product_category,
|
||||||
|
"category_description": category_desc,
|
||||||
|
"applicant": applicant,
|
||||||
|
"publication_date": publication_date,
|
||||||
|
"registration_date": registration_date,
|
||||||
|
}
|
||||||
|
# print(f"results : {self.results}")
|
||||||
|
await self.navigate_to_page(self.url)
|
||||||
|
return self.results
|
||||||
|
else:
|
||||||
|
print("No total count element found, possibly incorrect selector or page structure has changed.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"오류 발생 : {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def download_image(self, url, applno):
|
||||||
|
"""이미지를 비동기적으로 다운로드하고 applno를 파일 이름으로 사용하여 저장합니다."""
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
print(f"download_image session Start!!")
|
||||||
|
async with session.get(url) as response:
|
||||||
|
print(f"download_image url : {url}")
|
||||||
|
if response.status == 200:
|
||||||
|
filename = f"{applno}.jpeg"
|
||||||
|
async with aiofiles.open(filename, 'wb') as file:
|
||||||
|
content = await response.read()
|
||||||
|
await file.write(content)
|
||||||
|
print(f"이미지가 성공적으로 저장되었습니다: {filename}")
|
||||||
|
else:
|
||||||
|
print(f"이미지 다운로드 실패: HTTP {response.status}")
|
||||||
|
|
||||||
|
async def fetch_image_data(self, url):
|
||||||
|
"""주어진 URL로부터 이미지 데이터를 비동기적으로 가져와 반환합니다."""
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
# print(f"download_image session Start!!")
|
||||||
|
async with session.get(url) as response:
|
||||||
|
print(f"download_image url : {url}")
|
||||||
|
if response.status == 200:
|
||||||
|
# print(f"response : {response}")
|
||||||
|
content_type = response.headers.get('Content-Type', '') # await 제거
|
||||||
|
print(f"content_type : {content_type}")
|
||||||
|
if 'image' in content_type or 'octet-stream' in content_type:
|
||||||
|
# print(f"image content type or octet-stream : {content_type}")
|
||||||
|
return await response.read()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
# Content-Type이 이미지가 아니면, 데이터를 이미지로 변환
|
||||||
|
data = await response.read()
|
||||||
|
# print(f"Content-Type이 이미지가 아님 : {data}")
|
||||||
|
image = Image.open(BytesIO(data))
|
||||||
|
with BytesIO() as buffer:
|
||||||
|
image.save(buffer, 'JPEG')
|
||||||
|
print(f"image 를 JPEG로 저장")
|
||||||
|
return buffer.getvalue()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"이미지 변환 실패: {e}")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
print(f"이미지 다운로드 실패: HTTP {response.status}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# async def load_category_descriptions(self, filename):
|
||||||
|
# """JSON 파일에서 카테고리 설명을 비동기적으로 로드합니다."""
|
||||||
|
# async with aiofiles.open(filename, 'r', encoding='utf-8') as file:
|
||||||
|
# content = await file.read()
|
||||||
|
# print(f"JSON 파일에서 카테고리 설명을 비동기적으로 로드합니다: {content}")
|
||||||
|
# return json.loads(content)
|
||||||
|
|
||||||
|
def load_category_descriptions(self, filename):
|
||||||
|
"""JSON 파일에서 카테고리 설명을 로드합니다."""
|
||||||
|
with open(filename, 'r', encoding='utf-8') as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
def add_category_description(self, category_code):
|
||||||
|
"""주어진 카테고리 코드에 따라 설명을 반환합니다."""
|
||||||
|
print(f"add_category_description => category_code: {category_code}")
|
||||||
|
return self.category_description.get(category_code, "카테고리 설명을 찾을 수 없습니다.")
|
||||||
|
|
||||||
|
|
||||||
|
async def close_browser(self):
|
||||||
|
if self.context:
|
||||||
|
await self.context.close() # context를 닫습니다.
|
||||||
|
await self.browser.close() # browser를 닫습니다.
|
||||||
|
await self.playwright.stop() # playwright 세션을 종료합니다.
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
from requests_html import HTMLSession
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
class WebScraper:
|
||||||
|
def __init__(self):
|
||||||
|
self.session = HTMLSession()
|
||||||
|
self.url = "http://kdtj.kipris.or.kr/kdtj/searchLogina.do?method=loginTM#page1"
|
||||||
|
self.results = {}
|
||||||
|
self.category_description = self.load_category_descriptions('categories.json')
|
||||||
|
|
||||||
|
def add_category_description(self, category_code):
|
||||||
|
"""주어진 카테고리 코드에 따라 설명을 반환합니다."""
|
||||||
|
description = self.category_description.get(category_code, "카테고리 설명을 찾을 수 없습니다.")
|
||||||
|
return description
|
||||||
|
|
||||||
|
def load_category_descriptions(self, filename):
|
||||||
|
"""JSON 파일에서 카테고리 설명을 로드합니다."""
|
||||||
|
with open(filename, 'r', encoding='utf-8') as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
def navigate_to_page(self, url):
|
||||||
|
"""지정된 URL로 이동하고 페이지를 로드합니다."""
|
||||||
|
response = self.session.get(url)
|
||||||
|
response.html.render() # 필요 시 JavaScript 실행
|
||||||
|
print("Page loaded successfully.")
|
||||||
|
return response
|
||||||
|
|
||||||
|
def search_for_term(self, term):
|
||||||
|
"""검색어로 검색하고 결과를 수집합니다."""
|
||||||
|
response = self.navigate_to_page(self.url) # 로그인 페이지 로드
|
||||||
|
# await self.page.fill("#queryText", term)
|
||||||
|
# 검색어 입력과 폼 제출을 시뮬레이션
|
||||||
|
try:
|
||||||
|
form = response.html.find('form', containing='검색', first=True)
|
||||||
|
if form:
|
||||||
|
search_url = form.attrs.get('action')
|
||||||
|
if search_url:
|
||||||
|
data = {
|
||||||
|
'keywordTextarea': term, # form에서 요구하는 필드 이름에 맞추어야 함
|
||||||
|
}
|
||||||
|
search_response = self.session.post(search_url, data=data)
|
||||||
|
search_response.html.render()
|
||||||
|
return search_response
|
||||||
|
else:
|
||||||
|
print("폼의 action URL을 찾을 수 없습니다.")
|
||||||
|
else:
|
||||||
|
print("검색 폼을 찾을 수 없습니다.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"검색 실행 중 오류 발생: {e}")
|
||||||
|
|
||||||
|
# response.html.render(script=script, reload=False)
|
||||||
|
|
||||||
|
# # response.html.find('#keywordTextarea', first=True).fill(term)
|
||||||
|
# self.page.evaluate(f"document.querySelector('#keywordTextarea').value = '{term}';")
|
||||||
|
# print(f"검색어 입력: {term}")
|
||||||
|
# input_field = response.html.find('#keywordTextarea', first=True)
|
||||||
|
# script = f"document.querySelector('#keywordTextarea').value = '{term}';"
|
||||||
|
# response.html.page.evaluate(script) # JavaScript 실행
|
||||||
|
|
||||||
|
# search_button = response.html.find('.input_btn', first=True)
|
||||||
|
# search_button.click()
|
||||||
|
response.html.render(wait=10, timeout=20) # 결과 로드를 위해 대기
|
||||||
|
|
||||||
|
articles = response.html.find('form#listForm section.search_section article')
|
||||||
|
if not articles:
|
||||||
|
print("검색 결과가 없습니다.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Store results in a structured format
|
||||||
|
for i, article in enumerate(articles, 1):
|
||||||
|
title = article.find('.title', first=True).text
|
||||||
|
status = article.find('.status', first=True).text
|
||||||
|
image_url = article.find('img', first=True).attrs.get('src', '')
|
||||||
|
|
||||||
|
self.results[f'result_{i}'] = {
|
||||||
|
'title': title,
|
||||||
|
'status': status,
|
||||||
|
'image_url': image_url
|
||||||
|
}
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
def download_image(self, url, applno):
|
||||||
|
"""이미지를 다운로드하고 applno를 파일 이름으로 사용하여 저장합니다."""
|
||||||
|
response = requests.get(url) # 이미지 URL에 대한 요청
|
||||||
|
if response.status_code == 200:
|
||||||
|
filename = f"{applno}.jpeg"
|
||||||
|
with open(filename, 'wb') as file:
|
||||||
|
file.write(response.content)
|
||||||
|
print(f"이미지가 성공적으로 저장되었습니다: {filename}")
|
||||||
|
else:
|
||||||
|
print(f"이미지 다운로드 실패: HTTP {response.status_code}")
|
||||||
|
|
||||||
|
def fetch_image_data(self, url):
|
||||||
|
"""주어진 URL로부터 이미지 데이터를 직접 가져와 반환합니다."""
|
||||||
|
response = requests.get(url)
|
||||||
|
if response.status_code == 200:
|
||||||
|
return response.content
|
||||||
|
else:
|
||||||
|
print(f"이미지 다운로드 실패: HTTP {response.status_code}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def close_browser(self):
|
||||||
|
"""세션을 종료합니다."""
|
||||||
|
self.session.close()
|
||||||
|
|
||||||
|
# # 사용 예시
|
||||||
|
# scraper = WebScraper()
|
||||||
|
# results = scraper.search_for_term("특허")
|
||||||
|
# print(results)
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# XML 응답 데이터 (예시)
|
||||||
|
# xml_response = b'\xec\xa3\xbc\xec\x8b\x9d\xed\x9a\x8c\xec\x82\xac</applicantName><applicationDate>20230102</applicationDate><applicationNumber>4020230000430</applicationNumber><applicationStatus>\xea\xb3\xb5\xea\xb3\xa0</applicationStatus><bigDrawing>http://plus.kipris.or.kr/kiprisplusws/fileToss.jsp?arg=ad7a17eeeef6e4ea4b5e22ef00dd3e2991ba5d9d09407d4bf69fd0e282d93ba4822ea711d1c0014105513d5758c737d00c9e62115cad0aef</bigDrawing><classificationCode>03</classificationCode><drawing>http://plus.kipris.or.kr/kiprisplusws/fileToss.jsp?arg=ed43a0609e94d6e251697a9d72a913440be47db3b03c4b733d99b169217b523f0853e0d6dd613d455ccd291c28fd8817ed949b84dd8bb0c3</drawing><fullText>Y</fullText><indexNo>4</indexNo><internationalRegisterDate></internationalRegisterDate><internationalRegisterNumber></internationalRegisterNumber><priorityDate></priorityDate><priorityNumber></priorityNumber><publicationDate>20240403</publicationDate><publicationNumber>4020240052090</publicationNumber><regPrivilegeName></regPrivilegeName><regReferenceNumber></regReferenceNumber><registrationDate></registrationDate><registrationNumber></registrationNumber><registrationPublicDate></registrationPublicDate><registrationPublicNumber></registrationPublicNumber><title>\xec\x8a\xa4\xed\x82\xa4\xec\x97\x90\xec\x9d\xb4\xed\x8a\xb8</title><viennaCode></viennaCode></item></items></body><count><numOfRows>10</numOfRows><pageNo>1</pageNo><totalCount>4</totalCount></count></response>'
|
||||||
|
apikey = 'X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q=='
|
||||||
|
url = 'http://kipo-api.kipi.or.kr/openapi/service/trademarkInfoSearchService/getWordSearch'
|
||||||
|
params = {
|
||||||
|
'serviceKey': apikey, # 디코딩된 키 사용
|
||||||
|
'searchString': '스키에이트',
|
||||||
|
'searchRecentYear': '0',
|
||||||
|
'title': '',
|
||||||
|
'fullText': '',
|
||||||
|
'drawing': '',
|
||||||
|
'bigDrawing': ''
|
||||||
|
}
|
||||||
|
response = requests.get(url, params=params)
|
||||||
|
|
||||||
|
decoded_data = response.content.decode('utf-8')
|
||||||
|
print(f"decoded_data : {decoded_data}")
|
||||||
|
|
||||||
|
root = ET.fromstring(decoded_data)
|
||||||
|
|
||||||
|
# 결과 저장을 위한 딕셔너리 생성
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# 'item' 태그를 순회하면서 필요한 데이터 추출
|
||||||
|
for item in root.findall('.//body/items/item'):
|
||||||
|
result = {
|
||||||
|
"index_no": item.find('indexNo').text if item.find('indexNo') is not None else None,
|
||||||
|
"application_number": item.find('applicationNumber').text if item.find('applicationNumber') is not None else None,
|
||||||
|
"application_date": item.find('applicationDate').text if item.find('applicationDate') is not None else None,
|
||||||
|
"publication_number": item.find('publicationNumber').text if item.find('publicationNumber') is not None else None,
|
||||||
|
"publication_date": item.find('publicationDate').text if item.find('publicationDate') is not None else None,
|
||||||
|
"registration_date": item.find('registrationDate').text if item.find('registrationDate') is not None else None,
|
||||||
|
"registration_number": item.find('registrationNumber').text if item.find('registrationNumber') is not None else None,
|
||||||
|
"applicant_name": item.find('applicantName').text if item.find('applicantName') is not None else None,
|
||||||
|
"agent_name": item.find('agentName').text if item.find('agentName') is not None else None,
|
||||||
|
"title": item.find('title').text if item.find('title') is not None else None,
|
||||||
|
"drawing_url": item.find('drawing').text if item.find('drawing') is not None else None,
|
||||||
|
"big_drawing_url": item.find('bigDrawing').text if item.find('bigDrawing') is not None else None,
|
||||||
|
"full_text": item.find('fullText').text if item.find('fullText') is not None else None,
|
||||||
|
"application_status": item.find('applicationStatus').text if item.find('applicationStatus') is not None else None,
|
||||||
|
"classification_code": item.find('classificationCode').text if item.find('classificationCode') is not None else None
|
||||||
|
}
|
||||||
|
results.append(result)
|
||||||
|
|
||||||
|
# 결과 출력 (첫 번째 아이템 예시)
|
||||||
|
if results:
|
||||||
|
first_item = results[0]
|
||||||
|
print("첫 번째 아이템 정보:")
|
||||||
|
for key, value in first_item.items():
|
||||||
|
print(f"{key}: {value}")
|
||||||
|
else:
|
||||||
|
print("결과가 없습니다.")
|
||||||
Loading…
Reference in New Issue