Перенос данных парсинга в Excel выполняется частично
Спарсил все страницы сайта, но почему-то экспортируется в Excel только первая. Как исправить код, чтобы сохранялись все найденные объявления?
import requests
import pandas as pd
from bs4 import BeautifulSoup
from time import sleep
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"}
for i in range(0, 28):
sleep(3)
url = f"https://habarovsk.cian.ru/cat.php?deal_type=sale&engine_version=2&from_developer=1&object_type%5B0%5D=2&offer_type=flat&only_flat=1&p={i}®ion=5039&year%5B0%5D=2025&year%5B1%5D=2026&year%5B2%5D=2027&year%5B3%5D=2028&yeargte=2029"
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "lxml") # html.parser
data = soup.find_all("div", class_="_93444fe79c--container--kZeLu _93444fe79c--link--DqDOy")
results = []
for i in data:
name = i.find("div", class_="_93444fe79c--row--kEHOK").text.replace("\n", "")
address = i.find("a", class_="_93444fe79c--jk--dIktL").text
data = i.find("div", class_="_93444fe79c--labels--L8WyJ").text
cena = i.find("p", class_="_93444fe79c--color_gray60_100--r_axa _93444fe79c--lineHeight_20px--fX7_V _93444fe79c--fontWeight_normal--JEG_c _93444fe79c--fontSize_14px--reQMB _93444fe79c--display_block--KYb25 _93444fe79c--text--e4SBY _93444fe79c--text_letterSpacing__normal--tfToq").text
results.append([name, address, data, cena])
df = pd.DataFrame(results, columns=['name', 'address', 'data', 'cena'])
df.to_excel('results.xlsx', index=False)
Ответы (1 шт):
Полагаю, у вас ошибка в алгоритме. Перепишем код, оставив ключевые моменты:
number_of_pages = ...
for page in range(number_of_pages):
url = f"...&p={page}&..."
response = requests.get(url, ...)
soup = BeautifulSoup(response.text, ...)
data = soup.find_all(...)
results = []
for item in data:
name = item.find(...).text
...
results.append([name, ...])
df = pd.DataFrame(results, ...)
Обратите внимание, что у вас results
обнуляется для каждой страницы, не успев сохраниться. То есть, в таблицу df
попадут данные только последней обработанной страницы (не первой). Попробуйте вынести инициализацию results
за пределы цикла:
results = []
for page in range(number_of_pages):
...
for item in soup.find_all(...):
...
results.append([...])
df = pd.DataFrame(results, ...)
Пара замечаний. Во-первых, в вашем коде переменные i
и data
указывают на разные объекты во внутреннем и внешнем циклах. В пределах текущего кода это не критично. Но если вы продолжите работу во внешнем цикле после выхода из внутреннего, возникнет неопределенность, на что именно они указывают. Речь не столько о легкости чтения кода (что тоже важно), сколько о случае, когда цикл пустой и эти переменные после цикла останутся прежними.
Во вторых, итерироваться по фиксированному количеству страниц - не лучшая идея. Лучше исключить необходимость проверять, сколько их там. И уж тем более - исключить необходимость самому конструировать ссылки. В таких случаях есть смысл опираться на функционал, который предоставлен на странице. В вашем случае - это кнопка Дальше внизу страницы, которая, если активна, расположена внутри ссылки на следующую страницу. Попробуем это использовать:
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"}
base_url = 'https://habarovsk.cian.ru'
base_page = r'/cat.php'
base_request = (r'?deal_type=sale&engine_version=2&from_developer=1&'
r'object_type%5B0%5D=2&offer_type=flat&only_flat=1®ion=5039'
r'&year%5B0%5D=2025&year%5B1%5D=2026&year%5B2%5D=2027&year%5B3%5D=2028&yeargte=2029')
delay = 3
EMPTY = None
results = []
url = base_url + base_page + base_request
while isinstance(url, str) and url.startswith(base_url):
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "lxml")
ads = soup.find_all("div", class_="_93444fe79c--container--kZeLu _93444fe79c--link--DqDOy")
for item in ads:
name = item.find("div", class_="_93444fe79c--row--kEHOK")
address = item.find("a", class_="_93444fe79c--jk--dIktL")
data = item.find("div", class_="_93444fe79c--labels--L8WyJ")
price = item.find("p", class_="_93444fe79c--color_gray60_100--r_axa _93444fe79c--lineHeight_20px--fX7_V _93444fe79c--fontWeight_normal--JEG_c _93444fe79c--fontSize_14px--reQMB _93444fe79c--display_block--KYb25 _93444fe79c--text--e4SBY _93444fe79c--text_letterSpacing__normal--tfToq")
results.append([x.text if x else EMPTY for x in [name, address, data, price]])
next_button = soup.find(name='span', attrs='_93444fe79c--text--V2xLI', string='Дальше')
url = next_button.parent.get('href') if next_button else None
if url and url.startswith(base_page):
url = base_url + url
sleep(delay)
И ещё одно, я бы не стал как-либо обрабатывать данные на этапе их сбора. Речь о строке name = ... .text.replace("\n", "")
. Полагаю, манипуляции с данными лучше выносить в другой блок.