你好。我正在尝试抓取无限滚动网站。它卡在第 200 个数据中



我用selenium滚动并抓取所有url,并在beautifulsoup中使用这些url。但是在抓取的数据中有太多的重复。我试图离开他们与drop_duplicate,但它在大约200个数据堆栈。我无法检测到这个问题。我添加了我使用的代码。我要获取所有的价格,区域,房间等。


import requests
from lxml import html
from bs4 import BeautifulSoup as bs
import bs4
import pandas as pd

from selenium.webdriver.common.by import By
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from lxml import html
import pandas as pd
import time
driver = webdriver.Chrome(r'C:Program Files (x86)chromedriver_win32chromedriver.exe')
driver.get('https://tap.az/elanlar/dasinmaz-emlak/menziller')
time.sleep(1)
price = []
citi = []
elann = []
bina = []
arrea = []
adres = []
roome = []
baxhise = []
mulkayet = []
descript = []
urll = []
zefer = []
previous_height = driver.execute_script('return document.body.scrollHeight')

while True:

driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')

time.sleep(2)

new_height = driver.execute_script('return document.body.scrollHeight')
if new_height == previous_height:
break 
previous_height = new_height
lnks=driver.find_elements(By.CSS_SELECTOR, '#content > div > div > div.categories-products.js-categories-products > div.js-endless-container.products.endless-products > div.products-i')
for itema in lnks:
urla=itema.find_element(By.TAG_NAME, 'a')  
aae = (urla.get_attribute('href'))
urel = aae.split('/bookmark')[0]
result = requests.get(urel)
soup = bs(result.text, 'html.parser')
casee = soup.find_all("div",{"class":"lot-body l-center"})
for ae in casee:        
c =  ae.find_all('table', class_ = 'properties')
pp = c[0].text
city = pp.split('Şəhər')[-1].split('Elanın')[0].replace('ş' ,'sh').replace('ə' ,'e').replace('ü' ,'u').replace('ö' ,'o').replace('ı' ,'i').replace('ğ' ,'g').replace('ç' ,'ch').replace('Ç', 'ch').replace('Ş', 'sh').replace('Ə' ,'e').replace('Ü' ,'u').replace('Ö' ,'o').replace('İ', 'I')
cxe = c[0].text
elan_tipi = cxe.split('Elanın tipi')[-1].split('Binanın tipi')[0].replace(' verilir','')
elane = elan_tipi.replace(' ', '_').replace('ş' ,'sh').replace('ə' ,'e').replace('ü' ,'u').replace('ö' ,'o').replace('ı' ,'i').replace('ğ' ,'g').replace('ç' ,'ch').replace('Ç', 'ch').replace('Ş', 'sh').replace('Ə' ,'e').replace('Ü' ,'u').replace('Ö' ,'o').replace('İ', 'I')
cx = c[0].text
bina_tipi = cx.split('Binanın tipi')[-1].split('Sahə')[0].replace(' ', '_').replace('ş' ,'sh').replace('ə' ,'e').replace('ü' ,'u').replace('ö' ,'o').replace('ı' ,'i').replace('ğ' ,'g').replace('ç' ,'ch').replace('Ç', 'ch').replace('Ş', 'sh').replace('Ə' ,'e').replace('Ü' ,'u').replace('Ö' ,'o').replace('İ', 'I')
cx = c[0].text
area = cx.split('tikiliSahə,')[-1].split('Otaq')[0].replace('m²', '')                         
cx = c[0].text
room = cx.split('Otaq sayı')[-1].split('Yerləşmə yeri')[0]
cx = c[0].text
addresss = cx.split('Yerləşmə yeri')[-1].replace('ş' ,'sh').replace('ə' ,'e').replace('ü' ,'u').replace('ö' ,'o').replace('ı' ,'i').replace('ğ' ,'g').replace('ç' ,'ch').replace('Ç', 'ch').replace('Ş', 'sh').replace('Ə' ,'e').replace('Ü' ,'u').replace('Ö' ,'o').replace('İ', 'I')
d = ae.find_all('p')
elan_kod = (d[0].text.replace('Elanın nömrəsi:', ''))
d = ae.find_all('p')
baxhis = d[1].text.replace('Baxışların sayı: ', '')
d = ae.find_all('p')
description = (d[3].text.replace('Baxışların sayı: ', '').replace('ş' ,'sh').replace('ə' ,'e').replace('ü' ,'u').replace('ö' ,'o').replace('ı' ,'i').replace('ğ' ,'g').replace('ç' ,'ch').replace('Ç', 'ch').replace('Ş', 'sh').replace('Ə' ,'e').replace('Ü' ,'u').replace('Ö' ,'o').replace('İ', 'I').replace("n", ''))
kim =  ae.find_all('div', class_ = 'author')
kime = kim[0].text
if 'bütün' in kime:
mulkiyet = int(0)
else:
mulkiyet = int(1)
caseee = soup.find_all("div",{"class":"middle"})
for aecex in caseee:        
pricxxe =  aecex.find_all('span', class_ = 'price-val') 
pricef = pricxxe[0].text.replace(' ' , '')
price.append(pricef)
zefer.append(elane)
elann.append(elan_kod)
citi.append(city)
bina.append(bina_tipi)
arrea.append(area)
adres.append(addresss)
roome.append(room)
baxhise.append(baxhis)
mulkayet.append(mulkiyet)
descript.append(description)
ae = pd.DataFrame({'URL': urel,'Unique_id': elann,'Price': price,'Room': roome,'Area': arrea,'Seher': citi,'Elan_tipi': zefer,'Description': descript,'Address': adres,'Category': bina,'Mulkiyyet': mulkayet})
aere = ae.drop_duplicates()
aere.to_csv('dde.csv', index=False, encoding='utf-8' )


