Python에서 HTML 스크레이퍼를 만드는 방법
사이트 데이터를 검색하는 다른 방법은 API 호출입니다. API와의 상호 작용은 데이터베이스 또는 일반 파일에서 직접 데이터를 수신하는 방법으로 사이트 소유자가 공식적으로 제공하는 것입니다. 일반적으로 사이트 소유자의 허가와 특별 토큰이 필요합니다. 그러나 API를 항상 사용할 수 있는 것은 아니기 때문에 스크래핑이 매력적이지만 합법성이 의문을 제기합니다. 스크래핑은 사이트의 저작권 또는 사용 규칙을 위반할 수 있으며, 특히 이익, 경쟁 우위를 확보하거나 피해를 입히는 데 사용되는 경우 (예 : 너무 빈번한 요청으로 인해) 특히 그렇습니다. 그러나 스크래핑은 공개적으로 사용할 수 있으며 개인적인 사용, 학업 목적 또는 무해한 비상업적 용도로 사용됩니다. 데이터가 지불되고, 등록이 필요하고, 스크래핑에 대한 명확한 보호가 있고, 기밀 데이터 또는 사용자의 개인 데이터가 포함 된 경우 스크래핑의 유형을 피해야 합니다..
파이썬에 뷰티풀 수프 설치
뷰티풀 수프는 HTML 코드를 통해 사이트 데이터를 긁어모으기 위한 파이썬 라이브러리입니다.
라이브러리의 최신 버전을 설치합니다.
$ pip install beautifulsoup4
요청을 만들려면 요청(HTTP 요청을 보내기 위한 라이브러리)을 설정합니다.
$ pip install requests
라이브러리를 Python 또는 Jupiter 노트북 파일로 가져옵니다.
from bs4 import BeautifulSoup
import requests
파이썬에서 스크래핑하는 데 필요한 몇 가지 표준 라이브러리 :
import re
from re import sub
from decimal import Decimal
import io
from datetime import datetime
import pandas as pd
공개적으로 사용 가능한 부동산 광고가 포함된 플랫폼을 긁어모으고 싶다고 상상해보십시오. 우리는 특정 도시에서 대중교통의 가용성에 따라 부동산 가격이 어떻게 분배되는지 알아내기 위해 재산의 가격, 주소, 거리, 역 이름 및 가장 가까운 교통수단을 얻고 싶습니다. 사이트의 어떤 요소가 필요한 데이터를 저장하는지 알게 되면 각 광고에서 필요한 모든 정보를 얻을 수 있는 스크래핑 논리를 마련해야 합니다..
단일 속성에 대해 하나의 데이터 포인트(예: 첫 번째 선언의 가격표의 데이터)를 얻으려면 어떻게 해야 합니까?
전체 페이지에서 단일 속성에 대한 모든 데이터 포인트를 얻으려면 어떻게 해야 합니까(예 : 한 페이지의 모든 가격 태그)?
모든 결과 페이지의 단일 속성(예: 모든 결과 페이지의 모든 가격표)에 대한 모든 데이터 포인트를 얻으려면 어떻게 해야 합니까?
데이터가 다른 유형 일 수 있는 경우 불일치를 어떻게 해결합니까 (예 : 가격 필드에 주문형 가격을 지정하는 광고가 있습니다. 결국 숫자 및 문자열 값으로 구성된 열이 생깁니다. 이. 경우 분석을 허용하지 않음)?
복잡한 정보를 추출하는 가장 좋은 방법은 무엇입니까 (예 : 각 광고에 대중교통에 대한 정보가 포함되어 있다고 가정해보십시오(예 : "XY 지하철역까지 0.5 킬로미터")?
단일 데이터 요소를 가져오는 논리
사이트 코드 요청
먼저 브라우저에서 만든 검색 쿼리를 Python 스크립트로 사용합니다.
# поиск в определённой зоне
url = 'https://www.website.com/london/page_size=25&q=london&pn=1'
# делаем запрос и получаем html
html_text = requests.get(url).text
# используем парсер lxml
soup = BeautifulSoup(html_text, 'lxml')
soup 변수에는 검색 결과 페이지의 전체 HTML이 포함되어 있습니다.
속성 태그 찾기
브라우저는 특정 요소에 대한 정보를 직접 얻을 수 있는 편리한 방법을 제공합니다. Chrome에서 사이트의 모든 요소를 선택하고 마우스 오른쪽 버튼을 클릭하여 '요소 탐색'옵션을 선택할 수 있습니다. 오른쪽에서 항목이 강조 표시된 상태에서 사이트 코드가 열립니다.
HTML 클래스 및 ID 속성
HTML 클래스와 ID는 주로 CSS 스타일 시트의 클래스를 참조하는 데 사용되므로 데이터를 일관된 방식으로 표시할 수 있습니다. 한 광고에서 가격 정보를 얻는 데 사용되는 클래스는 다른 광고에서 가격을 얻는 데에도 사용됩니다 (클래스의 주요 목적에 해당).
HTML 클래스는 광고 섹션 외부의 가격 태그(예: 검색 쿼리와 관련이 없지만 결과 페이지에 계속 표시되는 특별 제안)를 참조할 수도 있습니다. 그러 나이 기사의 목적을 위해 우리는 부동산 목록의 가격에만 중점을 둡니다.
그래서 우리는 선언에 먼저 초점을 맞추고 특정 선언에 대한 소스 코드에서만 HTML 클래스를 찾습니다.
# используем парсер lxml
soup = BeautifulSoup(html_text, 'lxml')
# находим одно объявление
ad = soup.find('div', class_ = 'css-ad-wrapper-123456')
# находим цену
price = ad.find('p', class_ = 'css-aaabbbccc').text
find() 메서드 끝에. text를.text 사용하면 브라우저에 표시된 것처럼 일반 텍스트만 반환할 수 있습니다.. text가. .text 없으면 클래스에서 참조하는 HTML 문자열의 전체 소스 코드를 반환합니다.
중요 사항 : 우리는 항상 요소를 지정해야 합니다. 이. 경우 p 입니다.
단일 페이지에서 모든 데이터 요소를 검색하는 논리
모든 광고에 대한 가격표를 얻으려면 find() 대신 find.all() 메서드를 사용합니다.
ads = ad.find_all('p', class_ = 'css-ad-wrapper-123456')
이제 ads 변수에는 결과의 첫 페이지에 있는 각 선언에 대한 HTML 코드가 목록 목록으로 포함됩니다. 이 저장소 형식은 인덱스 별로 특정 선언에 대한 소스 코드에 액세스 할 수 있기 때문에 매우 유용합니다.
모든 가격표를 얻으려면 사전을 사용하여 데이터를 수집합니다.
map = {}
id = 0
# получаем все элементы
ads = ad.find_all('p', class_ = 'css-ad-wrapper-123456')
for i in range(len(ads)):
ad = ads[i]
id += 1
map[id] = {}
# находим цену
price = ad.find('p', class_ = 'css-aaabbbccc').text
# находим адрес
address = ad.find('p', class_ = 'css-address-123456').text
map[id]["address"] = address
map[id]["price"] = price
모든 페이지에서 데이터 요소 가져오기
일반적으로 검색 결과는 페이지로 나뉘거나 끝없이 아래로 스크롤됩니다.
URL의 페이지 번호는 일반적으로 두 번째 페이지에서 볼 수 있습니다. 첫 번째 페이지를 호출하기 위해 선택적 &pn=1 &pn=1스 니펫이있는 기본 URL을 사용하면 여전히 작동합니다 다른 for-loop 위에 하나의 for-loop를 사용하면 결과 페이지를 반복할 수 있습니다.
url = 'https://www.website.com/london/page_size=25&q=london&pn='
map = {}
id = 0
# максимальное количество страниц
max_pages = 15
for p in range(max_pages):
cur_url = url + str(p + 1)
print("Скрапинг страницы №: %d" % (p + 1))
html_text = requests.get(cur_url).text
soup = BeautifulSoup(html_text, 'lxml')
ads = soup.find_all('div', class_ = 'css-ad-wrapper-123456')
for i in range(len(ads)):
ad = ads[i]
id += 1
map[id] = {}
price = ad.find('p', class_ = 'css-aaabbbccc').text
address = ad.find('p', class_ = 'css-address-123456').text
map[id]["address"] = address
map[id]["price"] = price
결과의 마지막 페이지를 결정하는 방법이 궁금할 것입니다. 대부분의 경우 마지막 페이지에 도달한 후 마지막 페이지의 실제 수보다 큰 수의 쿼리는 우리를 첫 페이지로 다시 안내합니다. 따라서 스크립트가 완료될 때까지 기다리는 데 매우 큰 숫자를 사용하면 작동하지 않습니다. 잠시 후 중복 값을 수집하기 시작합니다.
이 문제를 해결하려면 페이지에 다음 링크가 있는 단추가 있는지 확인합니다.
url = 'https://www.website.com/london/page_size=25&q=london&pn='
map = {}
id = 0
# используем очень большое число
max_pages = 9999
for p in range(max_pages):
cur_url = url + str(p + 1)
print("Скрапинг страницы №: %d" % (p + 1))
html_text = requests.get(cur_url).text
soup = BeautifulSoup(html_text, 'lxml')
ads = soup.find_all('div', class_ = 'css-ad-wrapper-123456')
# ищем ссылку в кнопке
page_nav = soup.find_all('a', class_ = 'css-button-123456')
if(len(page_nav) == 0):
print("Максимальный номер страницы: %d" % (p))
break
(...)
끝없는 스크롤이 있는 사이트
이 경우 HTML 스크레이퍼가 작동하지 않습니다. 이 기사의 끝에서 논의할 대체 방법.
데이터 불일치 해결
파이썬에서 스크래핑을 시작할 때 불필요한 데이터를 제거해야 하는 경우 해결 방법 방법을 사용할 수 있습니다.
이상 감지 기능
def is_skipped(price):
'''
Определение цен, которые не являются ценами
(например "Цена по запросу")
'''
for i in range(len(price)):
if(price[i] != '£' and price[i] != ','
and (not price[i].isdigit())):
return True
return False
그리고 데이터를 수집할 때 적용하십시오 :
(...)
for i in range(len(ads)):
ad = ads[i]
id += 1
map[id] = {}
price = ad.find('p', class_ = 'css-aaabbbccc').text
# пропускаем объявление без корректной цены
if(is_skipped(price)): continue
map[id]["price"] = price
즉석에서 데이터 서식 지정
우리는 가격이 통화 기호가 있는 쉼표와 함께 문자열에 저장된다는 것을 눈치챘을 것입니다. 스크래핑 단계에서도 이 문제를 해결할 수 있습니다.
def to_num(price):
value = Decimal(sub(r'[^\d.]', '', price))
return float(value)
우리는이 기능을 사용합니다 :
(...)
for i in range(len(ads)):
ad = ads[i]
id += 1
map[id] = {}
price = ad.find('p', class_ = 'css-aaabbbccc').text
if(is_dropped(price)): continue
map[id]["price"] = to_num(price)
(...)
중첩된 데이터 검색
대중교통에 대한 정보는 중첩 된 구조를 가지고 있습니다. 거리, 역 이름 및 운송 유형에 대한 데이터가 필요합니다.
규칙에 따른 정보 선택
각 데이터는 마일 수, 역 이름 등의 형태로 표시됩니다. 우리는 "마일"이라는 단어를 구분 기호로 사용합니다.
map[id]["distance"] = []
map[id]["station"] = []
transport = ad.find_all('div', class_ = 'css-transport-123')
for i in range(len(transport)):
s = transport[i].text
x = s.split(' miles ')
map[id]["distance"].append(float(x[0]))
map[id]["station"].append(x[1])
처음에는 대중 교통 정보의 두 라인이 있기 때문에 운송 변수는 두 개의 목록 (예 : "슬로안 스퀘어 0.3 마일", "사우스 켄싱턴 0.5 마일")이 있기 때문에 목록에 두 개의 목록을 유지합니다. 우리는 인덱스 값으로 transport len을 사용하여 이러한 목록을 반복하고 각 줄을 거리와 역의 두 변수로 나눕니다.
시각적 정보에 대한 추가 HTML 속성 찾기
페이지 코드에서 대중교통의 유형을 나타내는 testid 속성을 찾을 수 있습니다. 브라우저에는 나타나지 않지만 페이지에 표시되는 이미지를 담당합니다. 이 데이터를 얻으려면 css-StyledIcon 클래스를 사용해야 합니다..
map[id]["distance"] = []
map[id]["station"] = []
map[id]["transport_type"] = []
transport = ad.find_all('div', class_ = 'css-transport-123')
type = ad.find_all('span', class_ = 'css-StyledIcon')
for i in range(len(transport)):
s = transport[i].text
x = s.split(' miles ')
map[id]["distance"].append(float(x[0]))
map[id]["station"].append(x[1])
map[id]["transport_type"].append(type[i]['testid'])
'IT' 카테고리의 다른 글
사이트별 크롤러 종류와 SEO 공부법 (0) | 2022.08.29 |
---|---|
온라인 영화 추천 시스템 살펴보기 (0) | 2022.08.22 |
파이썬을 통한 얼굴인식 프로그래밍 코딩 (0) | 2022.08.19 |
학생인증 서비스 제공 사이트 SheerID (0) | 2022.08.17 |
Slick : 무료로 온라인 문서를 제작하는 사이트 (0) | 2022.08.15 |
댓글