Desguace del mercado inmobiliario utilizando Python y BeautifulSoup

Necesito algún concepto sobre cómo analizar un mercado de bienes raíces utilizando Python. He buscado información sobre el análisis de los sitios web, incluso hice esto en VBA, pero me gustaría hacerlo en python.

Este es el sitio que se analizará (es solo una oferta ahora, pero estará trabajando en una amplia gama de ofertas inmobiliarias, sitios múltiples de kontrakt.szczecin.pl): http://www.kontrakt.szczecin.pl/ mieszkanie-sprzedaz-100m2-335000pln-grudziadzka-pomorzany-szczecin-zachodniopomorskie, 351149

En primer lugar, el progtwig utilizará 3 piezas de información:

1 / La tabla donde se encuentra la información (Parámetros principales): Numer oferty 351149, Liczba pokoi 3, Cena 335 000 PLN, Cena za m2 3 350 PLN (Número de oferta, Nº de habitación, Precio, Precio por metro cuadrado, etc.). Sin embargo, la cantidad de información depende de la oferta de propiedad: a veces es 14, a veces es 12, a veces 16, etc.

2 / Descripción de la propiedad en párrafos (es otra parte del progtwig, por ahora se puede omitir) : A veces en la tabla (1 /) hay información de que hay garaje o balcón. Pero en el párrafo hay una frase que dice que el garaje es por un precio adicional (lo que significa para mí que la propiedad no tiene garaje) o que el balcón es de tipo francés (que no es un balcón para mí). Logré que el progtwig encontrara la palabra correcta en el párrafo (como garaje) y copie el texto del párrafo con texto adicional en el lado izquierdo y derecho (por ejemplo: 20 letras en ambos lados, pero ¿qué pasa si la palabra está en primer lugar?) ?)