重复的一个原因是,每次你获得lnks时,你也会获得你在滚动之前抓取的产品。您可以通过在代码开头的某个地方初始化scrapedUrls = [](在所有循环之外),然后根据它检查urel,以及添加

来跳过重复的刮擦。
if urel in scrapedUrls: continue ## add this line
result = requests.get(urel) ## from your code
scrapedUrls.append(urel) ## add this line

但我不确定它能解决你的问题。


我不知道为什么它发生了,但当我试图刮链接与硒的find_elements,我得到相同的url一遍又一遍;所以我写了一个函数[getUniqLinks],你可以使用它来获得一个唯一的链接列表(prodUrls),通过滚动到一定的次数,然后解析page_sourceBeautifulSoup。下面是prodUrls = getUniqLinks(fullUrl, rootUrl, max_scrolls=250, tmo=1)打印输出中的两行:

WITH SELENIUM found 10957 product links [1 unique] 

PARSED PAGE_SOURCE  --->  found   12583 product links   [12576 unique]

(完整的功能和打印输出在https://pastebin.com/b3gwUAJZ.)

一些注意事项:

  • 如果你增加tmo,你也可以增加max_scrolls,但它开始变得相当缓慢后100卷轴。
  • 我也使用selenium来获取链接,只是为了打印和显示差异,但是您可以删除所有以# remove结尾的行,以摆脱那些不必要的部分。
  • 我使用硒的WebDriverWait而不是time.sleep,因为它在相关元素加载后停止等待-如果它不加载它允许的时间(tmo),它会引发一个错误,所以我发现在try...except块中使用比使用driver.implicitly_wait更方便和可读
  • 我不知道这是否与导致你的程序挂起的任何东西有关[因为我的可能只是因为元素的数量太多],但我的也挂起,如果我尝试使用硒来获得滚动后的所有链接,而不是添加到循环内的块prodLinks

现在,您可以遍历prodUrls并获得您想要的数据,但我认为最好为每个链接构建一个带有单独字典的列表[即,为每一行拥有一个字典而不是为每列拥有一个单独的列表]。

如果您使用这两个函数,那么您只需要准备一个选择器的引用字典,如

refDict = {
'title': 'h1.js-lot-title',
'price_text': 'div.price-container',
'price_amt': 'div.price-container > .price span.price-val',
'price_cur': 'div.price-container > .price span.price-cur',
'.lot-text tr.property': {'k':'td.property-name', 'v':'td.property-value'},
'contact_name': '.author > div.name',
'contact_phone': '.author > a.phone',
'lot_warning': 'div.lot-warning',
'div.lot-info': {'sel': 'p', 'sep': ':'},
'description': '.lot-text p'
}
可以传递给fillDict_fromTag

的,如下面的代码所示:

## FIRST PASTE FUNTION DEFINITIONS FROM https://pastebin.com/hKXYetmj
productDetails = []
puLen = len(prodUrls)
for pi, pUrl in enumerate(prodUrls[:500]):
print('', end=f'rScraping [for {pi+1} of {puLen}] {pUrl}')
pDets = {'prodId': [w for w in pUrl.split('/') if w][-1]}
resp = requests.get(pUrl)
if resp.status_code != 200:
pDets['Error_Message'] = f'{resp.raise_for_status()}'
pDets['sourceUrl'] = pUrl
productDetails.append(pDets) 
continue

pSoup = BeautifulSoup(resp.content, 'html.parser')
pDets = fillDict_fromTag(pSoup, refDict, pDets, rootUrl)
pDets['sourceUrl'] = pUrl
productDetails.append(pDets)
print()
prodDf = pd.DataFrame(productDetails).set_index('prodId')
prodDf.to_csv('ProductDetails.csv')

我在这里上传了'prodLinks.csv'和'ProductDetails.csv',虽然只有前500次刮痧'结果,因为我在大约20分钟后手动中断;我也在这里粘贴前3行(用print(prodDf.loc[prodDf.index[:3]].to_markdown())打印)

|   prodId | title                                                    | price_text   | price_amt   | price_cur   | Şəhər   | Elanın tipi    | Elanın tipi [link]                                              | Binanın tipi   | Binanın tipi [link]                                             |   Sahə, m² |   Otaq sayı | Yerləşmə yeri   | contact_name   | contact_phone   | lot_warning                                                                  |   Elanın nömrəsi |   Baxışların sayı | Yeniləndi      | description                                                                                                                                                                                                                                                                                                                                                                    | sourceUrl                                                |
|---------:|:---------------------------------------------------------|:-------------|:------------|:------------|:--------|:---------------|:----------------------------------------------------------------|:---------------|:----------------------------------------------------------------|-----------:|------------:|:----------------|:---------------|:----------------|:-----------------------------------------------------------------------------|-----------------:|------------------:|:---------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------|
| 35828514 | 2-otaqlı yeni tikili kirayə verilir, 20 Yanvar m., 45 m² | 600 AZN      | 600         | AZN         | Bakı    | Kirayə verilir | https://tap.az/elanlar/dasinmaz-emlak/menziller?p%5B740%5D=3724 | Yeni tikili    | https://tap.az/elanlar/dasinmaz-emlak/menziller?p%5B747%5D=3849 |         45 |           2 | 20 Yanvar m.    | Elşad Bəy      | (055) 568-12-13 | Diqqət! Beh göndərməmişdən öncə sövdələşmənin təhlükəsiz olduğuna əmin olun! |         35828514 |               105 | 22 Noyabr 2022 | 20 Yanvar metrosuna və Inşatcılar metrosuna 8 - 11 dəiqqə arası olan ərazidə, yeni tikili binada 1 otaq 2 otaq təmirli şəraitiynən mənzil kirayə 600 manata, ailiyə və iş adamına verilir. Qabaqçadan 2 ay ödəniş olsa kamendant pulu daxil, ayı 600 manat olaçaq, mənzili götūrən şəxs 1 ayın 20 % vasitəciyə ödəniş etməlidir. Xahìş olunur, rial olmuyan şəxs zəng etməsin. | https://tap.az/elanlar/dasinmaz-emlak/menziller/35828514 |
| 35833080 | 1-otaqlı yeni tikili kirayə verilir, Quba r., 60 m²      | 40 AZN       | 40          | AZN         | Quba    | Kirayə verilir | https://tap.az/elanlar/dasinmaz-emlak/menziller?p%5B740%5D=3724 | Yeni tikili    | https://tap.az/elanlar/dasinmaz-emlak/menziller?p%5B747%5D=3849 |         60 |           1 | Quba r.         | Orxan          | (050) 604-27-60 | Diqqət! Beh göndərməmişdən öncə sövdələşmənin təhlükəsiz olduğuna əmin olun! |         35833080 |               114 | 22 Noyabr 2022 | Quba merkezde her weraiti olan GUNLUK KIRAYE EV.Daimi isti soyuq su hamam metbex wifi.iwciler ve aile ucun elveriwlidir Təmirli                                                                                                                                                                                                                                                | https://tap.az/elanlar/dasinmaz-emlak/menziller/35833080 |
| 35898353 | 4-otaqlı mənzil, Nizami r., 100 m²                       | 153 000 AZN  | 153 000     | AZN         | Bakı    | Satılır        | https://tap.az/elanlar/dasinmaz-emlak/menziller?p%5B740%5D=3722 | Köhnə tikili   | https://tap.az/elanlar/dasinmaz-emlak/menziller?p%5B747%5D=3850 |        100 |           4 | Nizami r.       | Araz M         | (070) 723-54-50 | Diqqət! Beh göndərməmişdən öncə sövdələşmənin təhlükəsiz olduğuna əmin olun! |         35898353 |                71 | 27 Noyabr 2022 | X.Dostluğu metrosuna 2 deq mesafede Leninqrad lahiyeli 9 mərtəbəli binanın 5-ci mərtəbəsində 4 otaqlı yaxsi temirli mənzil satılır.Əmlak ofisinə ödəniş alıcı tərəfindən məbləğin 1%-ni təşkil edir.                                                                                                                                                                           | https://tap.az/elanlar/dasinmaz-emlak/menziller/35898353 |

最新更新