๊ฐ์ฌ ์ฝ๋ ฅ
์ ํ๋ธ ์คํํธ์ฝ๋ฉ ์ฑ๋์ด์ (๊ตฌ๋ ์ 3๋ง๋ช )
์ผ์ฑ์ ์ ๋ฉ์ธ ๊ฐ์ฌ
์์ค์ค์ผ, KT, ํ๊ตญ๋ฅ๋ฅ ํํ ๋ฑ ์ถ๊ฐ
'ํ์ด์ฌ์ ์ ๋๋ก ํ์ฉํด๋ณด๋ ค๊ณ ํด' ์ ์
์ํ์ฝ(Alpaco) K-๋์งํธํธ๋ ์ด๋ ๊ฐ์ฌ
๋์ ๋, ๋๊ตฌํ์๋, ์ด๋ฑํ๊ต,์คํ๊ต ์ฝ๋ฉ ํน๊ฐ
ํ๋ก๊ทธ๋๋จธ์ค ํ์ด์ฌ PCCE ์๊ฒฉ์ฆ ๊ฐ์
์คํํธ์ฝ๋ฉ ์ ํ๋ธ ์ฑ๋
https://www.youtube.com/channel/UCHwhZ7HPBhUh2IscPSL0pHA
์คํํธ์ฝ๋ฉ ๊ต์ก ์ผํฐ
Courses
Reviews
- [New Revised Edition] This is Real Web Crawling - Basic Edition
- [New Revised Edition] This is Real Excel Automation - Basic Edition
- [New Revised Edition] This is Real Web Crawling - Basic Edition
yusijin7722485
ยท
[New Revised Edition] This is Real Web Crawling - Practical Edition (AI Monetization)[New Revised Edition] This is Real Web Crawling - Practical Edition (AI Monetization)- [New Revised Edition] This is Real Web Crawling - Basic Edition
Posts
Q&A
์ํ๋ ๊ฐ์ด ์์ ๋
์ ๋ ๋ณดํต try except ๋ if ๋ฅผ ์ด์ฉํด์ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํด์ pydantic ํ ์ฒ๋ฆฌ๋ฐฉ๋ฒ์ ๋ํด์๋ ์๋ชจ๋ฅด๊ฒ ๋ค์ ๐ค ๋์ ์ ๋ฏธ๋์ด ๋ต๋ณ์ ์ฒจ๋ถํด ๋๋ฆฝ๋๋ค!์๋ ํ์ธ์, ์๊ฐ์๋. ์ง๋ฌธ ๊ฐ์ฌํฉ๋๋ค. ์ฝ๋๊น์ง ๊น๋ํ๊ฒ ์ฒจ๋ถํด์ฃผ์ ์ ์ง๋ฌธ์ ์๋๋ฅผ ์ ํํ ํ์ ํ๋ ๋ฐ ํฐ ๋์์ด ๋์์ต๋๋ค. Pydantic์ ํ์ฉํ ์ ๊ทผ ๋ฐฉ์, ์์ฃผ ํ๋ฅญํฉ๋๋ค.๊ฒฐ๋ก ๋ถํฐ ๋ง์๋๋ฆฌ๋ฉด, ์๊ฐ์๋์ด ์์ฑํ์ ์ฝ๋๋ '์ค๋ฒ ์์ง๋์ด๋ง'์ด ์ ํ ์๋๋๋ค. ์คํ๋ ค ์ค๋ฌด์์ ์ ๊ทน ๊ถ์ฅ๋๋ '๊ฒฌ๊ณ ํ(robust) ์์ง๋์ด๋ง'์ ๊ฐ๊น์ต๋๋ค. ์ ๊ฐ ๊ฐ์์์ (์๋ง๋) ๊ฐ๋จํ ๋์ ๋๋ฆฌ(dict)๋ก ์ฒ๋ฆฌํ ๊ฒ์ ๊ฐ์์ ํต์ฌ ์ฃผ์ (ํฌ๋กค๋ง)์ ์ง์คํ๊ธฐ ์ํจ์ด์์ ๊ฒ๋๋ค.์๊ฐ์๋์ ์ ๊ทผ ๋ฐฉ์(Pydantic)์ด ์ ์ข์์ง, ๊ทธ๋ฆฌ๊ณ '์ ์ฐํ ์ฒ๋ฆฌ'๋ ๋ฌด์์ธ์ง ์ค๋ช ํด ๋๋ฆด๊ฒ์. ๐ง Pydantic์ ์ฐ๋ ๊ฒ์ด ์ ์ข์๊ฐ? ๋ช ํํ ๋ฐ์ดํฐ ๊ตฌ์กฐ (Schema):NewsItem ๋ชจ๋ธ์ ๋ณด๋ ๊ฒ๋ง์ผ๋ก๋ "์, ์ด ํฌ๋กค๋ฌ๋ 5๊ฐ์ ํ๋๋ฅผ ์์งํ๊ณ , ๊ฐ ํ๋๋ ์ด๋ฐ ํ์ (str, HttpUrl)์ด๊ตฌ๋"๋ผ๊ณ ์ฆ์ ์ ์ ์์ต๋๋ค.๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ (SoC):crawl_... ํจ์๋ HTML์์ ๋ฐ์ดํฐ๋ฅผ '์ถ์ถ(Extract)'ํ๋ ์ฑ ์๋ง ์ง๋๋ค.NewsItem ๋ชจ๋ธ์ ์ถ์ถ๋ ๋ฐ์ดํฐ๊ฐ '์ฌ๋ฐ๋ฅธ์ง ๊ฒ์ฆ(Validate)'ํ๋ ์ฑ ์์ ์ง๋๋ค.์ฝ๋๊ฐ ํจ์ฌ ๊นจ๋ํด์ง๊ณ ์ ์ง๋ณด์๊ฐ ์ฌ์์ง๋๋ค.์กฐ๊ธฐ ๋ฐ๊ฒฌ (Fail-fast): ํฌ๋กค๋ง ๋์ค subject๊ฐ ๋น์ด์๊ฑฐ๋ detail_article_url์ด ์๋ชป๋ ํ์์ด๋ผ๋ฉด, Pydantic์ด ์ฆ์ ValidationError๋ฅผ ๋ฐ์์ํต๋๋ค. ๋ฐ์ดํฐ๊ฐ ํ์ฐธ ๋ค์ DB์ ์ ์ฅ๋๊ฑฐ๋ ๋ค๋ฅธ API๋ก ์ ์ก๋ ๋ ์๋ฌ๊ฐ ๋๋ ๊ฒ๋ณด๋ค ํจ์ฌ ์ข์ต๋๋ค. ๐ "์ ์ฐํ ์ฒ๋ฆฌ" vs "๊ฒฌ๊ณ ํ ์ฒ๋ฆฌ" '์ ์ฐํ๊ฒ ์ฒ๋ฆฌํ๋ค'๋ ๊ฒ์ด ์๋ฌ๋ฅผ ๋ฌด์ํ๊ณ ๋์ถฉ ๋์ด๊ฐ๋ ๊ฒ์ ์๋ฏธํ์ง๋ ์์ต๋๋ค.1. "๊ฐ๋จํ์ง๋ง ์ง์ ๋ถํด์ง๊ธฐ ์ฌ์ด" ์ ์ฐํ ์ฒ๋ฆฌ (๊ฐ์์์ ์ธ ๋ฒํ ๋ฐฉ์)Pydantic ์์ด crawl_news_per_page ํจ์ ๋ด์์ ๋ชจ๋ ๊ฒ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ๋๋ค.Pythondef crawl_news_per_page(soup: BeautifulSoup): result = [] news_per_page = soup.select(".block1") for article_card in news_per_page: try: subject = article_card.select_one(".articleSubject > a").text detail_article_url = get_detail_news_url(article_card.select_one(".articleSubject > a").get("href")) content_tag = article_card.select_one(".articleSummary") content = content_tag.contents[0].strip() press = content_tag.select_one(".press").text.strip() article_date = content_tag.select_one(".wdate").text.strip() # ์ฌ๊ธฐ์ ์ง์ ์ ํจ์ฑ ๊ฒ์ฌ (์ ์ ์ง์ ๋ถํด์ง) if not subject or not content: print("๊ฒฝ๊ณ : ๋น ์ ๋ชฉ์ด๋ ๋ด์ฉ์ด ์์ต๋๋ค. ์ด ํญ๋ชฉ์ ๊ฑด๋๋๋๋ค.") continue # ์ด ํญ๋ชฉ์ ์คํต result.append({ "subject": subject, "detail_article_url": detail_article_url, "content": content, "press": press, "article_date": article_date }) except AttributeError as e: # .text๋ .get('href')๋ฅผ ์๋ํ ๋ ํ๊ทธ๊ฐ None์ด๋ฉด ๋ฐ์ํ๋ ์๋ฌ print(f"ํ์ฑ ์๋ฌ ๋ฐ์: {e}. ์ด ํญ๋ชฉ์ ๊ฑด๋๋๋๋ค.") continue # ์ด ํญ๋ชฉ์ ์คํต except Exception as e: # ๊ธฐํ ์์ธ print(f"์ ์ ์๋ ์๋ฌ: {e}") return result ๋ฌธ์ ์ :try...except ๋ธ๋ก์ด ๊ฑฐ๋ํด์ง๊ณ , ํ์ฑ ๋ก์ง๊ณผ ๊ฒ์ฆ ๋ก์ง, ์๋ฌ ์ฒ๋ฆฌ ๋ก์ง์ด ๋ค์์ฌ '์คํ๊ฒํฐ ์ฝ๋'๊ฐ ๋๊ธฐ ์ฝ์ต๋๋ค.2. ์๊ฐ์๋์ด ์ ํํ "๊ฒฌ๊ณ ํ" ์ฒ๋ฆฌ (Pydantic)์ด ๋ฐฉ์์ด ํจ์ฌ ๋ซ์ต๋๋ค. ๋ค๋ง, ์๊ฐ์๋์ ์ฝ๋์์ ๋ ๊ฐ์ง ์น๋ช ์ ์ธ ๋ฌธ์ ๋ฅผ ์์ ํด์ผ ํฉ๋๋ค. ๐ก Pydantic ์ ๊ทผ๋ฒ ์ค์ ๊ฟํ (์ฝ๋ ๊ฐ์ ) ์๊ฐ์๋์ ์ฝ๋๋ 90% ํ๋ฅญํฉ๋๋ค. ํ์ง๋ง valid_url ๊ฒ์ฆ์์ ํ์ฑ ๋ถ๋ถ์ ์ค๋ฌด์์ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ ๋ถ๋ถ์ด ์์ต๋๋ค.1. Validator๋ I/O ์์ ์ ํด์ ์ ๋ฉ๋๋ค.๋ฌธ์ :valid_url ํจ์ ๋ด๋ถ์์ requests.get(url)๋ฅผ ํธ์ถํ๊ณ ๊ณ์ญ๋๋ค.์ด์ : Pydantic ๋ชจ๋ธ์ ์์ฑ(์ด๊ธฐํ)ํ ๋๋ง๋ค ๋งค๋ฒ HTTP ์์ฒญ์ด ๋ฐ์ํฉ๋๋ค. ๋ด์ค 100๊ฐ๋ฅผ ํฌ๋กค๋งํ๋ฉด, ๋ชจ๋ธ์ ๋ง๋ค ๋ 100๋ฒ, ๋์ค์ ์ค์ ๋ฐ์ดํฐ๋ฅผ ์ธ ๋ 100๋ฒ, ์ด 200๋ฒ์ ์์ฒญ์ด ๋๊ฐ ์ ์์ต๋๋ค. Validator๋ ๋ฐ์ดํฐ์ ํ์(format)์ด ์ ํจํ์ง๋ง ๋น ๋ฅด๊ณ ๊ฐ๋ณ๊ฒ ๊ฒ์ฌํด์ผ ํฉ๋๋ค. URL์ด ์ค์ ๋ก ์ฐ๊ฒฐ ๊ฐ๋ฅํ์ง(reachability)๋ ๋์ค์ ๊ทธ URL์ ์ฌ์ฉํ ๋ ๊ฒ์ฌํ๋ ๊ฒ์ด ๋ง์ต๋๋ค.ํด๊ฒฐ: Pydantic์ด ์ ๊ณตํ๋ HttpUrl ํ์ ์ ์ฌ์ฉํ๋ฉด requests ํธ์ถ ์์ด๋ URL ํ์์ ๊ฒ์ฆํด ์ค๋๋ค.2. ํ์ฑ(Parsing) ๋จ๊ณ๋ AttributeError์ ๋ฐฉ์ด์ ์ด์ด์ผ ํฉ๋๋ค.๋ฌธ์ :subject = article_card.select_one(".articleSubject > a").text์ด์ : ๋ง์ฝ .articleSubject > a ์ ๋ ํฐ์ ํด๋นํ๋ ํ๊ทธ๊ฐ ์๋ค๋ฉดselect_one์ None์ ๋ฐํํฉ๋๋ค. None.text๋ฅผ ํธ์ถํ๋ ์๊ฐ AttributeError๊ฐ ๋ฐ์ํ๊ณ , Pydantic ๋ชจ๋ธ์ ๊ตฌ๊ฒฝ๋ ๋ชปํ๊ณ ํ๋ก๊ทธ๋จ์ด ์ฃฝ์ต๋๋ค.ํด๊ฒฐ: ํ์ฑ ๋จ๊ณ์์๋ None์ ์์ ํ๊ฒ ์ฒ๋ฆฌํ๊ณ , None ๋๋ ๋น ๋ฌธ์์ด์ Pydantic ๋ชจ๋ธ์ ์ ๋ฌํด์ผ ํฉ๋๋ค. ๊ทธ ๋ค๋ Pydantic์ AfterValidator(non_empty_str)๊ฐ ์์์ ์ฒ๋ฆฌํด ์ค ๊ฒ์ ๋๋ค. ๐ง ๊ฐ์ ๋ ์ฝ๋ ์์ ์ ๊ฐ ์๊ฐ์๋์ ์ฝ๋๋ฅผ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.Python# ... (requests, BeautifulSoup, datetime ๋ฑ import๋ ๋์ผ) from typing import Annotated, Optional from pydantic import BaseModel, HttpUrl, field_validator, ValidationInfo # --- Pydantic ๋ชจ๋ธ ์ ์ --- def non_empty_str(v: str) -> str: """๊ณต๋ฐฑ ์ ๊ฑฐ ํ ๋น ๋ฌธ์์ด์ธ์ง ๊ฒ์ฌํ๋ Validator""" if not v or not v.strip(): raise ValueError("ํ์ ํ๋๊ฐ ๋น์ด์์ต๋๋ค.") return v.strip() def valid_date_format(v: str) -> str: """๋ ์ง ํ์์ด 'YYYY-MM-DD HH:MM:SS'์ธ์ง ๊ฒ์ฌํ๋ Validator""" v = v.strip() try: datetime.strptime(v, "%Y-%m-%d %H:%M:%S") return v except ValueError: raise ValueError("๋ ์ง ํ์์ด 'YYYY-MM-DD HH:MM:SS'์ ๋ค๋ฆ ๋๋ค.") class NewsItem(BaseModel): # str ํ์ ์ non_empty_str ๊ฒ์ฆ์ ์ ์ฉํฉ๋๋ค. subject: Annotated[str, AfterValidator(non_empty_str)] # HttpUrl ํ์ ์ ์ฌ์ฉํด URL ํ์์ ๊ฒ์ฆํฉ๋๋ค. (I/O ์์!) detail_article_url: HttpUrl content: Annotated[str, AfterValidator(non_empty_str)] press: Annotated[str, AfterValidator(non_empty_str)] article_date: Annotated[str, AfterValidator(valid_date_format)] # --- ํฌ๋กค๋ง ํจ์ ์ ์ --- ROOT = "https://finance.naver.com/" PATH = "news/mainnews.naver" def get_detail_news_url(path): return urljoin(ROOT, path) def safe_get_text(tag, selector): """์์ ํ๊ฒ .text๋ฅผ ์ถ์ถํ๋ ํฌํผ ํจ์""" selected = tag.select_one(selector) return selected.text.strip() if selected else "" # None ๋์ ๋น ๋ฌธ์์ด ๋ฐํ def safe_get_content(tag, selector): """๋ณธ๋ฌธ ๋ด์ฉ(press, wdate ์ ์ธ)์ ์์ ํ๊ฒ ์ถ์ถํ๋ ํฌํผ ํจ์""" selected = tag.select_one(selector) if not selected: return "" # .press, .wdate ํ๊ทธ๋ฅผ ์ ์ธํ ์์ ํ ์คํธ ๋ ธ๋๋ฅผ ์ฐพ์ต๋๋ค. content_text = "" for child in selected.contents: if child.name not in ['span', 'a']: # press/wdate๊ฐ span/a ํ๊ทธ์ผ ๊ฒฝ์ฐ content_text += str(child).strip() return content_text.strip() if content_text else "" def crawl_news_per_page(soup: BeautifulSoup): result = [] news_per_page = soup.select(".block1") for article_card in news_per_page: # 1. ์์ ํ๊ฒ ๋ฐ์ดํฐ ์ถ์ถ (AttributeError ๋ฐฉ์ง) # ํ๊ทธ๊ฐ ์์ผ๋ฉด None์ด๋ ""์ด subject_raw ๋ฑ์ ํ ๋น๋ฉ๋๋ค. subject_raw = safe_get_text(article_card, ".articleSubject > a") url_raw = article_card.select_one(".articleSubject > a") detail_article_url_raw = get_detail_news_url(url_raw.get("href")) if url_raw else "" content_raw = safe_get_content(article_card, ".articleSummary") press_raw = safe_get_text(article_card, ".articleSummary .press") article_date_raw = safe_get_text(article_card, ".articleSummary .wdate") try: # 2. Pydantic ๋ชจ๋ธ๋ก ๊ฒ์ฆ # ์ฌ๊ธฐ์ non_empty_str, HttpUrl, valid_date_format ๋ฑ์ด ์๋ํฉ๋๋ค. news_item = NewsItem( subject=subject_raw, detail_article_url=detail_article_url_raw, content=content_raw, press=press_raw, article_date=article_date_raw ) # ๊ฒ์ฆ ์ฑ๊ณต ์ ๊ฒฐ๊ณผ์ ์ถ๊ฐ result.append(news_item.model_dump()) except Exception as e: # 3. Pydantic ๊ฒ์ฆ ์คํจ ์ ์๋ฌ ์ฒ๋ฆฌ # (์: subject๊ฐ ๋น์ด์๊ฑฐ๋, URL ํ์์ด ์๋ ๋) print(f"--- ๋ฐ์ดํฐ ๊ฒ์ฆ ์คํจ (ํญ๋ชฉ ์คํต) ---") print(f"์๋ฌ: {e}") print(f"์๋ณธ ๋ฐ์ดํฐ: [์ ๋ชฉ: {subject_raw[:20]}...], [URL: {detail_article_url_raw}]") print("---------------------------------") return result # ... (crawl_all_news, get_news_page_url ๋ฑ ๋๋จธ์ง ์ฝ๋๋ ๋์ผ) ์์ฝ ์๊ฐ์๋์ Pydantic ์ ๊ทผ์ '์ค๋ฒ ์์ง๋์ด๋ง'์ด ์๋๋ผ '๋ฒ ์คํธ ํ๋ํฐ์ค'์ ๋๋ค.'์ ์ฐํ ์ฒ๋ฆฌ'๋ try...except๋ก ๋์ถฉ ๋๊ธฐ๋ ๊ฒ์ด ์๋๋ผ, ํ์ฑ(์ถ์ถ)๊ณผ ๊ฒ์ฆ(Validation)์ ์ฑ ์์ ๋ถ๋ฆฌํ๊ณ , Pydantic ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ '๊ฒฌ๊ณ ํ๊ฒ' ๋ณด์ฅํ๋ ๊ฒ์ ๋๋ค.Validator ๋ด๋ถ์์ requests.get ๊ฐ์ I/O ์์ ์ ์ ๋ ๊ธ๋ฌผ์ ๋๋ค. HttpUrl ํ์ ์ ํ์ฉํ์ธ์.ํ์ฑ ๋ก์ง์ AttributeError๊ฐ ๋์ง ์๋๋ก ๋ฐฉ์ด์ ์ผ๋ก ์์ฑํ๊ณ (์: safe_get_text), ๊ฒ์ฆ์ Pydantic์ ๋งก๊ธฐ์ธ์.
- 0
- 2
- 42
Q&A
44๊ฐ ์ ๋ชฉ, ๋งํฌ
์ฌํ๋ค๋ ์ค๋๋ผ ๋ต๋ณ์ด ์กฐ๊ธ ๋ฆ์์ต๋๋ค.๋๊ฐ์ด not ์ ํ์๋ฅผ ์ฌ์ฉํ๋ฉด ๋์ด์, ๊ฐ์์ ์ถ๊ฐ๋ก ์ ๋ฐ์ดํธํ์ง ์์๋๋ฐ์!์๋์ ๊ฐ์ด ์ ํ์๋ฅผ ๋ง๋์๋ฉด ์ ๋์ํ ๊ฒ๋๋ค ใ ใ posts = driver.find_elements(By.CSS_SELECTOR, "tbody > tr:not(.board-notice) .article") for post in posts: title = post.text link = post.get_attribute('href') print(title, link) html ๊ตฌ์กฐ๋ฅผ ์ ํํ ํ์ ํ๊ณ ์ ํ์๋ฅผ ์ด์ฉํ๋ฉด ๋ฉ๋๋ค ์ ํ์ ๋ง๋๋๊ฒ ๊น๋ค๋ก์ธ ๋๋, AI์๊ฒ HTML ๊ตฌ์กฐ (ex. table ํ๊ทธ)๋ฅผ ๋ณต์ฌํ๋ค์ ์ง๋ฌธํด๋ณด์๋ ๊ฒ๋ ์ข์์! (์ฌ์ง)
- 0
- 1
- 37
Q&A
5๋ฒ ๊ฐ์ ์ค์ต๋ฌธ์ ์์ ์ง๋ฌธ์์ต๋๋ค.
๊ทธ๋ ๊ฒ ํ์ ๋ ๋ฉ๋๋ค!๋ค๋ง print("์ด๋ฆ-",name) ์ด๋ ๊ฒ ์ผ์๋ ๋์ด์ฐ๊ธฐ๊ฐ ํ๋ ๋ค์ด๊ฐ๋ค๋ ํน์ง์ด ์์ด์ ใ ใ
- 0
- 1
- 34
Q&A
ํฌ๋กค๋งํ ๋งํฌ๊ฐ ์์ ๋ก ๋ค์ด๊ฐ๋ฉด ์๋์ด ์๋์
์๋ ํ์ธ์!์ ๋ ๋ค์ ๊ฒฐ๊ณผ๋ฌผ์ ํ์ธํ์๋ ํ์ดํผ๋งํฌ๊ฐ ์ ๋์ํ๋ค์! ์ฐ์ ํ์ดํผ๋งํฌ์ ์คํ๊ฐ ์๋์ง ์ํ๋ฒ ํ์ธํด ๋ณด์๊ตฌ์.์คํ๊ฐ ์๋๋๋ก ํ์ดํผ๋งํฌ๊ฐ ํ์ฑํ ๋์ง ์๋๋ค๋ฉด ์๋ ๊ฐ์ด๋๋ฅผ ๋ฐ๋ผ์ ํด๊ฒฐํด ๋ณด๋ ๊ฒ์ ์ถ์ฒ๋๋ฆฝ๋๋ค ๐ from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from bs4 import BeautifulSoup import time import pandas as pd # ํฌ๋กฌ ๋๋ผ์ด๋ฒ ์์ฑ driver = webdriver.Chrome() # ํ์ด์ง ์ด๋ driver.maximize_window() driver.get("https://shoppinghow.kakao.com/siso/p/sale/mall/talkstore") time.sleep(1) for i in range(1, 6): if i == 1: driver.execute_script("window.scrollTo(0, document.body.scrollHeight)") else: driver.execute_script("window.scrollTo(0, document.body.scrollHeight - 1000)") time.sleep(1.5) # ์ํ๋ช , ์์ธํ์ด์ง๋งํฌ, ํ๋งค๊ฐ๊ฒฉ, ํ๋งค์ฒ, ๋ฐฐ์ก์ ๋ณด html = driver.page_source soup = BeautifulSoup(html, 'html.parser') data = [] items = soup.select(".list_spsale.__tiara_container_ > li") for item in items: name = item.select_one(".info_name").text link = 'https://shoppinghow.kakao.com/' + item.select_one(".link_product").attrs['href'] price = int(item.select_one(".info_sales").text.replace(',', '').replace('์', '')) shop = item.select_one(".txt_shop").text shipping = item.select_one(".info_etc").contents[-1].strip() # print(name, link, price, shop, shipping) data.append([name, link, price, shop, shipping]) df = pd.DataFrame(data, columns=['์ํ๋ช ', '์์ธํ์ด์ง๋งํฌ', 'ํ๋งค๊ฐ๊ฒฉ', 'ํ๋งค์ฒ', '๋ฐฐ์ก์ ๋ณด']) df.to_excel('kakao_shopping_crawling.xlsx') driver.quit() ๐ก ๊ฐ์ฅ ํ์คํ ํด๊ฒฐ ๋ฐฉ๋ฒ: ์ฝ๋ ์์ ํ๊ธฐ ๊ฐ์ฅ ์ข์ ํด๊ฒฐ์ฑ ์ ํ์ด์ฌ ์ฝ๋ ์์ฒด๋ฅผ ์์ ํด์, ์์ ํ์ผ์ '์ด๊ฑด ํ์ดํผ๋งํฌ์ ๋๋ค'๋ผ๊ณ ๋ช ํํ๊ฒ ์๋ ค์ฃผ๋ ๊ฒ์ ๋๋ค. ์์ ์ HYPERLINK ํจ์๋ฅผ ์ฌ์ฉํ๋๋ก ์ฝ๋๋ฅผ ์ด์ง๋ง ๋ฐ๊ฟ์ฃผ๋ฉด ๋ฉ๋๋ค.์๋์ ๊ฐ์ด to_excel ์คํ ์ , ๋ฐ์ดํฐํ๋ ์์ ๋งํฌ ๋ถ๋ถ์ ์์ ํ๋ ์ฝ๋ ํ ์ค์ ์ถ๊ฐํด ๋ณด์ธ์. # ... (๋ฐ์ดํฐ ์์ง ์ฝ๋ ์๋ต) ... df = pd.DataFrame(data, columns=['์ํ๋ช ', '์์ธํ์ด์ง๋งํฌ', 'ํ๋งค๊ฐ๊ฒฉ', 'ํ๋งค์ฒ', '๋ฐฐ์ก์ ๋ณด']) # ์๋ ํ ์ค์ ์ถ๊ฐํด์ฃผ์ธ์! # '์์ธํ์ด์ง๋งํฌ' ์ด์ ๋ชจ๋ URL์ ์์ ์ HYPERLINK ํจ์ ํํ๋ก ๋ฐ๊ฟ์ค๋๋ค. df['์์ธํ์ด์ง๋งํฌ'] = df['์์ธํ์ด์ง๋งํฌ'].apply(lambda url: f'=HYPERLINK("{url}")') df.to_excel('kakao_shopping_crawling_hyperlink.xlsx', index=False)โ ์ถ๊ฐ์ ์ผ๋ก ํ์ธํด๋ณผ ์ฌํญ (์์ ์ค์ ) ๋ง์ฝ ์ ๋ฐฉ๋ฒ์ผ๋ก๋ ํด๊ฒฐ์ด ์ ๋๊ฑฐ๋, ์ฝ๋ ์์ ์ด ์ด๋ ค์ด ์ํฉ์ด๋ผ๋ฉด ์์ ์ค์ ์ ํ์ธํด๋ณผ ์ ์์ต๋๋ค.์์ ์ต์ ํ์ธํ์ผ > ์ต์ > ์ธ์ด ๊ต์ > ์๋ ๊ณ ์นจ ์ต์ ์ผ๋ก ๋ค์ด๊ฐ๋๋ค.์ ๋ ฅํ ๋ ์๋ ์์ ํญ์์ '์ธํฐ๋ท๊ณผ ๋คํธ์ํฌ ๊ฒฝ๋ก๋ฅผ ํ์ดํผ๋งํฌ๋ก ์ค์ ' ํญ๋ชฉ์ด ์ฒดํฌ๋์ด ์๋์ง ํ์ธํด์ฃผ์ธ์. (์๋ง ์ด๋ฏธ ์ฒดํฌ๋์ด ์์ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค.)๋ณด์ ์ผํฐ ์ค์ ํ์ธํ์ผ > ์ต์ > ๋ณด์ ์ผํฐ > ๋ณด์ ์ผํฐ ์ค์ ์ผ๋ก ๋ค์ด๊ฐ๋๋ค.์ ๋ขฐํ ์ ์๋ ์์น๋ ์ธ๋ถ ์ฝํ ์ธ ์ค์ ์ด ๋๋ฌด ์๊ฒฉํ๊ฒ ๋์ด์์ด ๋งํฌ ์คํ์ ๋ง๊ณ ์์ ์ ์์ต๋๋ค. ํ์ง๋ง ์ด ๋ถ๋ถ์ ๋ณด์๊ณผ ๊ด๋ จ๋ ์ค์ ์ด๋ฏ๋ก ์ ์คํ๊ฒ ๋ณ๊ฒฝํ์๋ ๊ฒ์ด ์ข์ต๋๋ค.
- 0
- 2
- 54
Q&A
์ ์๋ ์ ์์ ์ ์ฒด ์คํ์ ์ด๋ป๊ฒํ๋์?
ctrl + shift + f8 ์ด ์ ์ฒด ์คํ ๋จ์ถํค ์ ๋๋ค ใ ใ
- 0
- 2
- 32
Q&A
์ ๋ ๋์ PDF์๋ฃ๋ ๋ฐ์ ์ ์๋์
AI์ธํด์ด ์ ์๊ธฐ ํด์คฌ๋ค์... ์ด๊ฑด ์ด๋ป๊ฒ ์๊ณ ์๋๊ฑฐ์ง?
- 0
- 2
- 49
Q&A
๊ธ๋ชฉ๋ก ์ถ์ถํ๊ธฐ
๋ค์ด๋ฒ ์นดํ HTML ๊ตฌ์กฐ๊ฐ ์ด์ง ๋ฐ๋์ด์ ๊ธฐ์กด ๊ฐ์๋๋ก๋ ๋์ํ์ง ์์๊ฒ๋๋ค ์นํฌ๋กค๋ง์ ํ ๋๋ ์ด๋ฐ๊ฒฝ์ฐ๊ฐ ์ข ์ข ์์ด์์ฐ์ต์ผ์ ์ผ๋ถ๋ฌ ๊ฐ์ ์ ๋ฐ์ดํธ๋ฅผ ํ์ง ์์ ๊ฒ๋ ์์ด์ ใ ใ ๊ฑฐ์ ๋ค ํ์ จ๋๋ฐ ๋๋ฌดํ๊ทธ ๋ง๋์ค๋ tbody ์๋ tr ๊น์ง ์ ํํด์ผํด์~!.article-table > tbody:nth-of-type(2) > tr
- 0
- 2
- 54
Q&A
๋ฉ์ผ ์๋ํ ๋ก๊ทธ์ธ ์ค๋ณต๋ฐฉ์ง๋ฌธ์ํด๊ฒฐ ์ค๋ฅ ๋ฐ ๋ช ์์ ๋๊ธฐ ์ง๋ฌธ
๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ ๋๊ณ ์์ด์~pw.send_keys(user_pw)๋ณด์๋ฉด ํจ์ค์๋๋ฅผ 2๋ฒ ์ ๋ ฅํ๋๋ก ์ฝ๋๊ฐ ๊ตฌ์ฑ๋์ด ์๋ค์ ๊ทธ๋ฆฌ๊ณ ๋ฉ์ผ์๋ํ ํฉ์น ๋,๋ช ์์ ๋๊ธฐ ๊ฑธ์ด์ค ํ์ ์์ด time.sleep ๋ง ์ ์ ํ๊ฒ 1, 2์ด ์ฉ ์ค๋ ์ ๋์ํฉ๋๋ค.๊ฐ์ฌํฉ๋๋ค. ํ์ํ ํ๋ฃจ ๋์ธ์ :)
- 0
- 2
- 37
Q&A
๊ฐ์ ๋ ธํธ๊ฐ ์ด๋์ ์๋๊ฑด๊ฐ์?
์์ ํ๋ ์ด์ด์์ ์กฐ๊ธ๋ง ์๋๋ก ์คํฌ๋กค์ ๋ด๋ ค๋ณด๋ฉด ๋์ต๋๋ค!
- 0
- 2
- 38
Q&A
๊ฐ์ ์ปค๋ฆฌํ๋ผ ์ง๋ฌธ
์์ ๊ฐ ๊ฐ์๊ธฐ ๋ณ๊ฒฝ๋์ด์ ์กฐ๊ธ ๋นํฉ์ค๋ฌ์ฐ์ จ์ํ ๋ฐ์.. ใ ํฌ๋กค๋ง ๊ฐ์ ํน์ฑ์ ์์ ์ฌ์ดํธ๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด๊ฐ์๋ฅผ ์์ ํ๊ฑฐ๋ ์์ ๋ฅผ ๋ฐ๊ฟ์ค์ผ ํฉ๋๋ค. ๋ค์ด๋ฒ ์ผํ์ ๊ฒฝ์ฐ๋ ์ด๋ฒ์ ์ ๋ ๋์์ ์์ ํ ๋ง์ ๋ฒ๋ ธ์ด์.์ด๋ฅผ๊ฐ๊ณ ์๋ค๊ฐ ๋ณด์ํ์์ ์์ฒ ๋ด์ํด๋ฒ๋ฆฐ๊ฑฐ ๊ฐ์๋ฐ์ด๋ฐ ๊ฒฝ์ฐ ์ด์ฉ ์ ์์ต๋๋ค.. ใ ใ ์ ๋ ๋์์ ์ฌ์ฉํ ์ ์์ผ๋ ๊ฐ์์์ ์ ์ธํ๋๋ก ๊ฒฐ์ ํ์ด์. (๊ณ ๊ธ ํฌ๋กค๋ง ๊ธฐ์ ์ธ ํจํท์ค๋ํ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์์งํ ์๋ ์์ต๋๋ค๋ง, ์ค์ ํธ๊ฐ์์์๋ ๋ค๋ฃจ์ง ์์ต๋๋ค)
- 0
- 1
- 53





![Thumbnail image of the [New Revised Edition] This is Real Web Crawling - Basic Edition](https://cdn.inflearn.com/public/courses/327774/cover/e25c692a-f39a-41ec-9da7-bdd7922e6553/327774.png?w=148)
![Thumbnail image of the [New Revised Edition] This is Real Excel Automation - Basic Edition](https://cdn.inflearn.com/public/courses/330111/cover/d1d1754d-deea-4b3d-b14b-7e20cf602e5e/330111.png?w=148)
![Thumbnail image of the [New Revised Edition] This is Real Web Crawling - Practical Edition (AI Monetization)](https://cdn.inflearn.com/public/courses/328045/cover/aec69a0c-6a3c-4602-b4a0-62d492398c12/328045.png?w=148)