3 / Parámetros adicionales : no todas las ofertas lo tienen pero como esta ( http://www.kontrakt.szczecin.pl/mieszkanie-sprzedaz-6664m2-339600pln-potulicka-nowe-miasto-szczecin-zachodniopomorskie,351165 ) hay información sobre el número de balcones en la propiedad. A veces hay información sobre el sótano también. Debe ser un código similar al 1 / número.

Así que intenté algo como esto, usando algunas fonts de Internet (todavía está incompleta):

from urllib.request import urlopen as uReq from bs4 import BeautifulSoup as soup my_url = "http://www.kontrakt.szczecin.pl/mieszkanie-sprzedaz-6664m2-339600pln-potulicka-nowe-miasto-szczecin-zachodniopomorskie,351165" #PL: otwiera połączenie z wybraną stroną, pobieranie zawartości strony (urllib) #EN: Opens a connection and grabs url uClient = uReq(my_url) page_html = uClient.read() uClient.close() #html parsing (BeautifulSoup) page_soup = soup(page_html, "html.parser") #html.parser -> zapisujemy do html, nie np. do xml #PL: zbiera tabelkę z numerami ofert, kuchnią i innymi danymi o nieruchomości z tabelki #EN: grabs the data about real estate like kitchen, offer no, etc. containers = page_soup.findAll("section",{"class":"clearfix"},{"id":"quick-summary"}) # print(len(containers)) - len(containers) sprawdza ile takich obiektów istnieje na stronie #PL: Co prawda na stronie jest tylko jedna taka tabelka, ale dla dobra nauki zrobię tak jak gdyby tabelek było wiele. #EN: There is only one table, but for the sake of knowledge I do the container variable container = containers[0] find_dt = container.findAll("dt") find_dd = container.findAll("dd") print(find_dt[0].text + " " + find_dd[0]) 

Funciona, pero aún está incompleto. No lo continúo ahora porque hay un defecto importante. A medida que ve la última impresión, toma índices, pero no todas las propiedades tendrán el mismo orden (porque como mencioné a veces hay 10 piezas de información, a veces más, a veces menos). Será un gran lío en CSV.

Mi progtwig de VBA funcionó de esta manera:

  1. Copiar tabla a Excel (Hoja 1)
  2. En la hoja 2 había parámetros que el progtwig estaba buscando (como Precios)
  3. Mecanismo en método abreviado: Copie el parámetro de la hoja 2 (Precio), vaya a la hoja 1 (donde se analiza la información), encuentre la cadena de Precio (pegue la información de la hoja 2: “Precio”), vaya a la línea de abajo, copie el valor del precio, vaya a hoja 2, encuentra el precio, ve abajo, pega el valor del precio. Y así.

Buscando ayuda con el concepto y la encoding también.

EDITAR: PARTE 1 y PARTE 2 está listo. Pero tengo grandes problemas con la PARTE 3. Aquí está el código:

 from urllib import request as uReq import requests #dzięki temu program jest zamykany odrazu, i nie kontynuuje wykonywania reszty kodu. Po imporcie wystarczy exit(0) from sys import exit from urllib.request import urlopen as uReq2 from bs4 import BeautifulSoup as soup import csv import re import itertools filename = 'test.txt' #licznik, potrzebny do obliczenia ilości numerów ofert w pliku .txt num_lines = 0 # tworzymy listę danych i listę URLi. Wyniki będą dodawane do list, dlatego potrzeba jest ich utworzenia (jako puste) list_of_lines = ['351238', '351237', '111111', '351353'] list_of_lines2 = [] list_of_URLs = [] list_of_redictered_URLs = [] KONTRAKT = 'http://www.kontrakt.szczecin.pl' with open(filename, 'r') as file: for line in file: #dodajemy linię (ofertę) do listy list_of_lines.append(line.strip()) #num_lines jest licznikiem, wskazuje ile wierszy zawiera lista, zmienna jest istotna w zakresię tworzenia pętli z adresami URL num_lines += 1 #tworzymy URLe z Numerów Ofert zawartych w filename for i in range(num_lines): nr_oferty = list_of_lines[i] my_url = "http://www.kontrakt.szczecin.pl/lista-ofert/?f_listingId=" + nr_oferty + "&f=&submit=Szukaj" list_of_URLs.append(my_url) print(list_of_URLs) #Cześć druga: konwertowanie listy linków na listę linków przekierowanych #Program wchodzi na stronę, która powinna być przekierowana, jednak ze względu na użyscie Java Scriptu, #zadanie zostało utrudnione. Dlatego, też celem programu jest symulowanie przeglądarki, pobranie #zawartości strony, a następnie 'wyłuskanie' odpowiedniego linku do przekierowania i = 0 for i in range(num_lines): url_redirect = list_of_URLs[i] my_url = url_redirect BROWSER = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'} response = requests.get(my_url, headers=BROWSER) script1 = '' script2 = '' content_URL = str(response.content) find_script1 = (content_URL.find(script1)) find_script2 = (content_URL.find(script2)) url_ready = content_URL[find_script1:find_script2] print(i+1,'z', num_lines, '-', 'oferta nr:', str(my_url[57:57+6])) list_of_redictered_URLs.append(url_ready) #usuwanie zbędnych tagów i znaków, w celu uzyskania czystego przekierowanego linku list_of_redictered_URLs = [w.replace('window.location=\\\'','') for w in list_of_redictered_URLs] list_of_redictered_URLs = [w.replace('\\\';','') for w in list_of_redictered_URLs] #print(list_of_redictered_URLs) #usuwanie pustych wierszy z listy (oferty, które są nieakutalne na liste "wchodzą jako puste" !!! item: jest to zmienna, można zamienić np. na janusz. filtered_list = list(filter(lambda item: item.strip(), list_of_redictered_URLs)) filtered_list = [KONTRAKT + item for item in filtered_list] #zmiana na tuple, ze względu iż mutowalność (dodawanie kolejnych linków) nie będzie potrzebne filtered_list = tuple(filtered_list) #print(str(filtered_list)) print('Lista linków:\n',filtered_list) # Kolejną częścią programu jest pobieranie istotnych informacji (parametrów podstawowych) # ze strony kontrakt.szczecin.pl, a następnie ich zapisanie w pliku csv. # Nagłówki w csv oraz nazwy parametrów na stronie (muszą być identyczne jak na stronie, aby mogły # zostać odpowiednio przyporządkowane w .csv) HEADERS = ['Numer oferty', 'Liczba pokoi', 'Cena', 'Cena za m2', 'Powierzchnia', 'Piętro', 'Liczba pięter', 'Typ kuchni', 'Balkon', 'Czynsz administracyjny', 'Rodzaj ogrzewania', 'Umeblowanie', 'Wyposażona kuchnia', 'Gorąca woda', 'Rodzaj budynku', 'Materiał', 'Rok budowy', 'Stan nieruchomości', 'Rynek', 'Dach:', 'Liczba balkonów:', 'Liczba tarasów:', 'Piwnica:', 'Ogród:', 'Ochrona:', 'Garaż:', 'Winda:', 'Kształt działki:', 'Szerokość działki (mb.):', 'Długość działki (mb.):', 'Droga dojazdowa:', 'Gaz:', 'Prąd:', 'Siła:','piwnica', 'komórk', 'strych', 'gospodarcze', 'postojow', 'parking', 'przynależn', 'garaż', 'ogród', 'ogrod', 'działka', 'ocieplony', 'moderniz', 'restaur', 'odnow', 'ociepl', 'remon', 'elew', 'dozór', 'dozor', 'monitoring', 'monit', 'ochron', 'alarm', 'strzeż', 'portier', 'wspólnot', 'spółdziel', 'kuchni', 'aneks', 'widna', 'ciemna', 'prześwit', 'oficyn', 'linia', 'zabudow', 'opłat', 'bezczynsz', 'poziom', 'wind', 'francuski', 'ul.', 'w cenie', 'dodatkową'] LINKI = ["Link"] #HEADERS2 = ['Liczba balkonów:', # 'Liczba tarasów:', # 'Piwnica:', # 'Ogród:', # 'Ochrona:', # 'Garaż:', # 'Winda:'] HEADERS3 = ['piwnica', 'komórk', 'strych', 'gospodarcze', 'postojow', 'parking', 'przynależn', 'garaż', 'ogród', 'ogrod', 'działka', 'ocieplony', 'moderniz', 'restaur', 'odnow', 'ociepl', 'remon', 'elew', 'dozór', 'dozor', 'monitoring', 'monit', 'ochron', 'alarm', 'strzeż', 'portier', 'wspólnot', 'spółdziel', 'kuchni', 'aneks', 'widna', 'ciemna', 'prześwit', 'oficyn', 'linia', 'zabudow', 'opłat', 'bezczynsz', 'poziom', 'wind', 'francuski', 'ul.', 'w cenie', 'dodatkową',] csv_name = 'data.csv' print('Dane zostaną zapisane do pliku:',csv_name + '.csv') print('\n>>>>Program rozpoczyna pobieranie danych') #Pobieranie linków i = 0 #Tworzy plik csv o nazwie csv #writerow może mieć tylko jeden argument, dlatego jest nim sum poszczególnych list. Lista #linki ma jędną pozycję, ponieważ można sumować dane jednego typu. Nie można sumować listy ze stringami. with open(csv_name + '.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=',', quotechar='"') HEADERS_ALL = HEADERS+HEADERS3+LINKI csvwriter.writerow(HEADERS_ALL) for i in range(len(filtered_list)): my_url = filtered_list[i] with uReq2(my_url) as uClient: page_soup = soup(uClient.read(), 'lxml') print('\t\t-----------',i+1,'-----------\n',my_url) #
- nazwa parametru np. Kuchnia #
- wartość parametru np. widna row = ['-'] * len(HEADERS) + ['-'] * len(HEADERS3) + ['-'] * len(LINKI) # Parametry podstawowe (kontrakt.szczecin.pl) for dt, dd in zip(page_soup.select('section#quick-summary dt'), page_soup.select('section#quick-summary dd')): if dt.text.strip() not in HEADERS: print("\n 1(dt,dd):UWAGA!, kolumna [{}] nie istnieje w nagłówkach! (stała: HEADERS)\n".format(dt.text.strip())) continue row[HEADERS.index(dt.text.strip())] = dd.text.strip() # Parametry dodatkowe for span, li in zip(page_soup.select('section#property-features span'), page_soup.select('section#property-features li')): if span.text.strip() not in HEADERS: print("\n 2:UWAGA(span,li), kolumna [{}] nie istnieje w nagłówkach (stała HEADERS)!\n".format(span.text.strip())) continue row[HEADERS.index(span.text.strip())] = li.text.strip() #csvwriter.writerow(row) print(row) #No to zaczynamy zabawę................................... # zmienna j odnosi się do indeksu HEADERS3, jest to j nie i, ponieważ i jest w dalszym użyciu # w pętli powyżej for p in page_soup.select('section#description'): p = str(p) p = p.lower() for j in range(len(HEADERS3)): #print('j:',j) # find_p znajduje wszystkie słowa kluczowe z HEADERS3 w paragrafie na stronie kontraktu. find_p = re.findall(HEADERS3[j],p) # listy, które wyświetlają pozycję startową poszczególnych słów muszą zaczynać się od '-' lub 0?, # ponieważ, gdy dane słowo nie zostanie odnalezione to listy będą puste w pierwszej iteracji pętli # co w konsekewncji doprowadzi do błędu out of range m_start = [] m_end = [] lista_j = [] for m in re.finditer(HEADERS3[j], p): #print((m.start(),m.end()), m.group()) m_start.append(m.start()) m_end.append(m.end()) #print(h) for k in range(len(m_start)): #właściwe teraz nie wiem po co to jest.. try: x = m_start[k] y = m_end[k] except IndexError: x = m_start[0] y = m_end[0] #print('xy:',x,y) #print(find_p) #print(HEADERS3[j]) z = (HEADERS3[j]+':',p[-60+x:y+60]+' ++-NNN-++') lista_j.append(z) print (lista_j) print(str(lista_j)) row[HEADERS.index(span.text.strip())] = str(lista_j) csvwriter.writerow(row) #print(row)

Este fragmento de código analizará la tabla de resumen rápido de la url de la propiedad y la guarda en el archivo csv:

 from urllib.request import urlopen as uReq from bs4 import BeautifulSoup as soup import csv # my_url = 'http://www.kontrakt.szczecin.pl/mieszkanie-sprzedaz-6664m2-339600pln-potulicka-nowe-miasto-szczecin-zachodniopomorskie,351165' my_url = 'http://www.kontrakt.szczecin.pl/mieszkanie-sprzedaz-100m2-335000pln-grudziadzka-pomorzany-szczecin-zachodniopomorskie,351149' with uReq(my_url) as uClient: page_soup = soup(uClient.read(), 'lxml') with open('data.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=',', quotechar='"') for dt, dd in zip(page_soup.select('section#quick-summary dt'), page_soup.select('section#quick-summary dd')): csvwriter.writerow([dt.text.strip(), dd.text.strip()]) 

El resultado está en data.csv , captura de pantalla de mi LibreOffice:

introduzca la descripción de la imagen aquí

Para tener la tabla transpuesta, puedes usar este código:

 from urllib.request import urlopen as uReq from bs4 import BeautifulSoup as soup import csv # my_url = 'http://www.kontrakt.szczecin.pl/mieszkanie-sprzedaz-6664m2-339600pln-potulicka-nowe-miasto-szczecin-zachodniopomorskie,351165' my_url = 'http://www.kontrakt.szczecin.pl/mieszkanie-sprzedaz-100m2-335000pln-grudziadzka-pomorzany-szczecin-zachodniopomorskie,351149' with uReq(my_url) as uClient: page_soup = soup(uClient.read(), 'lxml') headers = ['Numer oferty', 'Liczba pokoi', 'Cena', 'Cena za m2', 'Powierzchnia', 'Piętro', 'Liczba pięter', 'Typ kuchni', 'Balkon', 'Czynsz administracyjny', 'Rodzaj ogrzewania', 'Gorąca woda', 'Rodzaj budynku', 'Materiał', 'Rok budowy', 'Stan nieruchomości', 'Rynek', 'Dach:', 'Liczba balkonów:', 'Piwnica:', 'Kształt działki:', 'Szerokość działki (mb.):', 'Długość działki (mb.):', 'Droga dojazdowa:', 'Gaz:', 'Prąd:', 'Siła:'] with open('data.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=',', quotechar='"') csvwriter.writerow(headers) row = ['-'] * len(headers) for dt, dd in zip(page_soup.select('section#quick-summary dt'), page_soup.select('section#quick-summary dd')): if dt.text.strip() not in headers: print("Warning, column [{}] doesn't exist in headers!".format(dt.text.strip())) continue row[headers.index(dt.text.strip())] = dd.text.strip() csvwriter.writerow(row) 

El resultado estará en un archivo csv como este (los valores no presentes se sustituirán por ‘-‘):

introduzca la descripción de la imagen aquí